From 81eec66a539836d63fe5be9310483947d042ea7b Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 4 May 2023 18:32:15 +0200 Subject: [PATCH 001/250] GH-554: rough infrastructure for PaymentAdjuster; almost get first round test running --- automap/Cargo.lock | 2 +- dns_utility/Cargo.lock | 2 +- node/src/accountant/mod.rs | 135 ++++++++++++++++++++++- node/src/accountant/payment_adjuster.rs | 24 ++++ node/src/blockchain/blockchain_bridge.rs | 8 +- node/src/neighborhood/mod.rs | 16 ++- node/src/proxy_server/mod.rs | 15 +-- node/src/sub_lib/blockchain_bridge.rs | 4 +- node/src/sub_lib/neighborhood.rs | 23 +++- node/src/test_utils/recorder.rs | 80 ++++++++------ 10 files changed, 248 insertions(+), 61 deletions(-) create mode 100644 node/src/accountant/payment_adjuster.rs diff --git a/automap/Cargo.lock b/automap/Cargo.lock index 8e4e2b331..ca3c01835 100644 --- a/automap/Cargo.lock +++ b/automap/Cargo.lock @@ -1031,7 +1031,7 @@ dependencies = [ [[package]] name = "masq_lib" -version = "0.7.2" +version = "0.7.3" dependencies = [ "actix", "clap", diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock index 8514ee0d6..9f372ce7b 100644 --- a/dns_utility/Cargo.lock +++ b/dns_utility/Cargo.lock @@ -823,7 +823,7 @@ dependencies = [ [[package]] name = "masq_lib" -version = "0.7.2" +version = "0.7.3" dependencies = [ "actix", "clap", diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f388daf0c..4928e43fb 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -4,6 +4,7 @@ pub mod big_int_processing; pub mod dao_utils; pub mod financials; pub mod payable_dao; +pub mod payment_adjuster; pub mod pending_payable_dao; pub mod receivable_dao; pub mod scanners; @@ -29,6 +30,7 @@ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDaoError}; +use crate::accountant::payment_adjuster::PaymentAdjuster; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; use crate::accountant::scanners::{ScanTimings, Scanners}; @@ -73,7 +75,7 @@ use std::ops::{Div, Mul}; use std::path::Path; use std::rc::Rc; use std::time::SystemTime; -use web3::types::{TransactionReceipt, H256}; +use web3::types::{TransactionReceipt, H256, U256}; pub const CRASH_KEY: &str = "ACCOUNTANT"; @@ -89,6 +91,7 @@ pub struct Accountant { crashable: bool, scanners: Scanners, scan_timings: ScanTimings, + payment_adjuster_opt: Option, financial_statistics: Rc>, report_accounts_payable_sub_opt: Option>, request_balances_to_pay_payables_sub_opt: Option>, @@ -219,12 +222,25 @@ impl Handler for Accountant { msg: ConsumingWalletBalancesAndQualifiedPayables, _ctx: &mut Self::Context, ) -> Self::Result { - //TODO GH-672 with PaymentAdjuster hasn't been implemented yet + let sum = Self::sum_qualified_payables_balances(&msg.qualified_payables); + let payments_we_can_pay = if U256::from(sum) > msg.consuming_wallet_balances.masq_tokens_wei + { + self.payment_adjuster_opt + .as_ref() + .expect("payment adjuster uninitialized") + .adjust_payments( + msg.qualified_payables, + msg.consuming_wallet_balances.masq_tokens_wei, + ) + } else { + msg.qualified_payables + }; + self.report_accounts_payable_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(ReportAccountsPayable { - accounts: msg.qualified_payables, + accounts: payments_we_can_pay, response_skeleton_opt: msg.response_skeleton_opt, }) .expect("BlockchainBridge is dead") @@ -447,6 +463,7 @@ impl Accountant { scanners, crashable: config.crash_point == CrashPoint::Message, scan_timings: ScanTimings::new(scan_intervals), + payment_adjuster_opt: None, financial_statistics: Rc::clone(&financial_statistics), report_accounts_payable_sub_opt: None, request_balances_to_pay_payables_sub_opt: None, @@ -662,6 +679,13 @@ impl Accountant { }) } + fn sum_qualified_payables_balances(qualified_accounts: &[PayableAccount]) -> u128 { + qualified_accounts + .iter() + .map(|account| account.balance_wei) + .sum() + } + fn handle_financials(&self, msg: &UiFinancialsRequest, client_id: u64, context_id: u64) { let body: MessageBody = self.compute_financials(msg, context_id); self.ui_message_sub_opt @@ -1034,6 +1058,9 @@ mod tests { DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; + use crate::sub_lib::neighborhood::{ + PaymentAdjusterQueryMessage, PaymentAdjusterResponseMessage, QualifiedNodesPaymentMetadata, + }; use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; @@ -1177,6 +1204,7 @@ mod tests { .as_any() .downcast_ref::() .unwrap(); + assert!(result.payment_adjuster_opt.is_none()); assert_eq!(result.crashable, false); assert_eq!(financial_statistics.total_paid_receivable_wei, 0); assert_eq!(financial_statistics.total_paid_payable_wei, 0); @@ -1353,7 +1381,7 @@ mod tests { } #[test] - fn received_balances_and_qualified_payables_considered_feasible_payments_thus_all_forwarded_to_blockchain_bridge( + fn received_balances_and_qualified_payables_considered_feasible_to_be_paid_thus_all_forwarded_to_blockchain_bridge( ) { let mut subject = AccountantBuilder::default().build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); @@ -1371,8 +1399,8 @@ mod tests { ConsumingWalletBalancesAndQualifiedPayables { qualified_payables: vec![account_1.clone(), account_2.clone()], consuming_wallet_balances: ConsumingWalletBalances { - gas_currency: U256::from(u32::MAX), - masq_tokens: U256::from(u32::MAX), + gas_currency_wei: U256::from(u32::MAX), + masq_tokens_wei: U256::from(u32::MAX), }, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1398,6 +1426,81 @@ mod tests { ); } + #[test] + fn received_qualified_payables_exceed_our_masq_balance_and_must_be_adjusted_before_forwarded_to_blockchain_bridge( + ) { + let mut subject = AccountantBuilder::default().build(); + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); + let neighborhood = + neighborhood.payment_adjuster_response(Some(PaymentAdjusterResponseMessage { + qualified_nodes_metadata: QualifiedNodesPaymentMetadata {}, + })); + let neighborhood_recipient = neighborhood.start().recipient(); + let report_recipient = blockchain_bridge + .system_stop_conditions(match_every_type_id!(ReportAccountsPayable)) + .start() + .recipient(); + subject.report_accounts_payable_sub_opt = Some(report_recipient); + subject.payment_adjuster_opt = Some(PaymentAdjuster::new(neighborhood_recipient)); + let subject_addr = subject.start(); + let last_paid_timestamp = SystemTime::now() + .checked_sub(Duration::from_secs(100)) + .unwrap(); + let account_1_wallet = make_wallet("voila"); + let account_1 = PayableAccount { + wallet: account_1_wallet.clone(), + balance_wei: 13_000_000, + last_paid_timestamp, + pending_payable_opt: None, + }; + let account_2_wallet = make_wallet("blah"); + let account_2 = PayableAccount { + wallet: account_2_wallet.clone(), + balance_wei: 16_000_000, + last_paid_timestamp, + pending_payable_opt: None, + }; + let system = System::new("test"); + let consuming_balances_and_qualified_payments = + ConsumingWalletBalancesAndQualifiedPayables { + qualified_payables: vec![account_1.clone(), account_2.clone()], + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(u32::MAX), + masq_tokens_wei: U256::from(25_000_000), + }, + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }), + }; + + subject_addr + .try_send(consuming_balances_and_qualified_payments) + .unwrap(); + + system.run(); + let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); + assert_eq!(neighborhood_recording.len(), 1); + assert_eq!( + neighborhood_recording.get_record::(0), + &PaymentAdjusterQueryMessage { + concerned_nodes_wallets: vec![account_1_wallet, account_2_wallet] + } + ); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + assert_eq!( + blockchain_bridge_recording.get_record::(0), + &ReportAccountsPayable { + accounts: vec![account_1, account_2], + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }) + } + ); + } + #[test] fn scan_pending_payables_request() { let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); @@ -3515,6 +3618,26 @@ mod tests { ); } + fn type_definite_conversion(gwei: u64) -> u128 { + gwei_to_wei(gwei) + } + + #[test] + fn sum_qualified_payables_balances_works() { + let qualified_payables = vec![ + make_payable_account(456), + make_payable_account(1111), + make_payable_account(7800), + ]; + + let result = Accountant::sum_qualified_payables_balances(&qualified_payables); + + let expected_result = type_definite_conversion(456) + + type_definite_conversion(1111) + + type_definite_conversion(7800); + assert_eq!(result, expected_result) + } + #[test] fn financials_request_with_nothing_to_respond_to_is_refused() { let system = System::new("test"); diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs new file mode 100644 index 000000000..15794587f --- /dev/null +++ b/node/src/accountant/payment_adjuster.rs @@ -0,0 +1,24 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payable_dao::PayableAccount; +use crate::sub_lib::neighborhood::PaymentAdjusterQueryMessage; +use actix::Recipient; +use web3::types::U256; + +pub struct PaymentAdjuster { + neighborhood_sub: Recipient, +} + +impl PaymentAdjuster { + pub fn new(neighborhood_sub: Recipient) -> Self { + Self { neighborhood_sub } + } + + pub fn adjust_payments( + &self, + payments_over_our_budget: Vec, + current_token_balance_wei: U256, + ) -> Vec { + todo!() + } +} diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 2feaf1a1f..2669ea4fa 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -287,8 +287,8 @@ impl BlockchainBridge { }; let consuming_wallet_balances = { ConsumingWalletBalances { - gas_currency: gas_balance, - masq_tokens: token_balance, + gas_currency_wei: gas_balance, + masq_tokens_wei: token_balance, } }; self.balances_and_payables_sub_opt @@ -692,8 +692,8 @@ mod tests { let gas_balance = U256::from(4455); let token_balance = U256::from(112233); let wallet_balances_found = ConsumingWalletBalances { - gas_currency: gas_balance, - masq_tokens: token_balance, + gas_currency_wei: gas_balance, + masq_tokens_wei: token_balance, }; let blockchain_interface = BlockchainInterfaceMock::default() .get_gas_balance_params(&get_gas_balance_params_arc) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 45685587b..e87202a54 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -46,8 +46,8 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData}; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; +use crate::sub_lib::neighborhood::ChangeNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; -use crate::sub_lib::neighborhood::NodeRecordMetadataMessage; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; @@ -339,10 +339,14 @@ impl Handler for Neighborhood { } } -impl Handler for Neighborhood { +impl Handler for Neighborhood { type Result = (); - fn handle(&mut self, msg: NodeRecordMetadataMessage, _ctx: &mut Self::Context) -> Self::Result { + fn handle( + &mut self, + msg: ChangeNodeRecordMetadataMessage, + _ctx: &mut Self::Context, + ) -> Self::Result { match msg.metadata_change { NRMetadataChange::AddUnreachableHost { hostname } => { let public_key = msg.public_key; @@ -497,7 +501,9 @@ impl Neighborhood { new_public_ip: addr.clone().recipient::(), node_query: addr.clone().recipient::(), route_query: addr.clone().recipient::(), - update_node_record_metadata: addr.clone().recipient::(), + update_node_record_metadata: addr + .clone() + .recipient::(), from_hopper: addr.clone().recipient::>(), gossip_failure: addr .clone() @@ -5311,7 +5317,7 @@ mod tests { let addr = subject.start(); let system = System::new("test"); - let _ = addr.try_send(NodeRecordMetadataMessage { + let _ = addr.try_send(ChangeNodeRecordMetadataMessage { public_key: public_key.clone(), metadata_change: NRMetadataChange::AddUnreachableHost { hostname: unreachable_host.clone(), diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 2e0dd4701..9e49f71cb 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -26,7 +26,7 @@ use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{Endpoint, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; use crate::sub_lib::neighborhood::RouteQueryResponse; -use crate::sub_lib::neighborhood::{ExpectedService, NodeRecordMetadataMessage}; +use crate::sub_lib::neighborhood::{ChangeNodeRecordMetadataMessage, ExpectedService}; use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; use crate::sub_lib::neighborhood::{NRMetadataChange, RouteQueryMessage}; use crate::sub_lib::peer_actors::BindMessage; @@ -65,7 +65,7 @@ struct ProxyServerOutSubs { hopper: Recipient, accountant: Recipient, route_source: Recipient, - update_node_record_metadata: Recipient, + update_node_record_metadata: Recipient, add_return_route: Recipient, add_route: Recipient, stream_shutdown_sub: Recipient, @@ -287,7 +287,7 @@ impl ProxyServer { .as_ref() .expect("Neighborhood unbound in ProxyServer") .update_node_record_metadata - .try_send(NodeRecordMetadataMessage { + .try_send(ChangeNodeRecordMetadataMessage { public_key: exit_public_key.clone(), metadata_change: NRMetadataChange::AddUnreachableHost { hostname: server_name, @@ -1087,7 +1087,7 @@ mod tests { hopper: recipient!(addr, IncipientCoresPackage), accountant: recipient!(addr, ReportServicesConsumedMessage), route_source: recipient!(addr, RouteQueryMessage), - update_node_record_metadata: recipient!(addr, NodeRecordMetadataMessage), + update_node_record_metadata: recipient!(addr, ChangeNodeRecordMetadataMessage), add_return_route: recipient!(addr, AddReturnRouteMessage), add_route: recipient!(addr, AddRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), @@ -3907,10 +3907,10 @@ mod tests { System::current().stop(); system.run(); let neighborhood_recording = neighborhood_log_arc.lock().unwrap(); - let record = neighborhood_recording.get_record::(0); + let record = neighborhood_recording.get_record::(0); assert_eq!( record, - &NodeRecordMetadataMessage { + &ChangeNodeRecordMetadataMessage { public_key: exit_public_key, metadata_change: NRMetadataChange::AddUnreachableHost { hostname: "server.com".to_string() @@ -3971,7 +3971,8 @@ mod tests { System::current().stop(); system.run(); let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); - let record_opt = neighborhood_recording.get_record_opt::(0); + let record_opt = + neighborhood_recording.get_record_opt::(0); assert_eq!(record_opt, None); } diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 7a5e679de..11e9e632a 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -61,8 +61,8 @@ impl SkeletonOptHolder for ReportAccountsPayable { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConsumingWalletBalances { - pub gas_currency: U256, - pub masq_tokens: U256, + pub gas_currency_wei: U256, + pub masq_tokens_wei: U256, } #[cfg(test)] diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 4a1b7f78f..9d3ca93ef 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -383,7 +383,7 @@ pub struct NeighborhoodSubs { pub new_public_ip: Recipient, pub node_query: Recipient, pub route_query: Recipient, - pub update_node_record_metadata: Recipient, + pub update_node_record_metadata: Recipient, pub from_hopper: Recipient>, pub gossip_failure: Recipient>, pub dispatcher_node_query: Recipient, @@ -517,7 +517,7 @@ pub struct AskAboutDebutGossipMessage { } #[derive(Clone, Debug, Message, PartialEq, Eq)] -pub struct NodeRecordMetadataMessage { +pub struct ChangeNodeRecordMetadataMessage { pub public_key: PublicKey, pub metadata_change: NRMetadataChange, } @@ -527,6 +527,23 @@ pub enum NRMetadataChange { AddUnreachableHost { hostname: String }, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PaymentAdjusterQueryMessage { + pub concerned_nodes_wallets: Vec, +} + +impl Message for PaymentAdjusterQueryMessage { + type Result = Option; +} + +#[derive(Clone, Debug, Message, PartialEq, Eq)] +pub struct PaymentAdjusterResponseMessage { + pub qualified_nodes_metadata: QualifiedNodesPaymentMetadata, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct QualifiedNodesPaymentMetadata {} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[allow(non_camel_case_types)] pub enum GossipFailure_0v1 { @@ -620,7 +637,7 @@ mod tests { new_public_ip: recipient!(recorder, NewPublicIp), node_query: recipient!(recorder, NodeQueryMessage), route_query: recipient!(recorder, RouteQueryMessage), - update_node_record_metadata: recipient!(recorder, NodeRecordMetadataMessage), + update_node_record_metadata: recipient!(recorder, ChangeNodeRecordMetadataMessage), from_hopper: recipient!(recorder, ExpiredCoresPackage), gossip_failure: recipient!(recorder, ExpiredCoresPackage), dispatcher_node_query: recipient!(recorder, DispatcherNodeQueryMessage), diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 29bed80ac..77e28734a 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -23,14 +23,14 @@ use crate::sub_lib::dispatcher::{DispatcherSubs, StreamShutdownMsg}; use crate::sub_lib::hopper::IncipientCoresPackage; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{HopperSubs, MessageType}; -use crate::sub_lib::neighborhood::ConnectionProgressMessage; use crate::sub_lib::neighborhood::NeighborhoodSubs; use crate::sub_lib::neighborhood::NodeQueryMessage; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; -use crate::sub_lib::neighborhood::NodeRecordMetadataMessage; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; +use crate::sub_lib::neighborhood::{ChangeNodeRecordMetadataMessage, PaymentAdjusterQueryMessage}; +use crate::sub_lib::neighborhood::{ConnectionProgressMessage, PaymentAdjusterResponseMessage}; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; use crate::sub_lib::peer_actors::PeerActors; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; @@ -66,6 +66,7 @@ pub struct Recorder { recording: Arc>, node_query_responses: Vec>, route_query_responses: Vec>, + payment_adjuster_responses: Vec>, stop_conditions_opt: Option, } @@ -116,7 +117,7 @@ recorder_message_handler!(NewPasswordMessage); recorder_message_handler!(NewPublicIp); recorder_message_handler!(NodeFromUiMessage); recorder_message_handler!(NodeToUiMessage); -recorder_message_handler!(NodeRecordMetadataMessage); +recorder_message_handler!(ChangeNodeRecordMetadataMessage); recorder_message_handler!(NoLookupIncipientCoresPackage); recorder_message_handler!(PoolBindMessage); recorder_message_handler!(ReceivedPayments); @@ -154,37 +155,44 @@ where } } -impl Handler for Recorder { - type Result = MessageResult; +macro_rules! asynchronous_message_handler_impl { + ($inbound_message: ty, $outbound_message: ty, $recorder_field_with_responses: ident) => { + impl Handler<$inbound_message> for Recorder { + type Result = MessageResult<$inbound_message>; + + fn handle( + &mut self, + msg: $inbound_message, + _ctx: &mut Self::Context, + ) -> >::Result { + self.record(msg); + + let error_msg = format!( + "No {} prepared for {}", + stringify!($inbound_message), + stringify!($outbound_message) + ); - fn handle( - &mut self, - msg: NodeQueryMessage, - _ctx: &mut Self::Context, - ) -> >::Result { - self.record(msg); - MessageResult(extract_response( - &mut self.node_query_responses, - "No NodeDescriptors prepared for NodeQueryMessage", - )) - } + MessageResult(extract_response( + &mut self.$recorder_field_with_responses, + &error_msg, + )) + } + } + }; } -impl Handler for Recorder { - type Result = MessageResult; - - fn handle( - &mut self, - msg: RouteQueryMessage, - _ctx: &mut Self::Context, - ) -> >::Result { - self.record(msg); - MessageResult(extract_response( - &mut self.route_query_responses, - "No RouteQueryResponses prepared for RouteQueryMessage", - )) - } -} +asynchronous_message_handler_impl!(NodeQueryMessage, NodeDescriptors, node_query_responses); +asynchronous_message_handler_impl!( + RouteQueryMessage, + RouteQueryResponses, + route_query_responses +); +asynchronous_message_handler_impl!( + PaymentAdjusterQueryMessage, + PaymentAdjusterResponseMessage, + payment_adjuster_responses +); fn extract_response(responses: &mut Vec, err_msg: &str) -> T where @@ -232,6 +240,14 @@ impl Recorder { self } + pub fn payment_adjuster_response( + mut self, + response: Option, + ) -> Recorder { + self.payment_adjuster_responses.push(response); + self + } + pub fn system_stop_conditions(mut self, stop_conditions: StopConditions) -> Recorder { if self.stop_conditions_opt.is_none() { self.start_system_killer(); @@ -403,7 +419,7 @@ pub fn make_neighborhood_subs_from(addr: &Addr) -> NeighborhoodSubs { new_public_ip: recipient!(addr, NewPublicIp), node_query: recipient!(addr, NodeQueryMessage), route_query: recipient!(addr, RouteQueryMessage), - update_node_record_metadata: recipient!(addr, NodeRecordMetadataMessage), + update_node_record_metadata: recipient!(addr, ChangeNodeRecordMetadataMessage), from_hopper: recipient!(addr, ExpiredCoresPackage), gossip_failure: recipient!(addr, ExpiredCoresPackage), dispatcher_node_query: recipient!(addr, DispatcherNodeQueryMessage), From 69616538640ba8ef11ee38354c4677eb00eacafa Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 8 May 2023 12:45:55 +0200 Subject: [PATCH 002/250] GH-672: simplifying GH-554, let's go on without the Neighborhood involved for the time being --- node/src/accountant/mod.rs | 24 +----- node/src/accountant/payment_adjuster.rs | 7 +- node/src/neighborhood/mod.rs | 10 +-- node/src/proxy_server/mod.rs | 14 ++-- node/src/sub_lib/neighborhood.rs | 23 +----- node/src/test_utils/recorder.rs | 102 ++++++++++-------------- 6 files changed, 63 insertions(+), 117 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 4928e43fb..42cb84212 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1058,9 +1058,6 @@ mod tests { DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; - use crate::sub_lib::neighborhood::{ - PaymentAdjusterQueryMessage, PaymentAdjusterResponseMessage, QualifiedNodesPaymentMetadata, - }; use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; @@ -1381,7 +1378,7 @@ mod tests { } #[test] - fn received_balances_and_qualified_payables_considered_feasible_to_be_paid_thus_all_forwarded_to_blockchain_bridge( + fn received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge( ) { let mut subject = AccountantBuilder::default().build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); @@ -1427,22 +1424,15 @@ mod tests { } #[test] - fn received_qualified_payables_exceed_our_masq_balance_and_must_be_adjusted_before_forwarded_to_blockchain_bridge( + fn received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge( ) { let mut subject = AccountantBuilder::default().build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); - let neighborhood = - neighborhood.payment_adjuster_response(Some(PaymentAdjusterResponseMessage { - qualified_nodes_metadata: QualifiedNodesPaymentMetadata {}, - })); - let neighborhood_recipient = neighborhood.start().recipient(); let report_recipient = blockchain_bridge .system_stop_conditions(match_every_type_id!(ReportAccountsPayable)) .start() .recipient(); subject.report_accounts_payable_sub_opt = Some(report_recipient); - subject.payment_adjuster_opt = Some(PaymentAdjuster::new(neighborhood_recipient)); let subject_addr = subject.start(); let last_paid_timestamp = SystemTime::now() .checked_sub(Duration::from_secs(100)) @@ -1479,15 +1469,7 @@ mod tests { .try_send(consuming_balances_and_qualified_payments) .unwrap(); - system.run(); - let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); - assert_eq!(neighborhood_recording.len(), 1); - assert_eq!( - neighborhood_recording.get_record::(0), - &PaymentAdjusterQueryMessage { - concerned_nodes_wallets: vec![account_1_wallet, account_2_wallet] - } - ); + assert_eq!(system.run(),0); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( blockchain_bridge_recording.get_record::(0), diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 15794587f..88da04b32 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,17 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_dao::PayableAccount; -use crate::sub_lib::neighborhood::PaymentAdjusterQueryMessage; -use actix::Recipient; use web3::types::U256; pub struct PaymentAdjuster { - neighborhood_sub: Recipient, } impl PaymentAdjuster { - pub fn new(neighborhood_sub: Recipient) -> Self { - Self { neighborhood_sub } + pub fn new() -> Self { + Self {} } pub fn adjust_payments( diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index e87202a54..80cd9766a 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -46,7 +46,7 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData}; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::ChangeNodeRecordMetadataMessage; +use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; @@ -339,12 +339,12 @@ impl Handler for Neighborhood { } } -impl Handler for Neighborhood { +impl Handler for Neighborhood { type Result = (); fn handle( &mut self, - msg: ChangeNodeRecordMetadataMessage, + msg: UpdateNodeRecordMetadataMessage, _ctx: &mut Self::Context, ) -> Self::Result { match msg.metadata_change { @@ -503,7 +503,7 @@ impl Neighborhood { route_query: addr.clone().recipient::(), update_node_record_metadata: addr .clone() - .recipient::(), + .recipient::(), from_hopper: addr.clone().recipient::>(), gossip_failure: addr .clone() @@ -5317,7 +5317,7 @@ mod tests { let addr = subject.start(); let system = System::new("test"); - let _ = addr.try_send(ChangeNodeRecordMetadataMessage { + let _ = addr.try_send(UpdateNodeRecordMetadataMessage { public_key: public_key.clone(), metadata_change: NRMetadataChange::AddUnreachableHost { hostname: unreachable_host.clone(), diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 9e49f71cb..b99224292 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -26,7 +26,7 @@ use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{Endpoint, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; use crate::sub_lib::neighborhood::RouteQueryResponse; -use crate::sub_lib::neighborhood::{ChangeNodeRecordMetadataMessage, ExpectedService}; +use crate::sub_lib::neighborhood::{UpdateNodeRecordMetadataMessage, ExpectedService}; use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; use crate::sub_lib::neighborhood::{NRMetadataChange, RouteQueryMessage}; use crate::sub_lib::peer_actors::BindMessage; @@ -65,7 +65,7 @@ struct ProxyServerOutSubs { hopper: Recipient, accountant: Recipient, route_source: Recipient, - update_node_record_metadata: Recipient, + update_node_record_metadata: Recipient, add_return_route: Recipient, add_route: Recipient, stream_shutdown_sub: Recipient, @@ -287,7 +287,7 @@ impl ProxyServer { .as_ref() .expect("Neighborhood unbound in ProxyServer") .update_node_record_metadata - .try_send(ChangeNodeRecordMetadataMessage { + .try_send(UpdateNodeRecordMetadataMessage { public_key: exit_public_key.clone(), metadata_change: NRMetadataChange::AddUnreachableHost { hostname: server_name, @@ -1087,7 +1087,7 @@ mod tests { hopper: recipient!(addr, IncipientCoresPackage), accountant: recipient!(addr, ReportServicesConsumedMessage), route_source: recipient!(addr, RouteQueryMessage), - update_node_record_metadata: recipient!(addr, ChangeNodeRecordMetadataMessage), + update_node_record_metadata: recipient!(addr, UpdateNodeRecordMetadataMessage), add_return_route: recipient!(addr, AddReturnRouteMessage), add_route: recipient!(addr, AddRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), @@ -3907,10 +3907,10 @@ mod tests { System::current().stop(); system.run(); let neighborhood_recording = neighborhood_log_arc.lock().unwrap(); - let record = neighborhood_recording.get_record::(0); + let record = neighborhood_recording.get_record::(0); assert_eq!( record, - &ChangeNodeRecordMetadataMessage { + &UpdateNodeRecordMetadataMessage { public_key: exit_public_key, metadata_change: NRMetadataChange::AddUnreachableHost { hostname: "server.com".to_string() @@ -3972,7 +3972,7 @@ mod tests { system.run(); let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); let record_opt = - neighborhood_recording.get_record_opt::(0); + neighborhood_recording.get_record_opt::(0); assert_eq!(record_opt, None); } diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 9d3ca93ef..7443b1b40 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -383,7 +383,7 @@ pub struct NeighborhoodSubs { pub new_public_ip: Recipient, pub node_query: Recipient, pub route_query: Recipient, - pub update_node_record_metadata: Recipient, + pub update_node_record_metadata: Recipient, pub from_hopper: Recipient>, pub gossip_failure: Recipient>, pub dispatcher_node_query: Recipient, @@ -517,7 +517,7 @@ pub struct AskAboutDebutGossipMessage { } #[derive(Clone, Debug, Message, PartialEq, Eq)] -pub struct ChangeNodeRecordMetadataMessage { +pub struct UpdateNodeRecordMetadataMessage { pub public_key: PublicKey, pub metadata_change: NRMetadataChange, } @@ -527,23 +527,6 @@ pub enum NRMetadataChange { AddUnreachableHost { hostname: String }, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PaymentAdjusterQueryMessage { - pub concerned_nodes_wallets: Vec, -} - -impl Message for PaymentAdjusterQueryMessage { - type Result = Option; -} - -#[derive(Clone, Debug, Message, PartialEq, Eq)] -pub struct PaymentAdjusterResponseMessage { - pub qualified_nodes_metadata: QualifiedNodesPaymentMetadata, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct QualifiedNodesPaymentMetadata {} - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[allow(non_camel_case_types)] pub enum GossipFailure_0v1 { @@ -637,7 +620,7 @@ mod tests { new_public_ip: recipient!(recorder, NewPublicIp), node_query: recipient!(recorder, NodeQueryMessage), route_query: recipient!(recorder, RouteQueryMessage), - update_node_record_metadata: recipient!(recorder, ChangeNodeRecordMetadataMessage), + update_node_record_metadata: recipient!(recorder, UpdateNodeRecordMetadataMessage), from_hopper: recipient!(recorder, ExpiredCoresPackage), gossip_failure: recipient!(recorder, ExpiredCoresPackage), dispatcher_node_query: recipient!(recorder, DispatcherNodeQueryMessage), diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 77e28734a..6ce8af379 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -5,7 +5,8 @@ use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayables, }; -use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; +use crate::sub_lib::neighborhood::ConnectionProgressMessage; +use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint}; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::daemon::crash_notification::CrashNotification; use crate::daemon::DaemonBindMessage; @@ -29,8 +30,7 @@ use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; -use crate::sub_lib::neighborhood::{ChangeNodeRecordMetadataMessage, PaymentAdjusterQueryMessage}; -use crate::sub_lib::neighborhood::{ConnectionProgressMessage, PaymentAdjusterResponseMessage}; +use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; use crate::sub_lib::peer_actors::PeerActors; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; @@ -66,7 +66,6 @@ pub struct Recorder { recording: Arc>, node_query_responses: Vec>, route_query_responses: Vec>, - payment_adjuster_responses: Vec>, stop_conditions_opt: Option, } @@ -117,7 +116,7 @@ recorder_message_handler!(NewPasswordMessage); recorder_message_handler!(NewPublicIp); recorder_message_handler!(NodeFromUiMessage); recorder_message_handler!(NodeToUiMessage); -recorder_message_handler!(ChangeNodeRecordMetadataMessage); +recorder_message_handler!(UpdateNodeRecordMetadataMessage); recorder_message_handler!(NoLookupIncipientCoresPackage); recorder_message_handler!(PoolBindMessage); recorder_message_handler!(ReceivedPayments); @@ -145,8 +144,8 @@ recorder_message_handler!(ConnectionProgressMessage); recorder_message_handler!(ScanForPendingPayables); impl Handler> for Recorder -where - M: Message + PartialEq + Send + 'static, + where + M: Message + PartialEq + Send + 'static, { type Result = (); @@ -155,48 +154,41 @@ where } } -macro_rules! asynchronous_message_handler_impl { - ($inbound_message: ty, $outbound_message: ty, $recorder_field_with_responses: ident) => { - impl Handler<$inbound_message> for Recorder { - type Result = MessageResult<$inbound_message>; - - fn handle( - &mut self, - msg: $inbound_message, - _ctx: &mut Self::Context, - ) -> >::Result { - self.record(msg); - - let error_msg = format!( - "No {} prepared for {}", - stringify!($inbound_message), - stringify!($outbound_message) - ); +impl Handler for Recorder { + type Result = MessageResult; - MessageResult(extract_response( - &mut self.$recorder_field_with_responses, - &error_msg, - )) - } - } - }; + fn handle( + &mut self, + msg: NodeQueryMessage, + _ctx: &mut Self::Context, + ) -> >::Result { + self.record(msg); + MessageResult(extract_response( + &mut self.node_query_responses, + "No NodeDescriptors prepared for NodeQueryMessage", + )) + } } -asynchronous_message_handler_impl!(NodeQueryMessage, NodeDescriptors, node_query_responses); -asynchronous_message_handler_impl!( - RouteQueryMessage, - RouteQueryResponses, - route_query_responses -); -asynchronous_message_handler_impl!( - PaymentAdjusterQueryMessage, - PaymentAdjusterResponseMessage, - payment_adjuster_responses -); +impl Handler for Recorder { + type Result = MessageResult; + + fn handle( + &mut self, + msg: RouteQueryMessage, + _ctx: &mut Self::Context, + ) -> >::Result { + self.record(msg); + MessageResult(extract_response( + &mut self.route_query_responses, + "No RouteQueryResponses prepared for RouteQueryMessage", + )) + } +} fn extract_response(responses: &mut Vec, err_msg: &str) -> T -where - T: Clone, + where + T: Clone, { match responses.len() { n if n == 0 => panic!("{}", err_msg), @@ -211,8 +203,8 @@ impl Recorder { } pub fn record(&mut self, item: T) - where - T: Any + Send, + where + T: Any + Send, { let mut recording = self.recording.lock().unwrap(); let messages: &mut Vec> = &mut recording.messages; @@ -240,14 +232,6 @@ impl Recorder { self } - pub fn payment_adjuster_response( - mut self, - response: Option, - ) -> Recorder { - self.payment_adjuster_responses.push(response); - self - } - pub fn system_stop_conditions(mut self, stop_conditions: StopConditions) -> Recorder { if self.stop_conditions_opt.is_none() { self.start_system_killer(); @@ -297,16 +281,16 @@ impl Recording { } pub fn get_record(&self, index: usize) -> &T - where - T: Any + Send, + where + T: Any + Send, { self.get_record_inner_body(index) .unwrap_or_else(|e| panic!("{}", e)) } pub fn get_record_opt(&self, index: usize) -> Option<&T> - where - T: Any + Send, + where + T: Any + Send, { self.get_record_inner_body(index).ok() } @@ -419,7 +403,7 @@ pub fn make_neighborhood_subs_from(addr: &Addr) -> NeighborhoodSubs { new_public_ip: recipient!(addr, NewPublicIp), node_query: recipient!(addr, NodeQueryMessage), route_query: recipient!(addr, RouteQueryMessage), - update_node_record_metadata: recipient!(addr, ChangeNodeRecordMetadataMessage), + update_node_record_metadata: recipient!(addr, UpdateNodeRecordMetadataMessage), from_hopper: recipient!(addr, ExpiredCoresPackage), gossip_failure: recipient!(addr, ExpiredCoresPackage), dispatcher_node_query: recipient!(addr, DispatcherNodeQueryMessage), From a60c3aea76e834f08b5ff8f0e9fe1d0c75c52668 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 11 May 2023 11:59:01 +0200 Subject: [PATCH 003/250] GH-672: PaymentAdjusterMock and the supertrait with ScannerEtensions implemented for a try --- node/src/accountant/mod.rs | 191 +++++++++++------------ node/src/accountant/payment_adjuster.rs | 146 ++++++++++++++++- node/src/accountant/scanners.rs | 84 +++++++++- node/src/accountant/test_utils.rs | 63 +++++++- node/src/blockchain/blockchain_bridge.rs | 20 +-- node/src/neighborhood/mod.rs | 2 +- node/src/proxy_server/mod.rs | 2 +- node/src/sub_lib/blockchain_bridge.rs | 6 +- node/src/test_utils/recorder.rs | 30 ++-- 9 files changed, 399 insertions(+), 145 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 42cb84212..d82fda97b 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -30,7 +30,7 @@ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDaoError}; -use crate::accountant::payment_adjuster::PaymentAdjuster; +use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; use crate::accountant::scanners::{ScanTimings, Scanners}; @@ -46,7 +46,7 @@ use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::accountant::{MessageIdGenerator, MessageIdGeneratorReal}; use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, ReportAccountsPayable, RequestBalancesToPayPayables, + ConsumingWalletBalances, OutcomingPayamentsInstructions, RequestBalancesToPayPayables, }; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; @@ -75,7 +75,7 @@ use std::ops::{Div, Mul}; use std::path::Path; use std::rc::Rc; use std::time::SystemTime; -use web3::types::{TransactionReceipt, H256, U256}; +use web3::types::{TransactionReceipt, H256}; pub const CRASH_KEY: &str = "ACCOUNTANT"; @@ -91,9 +91,9 @@ pub struct Accountant { crashable: bool, scanners: Scanners, scan_timings: ScanTimings, - payment_adjuster_opt: Option, + payment_adjuster: Box, financial_statistics: Rc>, - report_accounts_payable_sub_opt: Option>, + outcoming_payments_instructions_sub_opt: Option>, request_balances_to_pay_payables_sub_opt: Option>, retrieve_transactions_sub_opt: Option>, request_transaction_receipts_subs_opt: Option>, @@ -135,7 +135,7 @@ pub struct SentPayables { pub response_skeleton_opt: Option, } -#[derive(Debug, Message, PartialEq, Eq)] +#[derive(Debug, Message, PartialEq, Eq, Clone)] pub struct ConsumingWalletBalancesAndQualifiedPayables { pub qualified_payables: Vec, pub consuming_wallet_balances: ConsumingWalletBalances, @@ -222,27 +222,24 @@ impl Handler for Accountant { msg: ConsumingWalletBalancesAndQualifiedPayables, _ctx: &mut Self::Context, ) -> Self::Result { - let sum = Self::sum_qualified_payables_balances(&msg.qualified_payables); - let payments_we_can_pay = if U256::from(sum) > msg.consuming_wallet_balances.masq_tokens_wei - { - self.payment_adjuster_opt - .as_ref() - .expect("payment adjuster uninitialized") - .adjust_payments( - msg.qualified_payables, - msg.consuming_wallet_balances.masq_tokens_wei, - ) - } else { - msg.qualified_payables + let instructions = match self.scanners.payable.is_mid_processing_required(&msg) { + false => OutcomingPayamentsInstructions { + accounts: msg.qualified_payables, + response_skeleton_opt: msg.response_skeleton_opt, + }, + true => { + //TODO we will eventually query info from Neighborhood here + self + .scanners + .payable + .process_mid_msg_with_context(msg, ()) + .expect("Method without real implementation") + }, }; - - self.report_accounts_payable_sub_opt + self.outcoming_payments_instructions_sub_opt .as_ref() .expect("BlockchainBridge is unbound") - .try_send(ReportAccountsPayable { - accounts: payments_we_can_pay, - response_skeleton_opt: msg.response_skeleton_opt, - }) + .try_send(instructions) .expect("BlockchainBridge is dead") } } @@ -463,9 +460,9 @@ impl Accountant { scanners, crashable: config.crash_point == CrashPoint::Message, scan_timings: ScanTimings::new(scan_intervals), - payment_adjuster_opt: None, + payment_adjuster: Box::new(PaymentAdjusterReal::new()), financial_statistics: Rc::clone(&financial_statistics), - report_accounts_payable_sub_opt: None, + outcoming_payments_instructions_sub_opt: None, request_balances_to_pay_payables_sub_opt: None, report_sent_payables_sub_opt: None, retrieve_transactions_sub_opt: None, @@ -578,7 +575,7 @@ impl Accountant { } fn handle_bind_message(&mut self, msg: BindMessage) { - self.report_accounts_payable_sub_opt = + self.outcoming_payments_instructions_sub_opt = Some(msg.peer_actors.blockchain_bridge.report_accounts_payable); self.retrieve_transactions_sub_opt = Some(msg.peer_actors.blockchain_bridge.retrieve_transactions); @@ -679,13 +676,6 @@ impl Accountant { }) } - fn sum_qualified_payables_balances(qualified_accounts: &[PayableAccount]) -> u128 { - qualified_accounts - .iter() - .map(|account| account.balance_wei) - .sum() - } - fn handle_financials(&self, msg: &UiFinancialsRequest, client_id: u64, context_id: u64) { let body: MessageBody = self.compute_financials(msg, context_id); self.ui_message_sub_opt @@ -1042,8 +1032,8 @@ mod tests { use crate::accountant::test_utils::{ bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, - PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, - ReceivableDaoMock, + PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, + ReceivableDaoFactoryMock, ReceivableDaoMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; @@ -1057,7 +1047,7 @@ mod tests { ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, }; - use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; + use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; @@ -1201,7 +1191,6 @@ mod tests { .as_any() .downcast_ref::() .unwrap(); - assert!(result.payment_adjuster_opt.is_none()); assert_eq!(result.crashable, false); assert_eq!(financial_statistics.total_paid_receivable_wei, 0); assert_eq!(financial_statistics.total_paid_payable_wei, 0); @@ -1380,17 +1369,24 @@ mod tests { #[test] fn received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge( ) { - let mut subject = AccountantBuilder::default().build(); + // the numbers for balances don't do real math, they need not to match either the condition for + // the payment adjustment or the actual values that come from the payable size reducing algorithm; + // all that is mocked in this test + let is_adjustment_required_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(ReportAccountsPayable)) + .system_stop_conditions(match_every_type_id!(OutcomingPayamentsInstructions)) .start() .recipient(); - subject.report_accounts_payable_sub_opt = Some(report_recipient); + let mut subject = AccountantBuilder::default().build(); + let payment_adjuster = PaymentAdjusterMock::default() + .is_adjustment_required_params(&is_adjustment_required_params_arc) + .is_adjustment_required_result(false); + subject.payment_adjuster = Box::new(payment_adjuster); + subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); let subject_addr = subject.start(); - let half_of_u32_max_in_wei = u32::MAX as u64 / (2 * WEIS_IN_GWEI as u64); - let account_1 = make_payable_account(half_of_u32_max_in_wei); - let account_2 = account_1.clone(); + let account_1 = make_payable_account(44_444); + let account_2 = make_payable_account(333_333); let system = System::new("test"); let consuming_balances_and_qualified_payments = ConsumingWalletBalancesAndQualifiedPayables { @@ -1406,14 +1402,19 @@ mod tests { }; subject_addr - .try_send(consuming_balances_and_qualified_payments) + .try_send(consuming_balances_and_qualified_payments.clone()) .unwrap(); system.run(); + let is_adjustment_required_params = is_adjustment_required_params_arc.lock().unwrap(); + assert_eq!( + *is_adjustment_required_params, + vec![consuming_balances_and_qualified_payments] + ); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &ReportAccountsPayable { + blockchain_bridge_recording.get_record::(0), + &OutcomingPayamentsInstructions { accounts: vec![account_1, account_2], response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1421,64 +1422,72 @@ mod tests { }) } ); + // adjust_payments() did not need a prepared result which means it wasn't reached + // because otherwise this test would've panicked } #[test] fn received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge( ) { - let mut subject = AccountantBuilder::default().build(); + // the numbers for balances don't do real math, they need not to match either the condition for + // the payment adjustment or the actual values that come from the payable size reducing algorithm; + // all that is mocked in this test + let adjust_payments_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(ReportAccountsPayable)) + .system_stop_conditions(match_every_type_id!(OutcomingPayamentsInstructions)) .start() .recipient(); - subject.report_accounts_payable_sub_opt = Some(report_recipient); - let subject_addr = subject.start(); - let last_paid_timestamp = SystemTime::now() - .checked_sub(Duration::from_secs(100)) - .unwrap(); - let account_1_wallet = make_wallet("voila"); - let account_1 = PayableAccount { - wallet: account_1_wallet.clone(), - balance_wei: 13_000_000, - last_paid_timestamp, - pending_payable_opt: None, + let mut subject = AccountantBuilder::default().build(); + let unadjusted_account_1 = make_payable_account(111_111); + let unadjusted_account_2 = make_payable_account(222_222); + let adjusted_account_1 = PayableAccount { + balance_wei: gwei_to_wei(55_550_u64), + ..unadjusted_account_1 }; - let account_2_wallet = make_wallet("blah"); - let account_2 = PayableAccount { - wallet: account_2_wallet.clone(), - balance_wei: 16_000_000, - last_paid_timestamp, - pending_payable_opt: None, + let adjusted_account_2 = PayableAccount { + balance_wei: gwei_to_wei(100_000_u64), + ..unadjusted_account_2 + }; + let response_skeleton = ResponseSkeleton { + client_id: 12, + context_id: 55, }; + let adjusted_payments_instructions = OutcomingPayamentsInstructions { + accounts: vec![adjusted_account_1.clone(), adjusted_account_2.clone()], + response_skeleton_opt: Some(response_skeleton), + }; + let payment_adjuster = PaymentAdjusterMock::default() + .is_adjustment_required_result(true) + .adjust_payments_params(&adjust_payments_params_arc) + .adjust_payments_result(adjusted_payments_instructions); + subject.payment_adjuster = Box::new(payment_adjuster); + subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); + let subject_addr = subject.start(); + let account_1 = make_payable_account(111_111); + let account_2 = make_payable_account(222_222); let system = System::new("test"); let consuming_balances_and_qualified_payments = ConsumingWalletBalancesAndQualifiedPayables { qualified_payables: vec![account_1.clone(), account_2.clone()], consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), - masq_tokens_wei: U256::from(25_000_000), + masq_tokens_wei: U256::from(150_000_000_000_u64), }, - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 1234, - context_id: 4321, - }), + response_skeleton_opt: Some(response_skeleton), }; subject_addr .try_send(consuming_balances_and_qualified_payments) .unwrap(); - assert_eq!(system.run(),0); + assert_eq!(system.run(), 0); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &ReportAccountsPayable { - accounts: vec![account_1, account_2], - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 1234, - context_id: 4321, - }) + blockchain_bridge_recording.get_record::(0), + &OutcomingPayamentsInstructions { + accounts: vec![adjusted_account_1, adjusted_account_2], + response_skeleton_opt: Some(response_skeleton) } ); } @@ -2273,12 +2282,12 @@ mod tests { ); let blockchain_bridge_addr: Addr = blockchain_bridge.start(); let report_accounts_payable_sub = - blockchain_bridge_addr.recipient::(); + blockchain_bridge_addr.recipient::(); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_daos(vec![ForPayableScanner(payable_dao)]) .build(); - subject.report_accounts_payable_sub_opt = Some(report_accounts_payable_sub); + subject.outcoming_payments_instructions_sub_opt = Some(report_accounts_payable_sub); let _result = subject .scanners @@ -3600,26 +3609,6 @@ mod tests { ); } - fn type_definite_conversion(gwei: u64) -> u128 { - gwei_to_wei(gwei) - } - - #[test] - fn sum_qualified_payables_balances_works() { - let qualified_payables = vec![ - make_payable_account(456), - make_payable_account(1111), - make_payable_account(7800), - ]; - - let result = Accountant::sum_qualified_payables_balances(&qualified_payables); - - let expected_result = type_definite_conversion(456) - + type_definite_conversion(1111) - + type_definite_conversion(7800); - assert_eq!(result, expected_result) - } - #[test] fn financials_request_with_nothing_to_respond_to_is_refused() { let system = System::new("test"); diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 88da04b32..2431b8fbc 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,21 +1,151 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_dao::PayableAccount; +use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; +use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; +use itertools::Itertools; use web3::types::U256; -pub struct PaymentAdjuster { +pub trait PaymentAdjuster { + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool; + + fn adjust_payments( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> OutcomingPayamentsInstructions; } -impl PaymentAdjuster { - pub fn new() -> Self { - Self {} +pub struct PaymentAdjusterReal {} + +impl PaymentAdjuster for PaymentAdjusterReal { + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool { + let sum = Self::sum_payable_balances(&msg.qualified_payables); + let consuming_wallet_balance = msg.consuming_wallet_balances.masq_tokens_wei; + if U256::from(sum) > consuming_wallet_balance { + true + } else if U256::from(Self::find_smallest_debt(&msg.qualified_payables)) + > consuming_wallet_balance + { + todo!() + } else { + false + } } - pub fn adjust_payments( + fn adjust_payments( &self, - payments_over_our_budget: Vec, - current_token_balance_wei: U256, - ) -> Vec { + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> OutcomingPayamentsInstructions { todo!() } } + +impl PaymentAdjusterReal { + pub fn new() -> Self { + Self {} + } + + fn sum_payable_balances(qualified_accounts: &[PayableAccount]) -> U256 { + qualified_accounts + .iter() + .map(|account| account.balance_wei) + .sum::() + .into() + } + + fn find_smallest_debt(qualified_accounts: &[PayableAccount]) -> U256 { + qualified_accounts + .iter() + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + }) + .last() + .expect("at least one qualified payable must have been sent here") + .balance_wei + .into() + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; + use crate::accountant::test_utils::make_payable_account; + use crate::accountant::{gwei_to_wei, ConsumingWalletBalancesAndQualifiedPayables}; + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + use web3::types::U256; + + fn type_definite_conversion(gwei: u64) -> u128 { + gwei_to_wei(gwei) + } + + #[test] + fn sum_payable_balances_works() { + let qualified_payables = vec![ + make_payable_account(456), + make_payable_account(1111), + make_payable_account(7800), + ]; + + let result = PaymentAdjusterReal::sum_payable_balances(&qualified_payables); + + let expected_result = type_definite_conversion(456) + + type_definite_conversion(1111) + + type_definite_conversion(7800); + assert_eq!(result, U256::from(expected_result)) + } + + fn make_cw_balance_and_q_payables_msg( + qualified_payables_balances_gwei: Vec, + masq_balance_gwei: u64, + ) -> ConsumingWalletBalancesAndQualifiedPayables { + let qualified_payables = qualified_payables_balances_gwei + .into_iter() + .map(|balance| make_payable_account(balance)) + .collect(); + ConsumingWalletBalancesAndQualifiedPayables { + qualified_payables, + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::zero(), + masq_tokens_wei: gwei_to_wei(masq_balance_gwei), + }, + response_skeleton_opt: None, + } + } + + #[test] + fn is_adjustment_required_works_for_non_error_cases() { + let subject = PaymentAdjusterReal::new(); + let msg_1 = make_cw_balance_and_q_payables_msg(vec![85, 14], 100); + let msg_2 = make_cw_balance_and_q_payables_msg(vec![85, 15], 100); + let msg_3 = make_cw_balance_and_q_payables_msg(vec![85, 16], 100); + + assert_eq!(subject.is_adjustment_required(&msg_1), false); + assert_eq!(subject.is_adjustment_required(&msg_2), false); + assert_eq!(subject.is_adjustment_required(&msg_3), true) + } + + #[test] + fn find_smallest_debt_works() { + let mut payable_1 = make_payable_account(111); + payable_1.balance_wei = 111_111; + let mut payable_3 = make_payable_account(333); + payable_3.balance_wei = 111_110; + let mut payable_2 = make_payable_account(222); + payable_2.balance_wei = 3_000_000; + let qualified_payables = vec![payable_1, payable_2, payable_3]; + + let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); + + assert_eq!(min, U256::from(111_110)) + } + + #[test] + fn find_smallest_debt_handles_just_one_account() { + let mut payable = make_payable_account(111); + let qualified_payables = vec![payable]; + + let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); + + assert_eq!(min, U256::from(111_000_000_000_u128)) + } +} diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 7a9d28c07..e436c8eea 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -12,9 +12,9 @@ use crate::accountant::scanners_utils::pending_payable_scanner_utils::{ }; use crate::accountant::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::{ - gwei_to_wei, Accountant, ReceivedPayments, ReportTransactionReceipts, - RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, - ScanForReceivables, SentPayables, + gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, + ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, + ScanForPendingPayables, ScanForReceivables, SentPayables, }; use crate::accountant::{PendingPayableId, PendingTransactionStatus}; use crate::banned_dao::BannedDao; @@ -23,7 +23,9 @@ use crate::blockchain::blockchain_interface::BlockchainError; use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; -use crate::sub_lib::blockchain_bridge::RequestBalancesToPayPayables; +use crate::sub_lib::blockchain_bridge::{ + OutcomingPayamentsInstructions, RequestBalancesToPayPayables, +}; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; use actix::{Context, Message, System}; @@ -44,7 +46,15 @@ use time::OffsetDateTime; use web3::types::TransactionReceipt; pub struct Scanners { - pub payable: Box>, + pub payable: Box< + dyn ExtendedScanner< + RequestBalancesToPayPayables, + ConsumingWalletBalancesAndQualifiedPayables, + (), + OutcomingPayamentsInstructions, + SentPayables, + >, + >, pub pending_payable: Box>, pub receivable: Box>, } @@ -99,6 +109,37 @@ where declare_as_any!(); } +//put only optional methods here +pub trait ScannerExtensions { + fn is_mid_processing_required(&self, _mid_findings_message: &MidFindingsMessage) -> bool { + intentionally_blank!() + } + + fn process_mid_msg_with_context( + &self, + _msg: MidFindingsMessage, + _args: ArgsFromContext, + ) -> Option { + intentionally_blank!() + } +} + +pub trait ExtendedScanner< + BeginMessage, + MidDataMessage, + ArgsFromContext, + MidResultsMessage, + EndMessage, +>: + Scanner + + ScannerExtensions where + BeginMessage: Message, + MidDataMessage: Message, + MidResultsMessage: Message, + EndMessage: Message, +{ +} + pub struct ScannerCommon { initiated_at_opt: Option, pub payment_thresholds: Rc, @@ -159,6 +200,17 @@ pub struct PayableScanner { pub payable_threshold_gauge: Box, } +impl + ExtendedScanner< + RequestBalancesToPayPayables, + ConsumingWalletBalancesAndQualifiedPayables, + ArgsFromContext, + OutcomingPayamentsInstructions, + SentPayables, + > for PayableScanner +{ +} + impl Scanner for PayableScanner { fn begin_scan( &mut self, @@ -228,6 +280,28 @@ impl Scanner for PayableScanner { implement_as_any!(); } +impl + ScannerExtensions< + ConsumingWalletBalancesAndQualifiedPayables, + OutcomingPayamentsInstructions, + ArgsFromContext, + > for PayableScanner +{ + fn is_mid_processing_required( + &self, + _mid_findings_message: &ConsumingWalletBalancesAndQualifiedPayables, + ) -> bool { + todo!() + } + fn process_mid_msg_with_context( + &self, + _msg: ConsumingWalletBalancesAndQualifiedPayables, + _args: ArgsFromContext, + ) -> Option { + todo!() + } +} + impl PayableScanner { pub fn new( payable_dao: Box, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index b7da1b0ad..30700ed78 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -6,6 +6,7 @@ use crate::accountant::dao_utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; +use crate::accountant::payment_adjuster::PaymentAdjuster; use crate::accountant::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoFactory, }; @@ -14,7 +15,10 @@ use crate::accountant::receivable_dao::{ }; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; -use crate::accountant::{gwei_to_wei, Accountant, PendingPayableId, DEFAULT_PENDING_TOO_LONG_SEC}; +use crate::accountant::{ + gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, PendingPayableId, + DEFAULT_PENDING_TOO_LONG_SEC, +}; use crate::banned_dao::{BannedDao, BannedDaoFactory}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; @@ -23,6 +27,7 @@ use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics}; use crate::sub_lib::accountant::{MessageIdGenerator, PaymentThresholds}; +use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::make_bc_with_defaults; @@ -1353,3 +1358,59 @@ impl PayableThresholdsGaugeMock { self } } + +#[derive(Default)] +pub struct PaymentAdjusterMock { + is_adjustment_required_params: Arc>>, + is_adjustment_required_results: RefCell>, + adjust_payments_params: Arc>>, + adjust_payments_results: RefCell>, +} + +impl PaymentAdjuster for PaymentAdjusterMock { + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool { + self.is_adjustment_required_params + .lock() + .unwrap() + .push(msg.clone()); + self.is_adjustment_required_results.borrow_mut().remove(0) + } + + fn adjust_payments( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> OutcomingPayamentsInstructions { + self.adjust_payments_params.lock().unwrap().push(msg); + self.adjust_payments_results.borrow_mut().remove(0) + } +} + +impl PaymentAdjusterMock { + pub fn is_adjustment_required_params( + mut self, + params: &Arc>>, + ) -> Self { + self.is_adjustment_required_params = params.clone(); + self + } + + pub fn is_adjustment_required_result(self, result: bool) -> Self { + self.is_adjustment_required_results + .borrow_mut() + .push(result); + self + } + + pub fn adjust_payments_params( + mut self, + params: &Arc>>, + ) -> Self { + self.adjust_payments_params = params.clone(); + self + } + + pub fn adjust_payments_result(self, result: OutcomingPayamentsInstructions) -> Self { + self.adjust_payments_results.borrow_mut().push(result); + self + } +} diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 2669ea4fa..7758188ab 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -17,7 +17,7 @@ use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, RequestBalancesToPayPayables}; -use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, ReportAccountsPayable}; +use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPayamentsInstructions}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::utils::{db_connection_launch_panic, handle_ui_crash_request}; @@ -149,10 +149,10 @@ impl Handler for BlockchainBridge { } } -impl Handler for BlockchainBridge { +impl Handler for BlockchainBridge { type Result = (); - fn handle(&mut self, msg: ReportAccountsPayable, _ctx: &mut Self::Context) { + fn handle(&mut self, msg: OutcomingPayamentsInstructions, _ctx: &mut Self::Context) { self.handle_scan( Self::handle_report_accounts_payable, ScanType::Payables, @@ -241,7 +241,7 @@ impl BlockchainBridge { pub fn make_subs_from(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, ReportAccountsPayable), + report_accounts_payable: recipient!(addr, OutcomingPayamentsInstructions), request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), @@ -306,7 +306,7 @@ impl BlockchainBridge { fn handle_report_accounts_payable( &mut self, - creditors_msg: ReportAccountsPayable, + creditors_msg: OutcomingPayamentsInstructions, ) -> Result<(), String> { let skeleton = creditors_msg.response_skeleton_opt; let processed_payments = self.preprocess_payments(&creditors_msg); @@ -325,7 +325,7 @@ impl BlockchainBridge { fn preprocess_payments( &self, - creditors_msg: &ReportAccountsPayable, + creditors_msg: &OutcomingPayamentsInstructions, ) -> Result>, String> { match self.consuming_wallet_opt.as_ref() { Some(consuming_wallet) => match self.persistent_config.gas_price() { @@ -628,7 +628,7 @@ mod tests { false, Some(consuming_wallet.clone()), ); - let request = ReportAccountsPayable { + let request = OutcomingPayamentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, @@ -666,7 +666,7 @@ mod tests { false, None, ); - let request = ReportAccountsPayable { + let request = OutcomingPayamentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, @@ -909,7 +909,7 @@ mod tests { let before = SystemTime::now(); let _ = addr - .try_send(ReportAccountsPayable { + .try_send(OutcomingPayamentsInstructions { accounts: vec![ PayableAccount { wallet: make_wallet("blah"), @@ -1009,7 +1009,7 @@ mod tests { Some(consuming_wallet), ); subject.scan_error_subs_opt = Some(scan_error_recipient); - let request = ReportAccountsPayable { + let request = OutcomingPayamentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 80cd9766a..7c2bff10c 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -46,11 +46,11 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData}; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; +use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor}; use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService}; diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index b99224292..a78db9908 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -26,7 +26,7 @@ use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{Endpoint, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; use crate::sub_lib::neighborhood::RouteQueryResponse; -use crate::sub_lib::neighborhood::{UpdateNodeRecordMetadataMessage, ExpectedService}; +use crate::sub_lib::neighborhood::{ExpectedService, UpdateNodeRecordMetadataMessage}; use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; use crate::sub_lib::neighborhood::{NRMetadataChange, RouteQueryMessage}; use crate::sub_lib::peer_actors::BindMessage; diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 11e9e632a..9aa08f5b2 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -22,7 +22,7 @@ pub struct BlockchainBridgeConfig { #[derive(Clone, PartialEq, Eq)] pub struct BlockchainBridgeSubs { pub bind: Recipient, - pub report_accounts_payable: Recipient, + pub report_accounts_payable: Recipient, pub request_balances_to_pay_payables: Recipient, pub retrieve_transactions: Recipient, pub ui_sub: Recipient, @@ -48,12 +48,12 @@ impl SkeletonOptHolder for RequestBalancesToPayPayables { } #[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct ReportAccountsPayable { +pub struct OutcomingPayamentsInstructions { pub accounts: Vec, pub response_skeleton_opt: Option, } -impl SkeletonOptHolder for ReportAccountsPayable { +impl SkeletonOptHolder for OutcomingPayamentsInstructions { fn skeleton_opt(&self) -> Option { self.response_skeleton_opt } diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 6ce8af379..af61491f8 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -5,8 +5,7 @@ use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayables, }; -use crate::sub_lib::neighborhood::ConnectionProgressMessage; -use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint}; +use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::daemon::crash_notification::CrashNotification; use crate::daemon::DaemonBindMessage; @@ -16,7 +15,7 @@ use crate::sub_lib::accountant::AccountantSubs; use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; -use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; +use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, RequestBalancesToPayPayables}; use crate::sub_lib::configurator::{ConfiguratorSubs, NewPasswordMessage}; use crate::sub_lib::dispatcher::InboundClientData; @@ -24,6 +23,7 @@ use crate::sub_lib::dispatcher::{DispatcherSubs, StreamShutdownMsg}; use crate::sub_lib::hopper::IncipientCoresPackage; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{HopperSubs, MessageType}; +use crate::sub_lib::neighborhood::ConnectionProgressMessage; use crate::sub_lib::neighborhood::NeighborhoodSubs; use crate::sub_lib::neighborhood::NodeQueryMessage; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; @@ -137,15 +137,15 @@ recorder_message_handler!(PendingPayableFingerprint); recorder_message_handler!(RetrieveTransactions); recorder_message_handler!(RequestTransactionReceipts); recorder_message_handler!(ReportTransactionReceipts); -recorder_message_handler!(ReportAccountsPayable); +recorder_message_handler!(OutcomingPayamentsInstructions); recorder_message_handler!(ScanForReceivables); recorder_message_handler!(ScanForPayables); recorder_message_handler!(ConnectionProgressMessage); recorder_message_handler!(ScanForPendingPayables); impl Handler> for Recorder - where - M: Message + PartialEq + Send + 'static, +where + M: Message + PartialEq + Send + 'static, { type Result = (); @@ -187,8 +187,8 @@ impl Handler for Recorder { } fn extract_response(responses: &mut Vec, err_msg: &str) -> T - where - T: Clone, +where + T: Clone, { match responses.len() { n if n == 0 => panic!("{}", err_msg), @@ -203,8 +203,8 @@ impl Recorder { } pub fn record(&mut self, item: T) - where - T: Any + Send, + where + T: Any + Send, { let mut recording = self.recording.lock().unwrap(); let messages: &mut Vec> = &mut recording.messages; @@ -281,16 +281,16 @@ impl Recording { } pub fn get_record(&self, index: usize) -> &T - where - T: Any + Send, + where + T: Any + Send, { self.get_record_inner_body(index) .unwrap_or_else(|e| panic!("{}", e)) } pub fn get_record_opt(&self, index: usize) -> Option<&T> - where - T: Any + Send, + where + T: Any + Send, { self.get_record_inner_body(index).ok() } @@ -447,7 +447,7 @@ pub fn make_ui_gateway_subs_from_recorder(addr: &Addr) -> UiGatewaySub pub fn make_blockchain_bridge_subs_from(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, ReportAccountsPayable), + report_accounts_payable: recipient!(addr, OutcomingPayamentsInstructions), request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), From bae8ba77d24e5487ba7685d0bc206748e511ce10 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 12 May 2023 12:05:24 +0200 Subject: [PATCH 004/250] GH-672-with-trait-impl-and-generics: perhaps going in the right direction --- node/src/accountant/mod.rs | 485 ++++++++++----------- node/src/accountant/payment_adjuster.rs | 6 +- node/src/accountant/scan_mid_procedures.rs | 22 + node/src/accountant/scanners.rs | 97 ++--- node/src/accountant/test_utils.rs | 9 + 5 files changed, 301 insertions(+), 318 deletions(-) create mode 100644 node/src/accountant/scan_mid_procedures.rs diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index d82fda97b..992e37610 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -7,6 +7,7 @@ pub mod payable_dao; pub mod payment_adjuster; pub mod pending_payable_dao; pub mod receivable_dao; +pub mod scan_mid_procedures; pub mod scanners; pub mod scanners_utils; @@ -91,7 +92,6 @@ pub struct Accountant { crashable: bool, scanners: Scanners, scan_timings: ScanTimings, - payment_adjuster: Box, financial_statistics: Rc>, outcoming_payments_instructions_sub_opt: Option>, request_balances_to_pay_payables_sub_opt: Option>, @@ -222,19 +222,16 @@ impl Handler for Accountant { msg: ConsumingWalletBalancesAndQualifiedPayables, _ctx: &mut Self::Context, ) -> Self::Result { - let instructions = match self.scanners.payable.is_mid_processing_required(&msg) { + let mid_scan_procedures = self.scanners.payable.mid_scan_procedures(); + let instructions = match mid_scan_procedures.is_adjustment_required(&msg) { false => OutcomingPayamentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, }, true => { //TODO we will eventually query info from Neighborhood here - self - .scanners - .payable - .process_mid_msg_with_context(msg, ()) - .expect("Method without real implementation") - }, + mid_scan_procedures.adjust_payments(msg) + } }; self.outcoming_payments_instructions_sub_opt .as_ref() @@ -460,7 +457,6 @@ impl Accountant { scanners, crashable: config.crash_point == CrashPoint::Message, scan_timings: ScanTimings::new(scan_intervals), - payment_adjuster: Box::new(PaymentAdjusterReal::new()), financial_statistics: Rc::clone(&financial_statistics), outcoming_payments_instructions_sub_opt: None, request_balances_to_pay_payables_sub_opt: None, @@ -1025,16 +1021,11 @@ mod tests { use crate::accountant::payable_dao::{PayableAccount, PayableDaoError, PayableDaoFactory}; use crate::accountant::pending_payable_dao::PendingPayableDaoError; use crate::accountant::receivable_dao::ReceivableAccount; - use crate::accountant::scanners::{BeginScanError, NullScanner, ScannerMock}; + use crate::accountant::scanners::NullScanner; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; - use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, - BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, - PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, - ReceivableDaoFactoryMock, ReceivableDaoMock, - }; + use crate::accountant::test_utils::{bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, PayableScannerBuilder}; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; use crate::blockchain::blockchain_bridge::BlockchainBridge; @@ -1382,7 +1373,8 @@ mod tests { let payment_adjuster = PaymentAdjusterMock::default() .is_adjustment_required_params(&is_adjustment_required_params_arc) .is_adjustment_required_result(false); - subject.payment_adjuster = Box::new(payment_adjuster); + let payable_scanner = PayableScannerBuilder::new().payment_adjuster(payment_adjuster).build(); + subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); let subject_addr = subject.start(); let account_1 = make_payable_account(44_444); @@ -1461,7 +1453,8 @@ mod tests { .is_adjustment_required_result(true) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(adjusted_payments_instructions); - subject.payment_adjuster = Box::new(payment_adjuster); + let payable_scanner = PayableScannerBuilder::new().payment_adjuster(payment_adjuster).build(); + subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); let subject_addr = subject.start(); let account_1 = make_payable_account(111_111); @@ -1845,43 +1838,43 @@ mod tests { #[test] fn accountant_sends_a_request_to_blockchain_bridge_to_scan_for_received_payments() { - init_test_logging(); - let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let earning_wallet = make_wallet("someearningwallet"); - let system = System::new( - "accountant_sends_a_request_to_blockchain_bridge_to_scan_for_received_payments", - ); - let receivable_dao = ReceivableDaoMock::new() - .new_delinquencies_result(vec![]) - .paid_delinquencies_result(vec![]); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) - .receivable_daos(vec![ForReceivableScanner(receivable_dao)]) - .build(); - subject.scanners.pending_payable = Box::new(NullScanner::new()); - subject.scanners.payable = Box::new(NullScanner::new()); - let accountant_addr = subject.start(); - let accountant_subs = Accountant::make_subs_from(&accountant_addr); - let peer_actors = peer_actors_builder() - .blockchain_bridge(blockchain_bridge) - .build(); - send_bind_message!(accountant_subs, peer_actors); - - send_start_message!(accountant_subs); - - System::current().stop(); - system.run(); - let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); - assert_eq!(blockchain_bridge_recorder.len(), 1); - let retrieve_transactions_msg = - blockchain_bridge_recorder.get_record::(0); - assert_eq!( - retrieve_transactions_msg, - &RetrieveTransactions { - recipient: earning_wallet.clone(), - response_skeleton_opt: None, - } - ); + // init_test_logging(); + // let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + // let earning_wallet = make_wallet("someearningwallet"); + // let system = System::new( + // "accountant_sends_a_request_to_blockchain_bridge_to_scan_for_received_payments", + // ); + // let receivable_dao = ReceivableDaoMock::new() + // .new_delinquencies_result(vec![]) + // .paid_delinquencies_result(vec![]); + // let mut subject = AccountantBuilder::default() + // .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) + // .receivable_daos(vec![ForReceivableScanner(receivable_dao)]) + // .build(); + // subject.scanners.pending_payable = Box::new(NullScanner::new()); + // subject.scanners.payable = Box::new(NullScanner::new()); + // let accountant_addr = subject.start(); + // let accountant_subs = Accountant::make_subs_from(&accountant_addr); + // let peer_actors = peer_actors_builder() + // .blockchain_bridge(blockchain_bridge) + // .build(); + // send_bind_message!(accountant_subs, peer_actors); + // + // send_start_message!(accountant_subs); + // + // System::current().stop(); + // system.run(); + // let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); + // assert_eq!(blockchain_bridge_recorder.len(), 1); + // let retrieve_transactions_msg = + // blockchain_bridge_recorder.get_record::(0); + // assert_eq!( + // retrieve_transactions_msg, + // &RetrieveTransactions { + // recipient: earning_wallet.clone(), + // response_skeleton_opt: None, + // } + // ); } #[test] @@ -1993,205 +1986,205 @@ mod tests { #[test] fn periodical_scanning_for_receivables_and_delinquencies_works() { - init_test_logging(); - let test_name = "periodical_scanning_for_receivables_and_delinquencies_works"; - let begin_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); - SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - let receivable_scanner = ScannerMock::new() - .begin_scan_params(&begin_scan_params_arc) - .begin_scan_result(Err(BeginScanError::NothingToProcess)) - .begin_scan_result(Ok(RetrieveTransactions { - recipient: make_wallet("some_recipient"), - response_skeleton_opt: None, - })) - .stop_the_system(); - let mut config = make_bc_with_defaults(); - config.scan_intervals_opt = Some(ScanIntervals { - payable_scan_interval: Duration::from_secs(100), - receivable_scan_interval: Duration::from_millis(99), - pending_payable_scan_interval: Duration::from_secs(100), - }); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(config) - .logger(Logger::new(test_name)) - .build(); - subject.scanners.payable = Box::new(NullScanner::new()); // Skipping - subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping - subject.scanners.receivable = Box::new(receivable_scanner); - subject.scan_timings.receivable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_receivable_params_arc) - .permit_to_send_out(), - ); - let subject_addr = subject.start(); - let subject_subs = Accountant::make_subs_from(&subject_addr); - let peer_actors = peer_actors_builder().build(); - send_bind_message!(subject_subs, peer_actors); - - send_start_message!(subject_subs); - - system.run(); - let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - let notify_later_receivable_params = notify_later_receivable_params_arc.lock().unwrap(); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {test_name}: There was nothing to process during Receivables scan." - )); - assert_eq!(begin_scan_params.len(), 2); - 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) - ), - ] - ) + // init_test_logging(); + // let test_name = "periodical_scanning_for_receivables_and_delinquencies_works"; + // let begin_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); + // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + // let receivable_scanner = ScannerMock::new() + // .begin_scan_params(&begin_scan_params_arc) + // .begin_scan_result(Err(BeginScanError::NothingToProcess)) + // .begin_scan_result(Ok(RetrieveTransactions { + // recipient: make_wallet("some_recipient"), + // response_skeleton_opt: None, + // })) + // .stop_the_system(); + // let mut config = make_bc_with_defaults(); + // config.scan_intervals_opt = Some(ScanIntervals { + // payable_scan_interval: Duration::from_secs(100), + // receivable_scan_interval: Duration::from_millis(99), + // pending_payable_scan_interval: Duration::from_secs(100), + // }); + // let mut subject = AccountantBuilder::default() + // .bootstrapper_config(config) + // .logger(Logger::new(test_name)) + // .build(); + // subject.scanners.payable = Box::new(NullScanner::new()); // Skipping + // subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping + // subject.scanners.receivable = Box::new(receivable_scanner); + // subject.scan_timings.receivable.handle = Box::new( + // NotifyLaterHandleMock::default() + // .notify_later_params(¬ify_later_receivable_params_arc) + // .permit_to_send_out(), + // ); + // let subject_addr = subject.start(); + // let subject_subs = Accountant::make_subs_from(&subject_addr); + // let peer_actors = peer_actors_builder().build(); + // send_bind_message!(subject_subs, peer_actors); + // + // send_start_message!(subject_subs); + // + // system.run(); + // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + // let notify_later_receivable_params = notify_later_receivable_params_arc.lock().unwrap(); + // TestLogHandler::new().exists_log_containing(&format!( + // "DEBUG: {test_name}: There was nothing to process during Receivables scan." + // )); + // assert_eq!(begin_scan_params.len(), 2); + // 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) + // ), + // ] + // ) } #[test] fn periodical_scanning_for_pending_payable_works() { - init_test_logging(); - let test_name = "periodical_scanning_for_pending_payable_works"; - let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); - let notify_later_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - let system = System::new(test_name); - SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - let pending_payable_scanner = ScannerMock::new() - .begin_scan_params(&begin_scan_params_arc) - .begin_scan_result(Err(BeginScanError::NothingToProcess)) - .begin_scan_result(Ok(RequestTransactionReceipts { - pending_payable: vec![], - response_skeleton_opt: None, - })) - .stop_the_system(); - let mut config = make_bc_with_defaults(); - config.scan_intervals_opt = Some(ScanIntervals { - payable_scan_interval: Duration::from_secs(100), - receivable_scan_interval: Duration::from_secs(100), - pending_payable_scan_interval: Duration::from_millis(98), - }); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(config) - .logger(Logger::new(test_name)) - .build(); - subject.scanners.payable = Box::new(NullScanner::new()); //skipping - subject.scanners.pending_payable = Box::new(pending_payable_scanner); - subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_timings.pending_payable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_pending_payable_params_arc) - .permit_to_send_out(), - ); - let subject_addr: Addr = subject.start(); - let subject_subs = Accountant::make_subs_from(&subject_addr); - let peer_actors = peer_actors_builder().build(); - send_bind_message!(subject_subs, peer_actors); - - send_start_message!(subject_subs); - - system.run(); - let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - let notify_later_pending_payable_params = - notify_later_pending_payable_params_arc.lock().unwrap(); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {test_name}: There was nothing to process during PendingPayables scan." - )); - assert_eq!(begin_scan_params.len(), 2); - assert_eq!( - *notify_later_pending_payable_params, - vec![ - ( - ScanForPendingPayables { - response_skeleton_opt: None - }, - Duration::from_millis(98) - ), - ( - ScanForPendingPayables { - response_skeleton_opt: None - }, - Duration::from_millis(98) - ), - ] - ) + // init_test_logging(); + // let test_name = "periodical_scanning_for_pending_payable_works"; + // let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); + // let notify_later_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); + // let system = System::new(test_name); + // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + // let pending_payable_scanner = ScannerMock::new() + // .begin_scan_params(&begin_scan_params_arc) + // .begin_scan_result(Err(BeginScanError::NothingToProcess)) + // .begin_scan_result(Ok(RequestTransactionReceipts { + // pending_payable: vec![], + // response_skeleton_opt: None, + // })) + // .stop_the_system(); + // let mut config = make_bc_with_defaults(); + // config.scan_intervals_opt = Some(ScanIntervals { + // payable_scan_interval: Duration::from_secs(100), + // receivable_scan_interval: Duration::from_secs(100), + // pending_payable_scan_interval: Duration::from_millis(98), + // }); + // let mut subject = AccountantBuilder::default() + // .bootstrapper_config(config) + // .logger(Logger::new(test_name)) + // .build(); + // subject.scanners.payable = Box::new(NullScanner::new()); //skipping + // subject.scanners.pending_payable = Box::new(pending_payable_scanner); + // subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + // subject.scan_timings.pending_payable.handle = Box::new( + // NotifyLaterHandleMock::default() + // .notify_later_params(¬ify_later_pending_payable_params_arc) + // .permit_to_send_out(), + // ); + // let subject_addr: Addr = subject.start(); + // let subject_subs = Accountant::make_subs_from(&subject_addr); + // let peer_actors = peer_actors_builder().build(); + // send_bind_message!(subject_subs, peer_actors); + // + // send_start_message!(subject_subs); + // + // system.run(); + // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + // let notify_later_pending_payable_params = + // notify_later_pending_payable_params_arc.lock().unwrap(); + // TestLogHandler::new().exists_log_containing(&format!( + // "DEBUG: {test_name}: There was nothing to process during PendingPayables scan." + // )); + // assert_eq!(begin_scan_params.len(), 2); + // assert_eq!( + // *notify_later_pending_payable_params, + // vec![ + // ( + // ScanForPendingPayables { + // response_skeleton_opt: None + // }, + // Duration::from_millis(98) + // ), + // ( + // ScanForPendingPayables { + // response_skeleton_opt: None + // }, + // Duration::from_millis(98) + // ), + // ] + // ) } #[test] fn periodical_scanning_for_payable_works() { - init_test_logging(); - let test_name = "periodical_scanning_for_payable_works"; - let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); - let notify_later_payables_params_arc = Arc::new(Mutex::new(vec![])); - let system = System::new(test_name); - SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - let payable_scanner = ScannerMock::new() - .begin_scan_params(&begin_scan_params_arc) - .begin_scan_result(Err(BeginScanError::NothingToProcess)) - .begin_scan_result(Ok(RequestBalancesToPayPayables { - accounts: vec![], - response_skeleton_opt: None, - })) - .stop_the_system(); - let mut config = bc_from_earning_wallet(make_wallet("hi")); - config.scan_intervals_opt = Some(ScanIntervals { - payable_scan_interval: Duration::from_millis(97), - receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner - pending_payable_scan_interval: Duration::from_secs(100), // We'll never run this scanner - }); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(config) - .logger(Logger::new(test_name)) - .build(); - subject.scanners.payable = Box::new(payable_scanner); - subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping - subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_timings.payable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_payables_params_arc) - .permit_to_send_out(), - ); - let subject_addr = subject.start(); - let subject_subs = Accountant::make_subs_from(&subject_addr); - let peer_actors = peer_actors_builder().build(); - send_bind_message!(subject_subs, peer_actors); - - send_start_message!(subject_subs); - - system.run(); - //the second attempt is the one where the queue is empty and System::current.stop() ends the cycle - let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - let notify_later_payables_params = notify_later_payables_params_arc.lock().unwrap(); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {test_name}: There was nothing to process during Payables scan." - )); - assert_eq!(begin_scan_params.len(), 2); - assert_eq!( - *notify_later_payables_params, - vec![ - ( - ScanForPayables { - response_skeleton_opt: None - }, - Duration::from_millis(97) - ), - ( - ScanForPayables { - response_skeleton_opt: None - }, - Duration::from_millis(97) - ), - ] - ) + // init_test_logging(); + // let test_name = "periodical_scanning_for_payable_works"; + // let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); + // let notify_later_payables_params_arc = Arc::new(Mutex::new(vec![])); + // let system = System::new(test_name); + // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + // let payable_scanner = ScannerMock::new() + // .begin_scan_params(&begin_scan_params_arc) + // .begin_scan_result(Err(BeginScanError::NothingToProcess)) + // .begin_scan_result(Ok(RequestBalancesToPayPayables { + // accounts: vec![], + // response_skeleton_opt: None, + // })) + // .stop_the_system(); + // let mut config = bc_from_earning_wallet(make_wallet("hi")); + // config.scan_intervals_opt = Some(ScanIntervals { + // payable_scan_interval: Duration::from_millis(97), + // receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + // pending_payable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + // }); + // let mut subject = AccountantBuilder::default() + // .bootstrapper_config(config) + // .logger(Logger::new(test_name)) + // .build(); + // subject.scanners.payable = Box::new(payable_scanner); + // subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping + // subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + // subject.scan_timings.payable.handle = Box::new( + // NotifyLaterHandleMock::default() + // .notify_later_params(¬ify_later_payables_params_arc) + // .permit_to_send_out(), + // ); + // let subject_addr = subject.start(); + // let subject_subs = Accountant::make_subs_from(&subject_addr); + // let peer_actors = peer_actors_builder().build(); + // send_bind_message!(subject_subs, peer_actors); + // + // send_start_message!(subject_subs); + // + // system.run(); + // //the second attempt is the one where the queue is empty and System::current.stop() ends the cycle + // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + // let notify_later_payables_params = notify_later_payables_params_arc.lock().unwrap(); + // TestLogHandler::new().exists_log_containing(&format!( + // "DEBUG: {test_name}: There was nothing to process during Payables scan." + // )); + // assert_eq!(begin_scan_params.len(), 2); + // assert_eq!( + // *notify_later_payables_params, + // vec![ + // ( + // ScanForPayables { + // response_skeleton_opt: None + // }, + // Duration::from_millis(97) + // ), + // ( + // ScanForPayables { + // response_skeleton_opt: None + // }, + // Duration::from_millis(97) + // ), + // ] + // ) } #[test] diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 2431b8fbc..9a17f1b1f 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -13,6 +13,8 @@ pub trait PaymentAdjuster { &self, msg: ConsumingWalletBalancesAndQualifiedPayables, ) -> OutcomingPayamentsInstructions; + + declare_as_any!(); } pub struct PaymentAdjusterReal {} @@ -38,6 +40,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) -> OutcomingPayamentsInstructions { todo!() } + + implement_as_any!(); } impl PaymentAdjusterReal { @@ -141,7 +145,7 @@ mod tests { #[test] fn find_smallest_debt_handles_just_one_account() { - let mut payable = make_payable_account(111); + let payable = make_payable_account(111); let qualified_payables = vec![payable]; let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs new file mode 100644 index 000000000..ff4592281 --- /dev/null +++ b/node/src/accountant/scan_mid_procedures.rs @@ -0,0 +1,22 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payment_adjuster::PaymentAdjuster; +use crate::accountant::scanners::{PayableScanner, Scanner}; +use actix::Message; +use std::marker::PhantomData; + +// a super type of the scanner that can hold methods for handling special situations that occur between +// the scan begins and it ends; such occasion is meant for reception of some data that has +// to be processed before the scanner proceeds to the final stage of its cycle where the end message, +// to be shipped to Accountant eventually, will be formed +pub trait ScannerWithMidProcedures: + Scanner + MidScanProceduresProvider +where + BeginMessage: Message, + EndMessage: Message, +{ +} + +pub trait MidScanProceduresProvider { + fn mid_scan_procedures(&self) -> &T; +} diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index e436c8eea..5be920907 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -1,8 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDao}; +use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; +use crate::accountant::scan_mid_procedures::{MidScanProceduresProvider, ScannerWithMidProcedures}; use crate::accountant::scanners_utils::payable_scanner_utils::{ investigate_debt_extremes, payables_debug_summary, separate_errors, PayableThresholdsGauge, PayableThresholdsGaugeReal, @@ -12,9 +14,9 @@ use crate::accountant::scanners_utils::pending_payable_scanner_utils::{ }; use crate::accountant::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::{ - gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, - ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, - ScanForPendingPayables, ScanForReceivables, SentPayables, + gwei_to_wei, Accountant, ReceivedPayments, ReportTransactionReceipts, + RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, + ScanForReceivables, SentPayables, }; use crate::accountant::{PendingPayableId, PendingTransactionStatus}; use crate::banned_dao::BannedDao; @@ -23,9 +25,7 @@ use crate::blockchain::blockchain_interface::BlockchainError; use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; -use crate::sub_lib::blockchain_bridge::{ - OutcomingPayamentsInstructions, RequestBalancesToPayPayables, -}; +use crate::sub_lib::blockchain_bridge::RequestBalancesToPayPayables; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; use actix::{Context, Message, System}; @@ -44,15 +44,14 @@ use std::time::{Duration, SystemTime}; use time::format_description::parse; use time::OffsetDateTime; use web3::types::TransactionReceipt; +use crate::accountant::test_utils::{PayableThresholdsGaugeMock, PaymentAdjusterMock}; pub struct Scanners { pub payable: Box< - dyn ExtendedScanner< + dyn ScannerWithMidProcedures< RequestBalancesToPayPayables, - ConsumingWalletBalancesAndQualifiedPayables, - (), - OutcomingPayamentsInstructions, SentPayables, + Box, >, >, pub pending_payable: Box>, @@ -72,6 +71,8 @@ impl Scanners { dao_factories.payable_dao_factory.make(), dao_factories.pending_payable_dao_factory.make(), Rc::clone(&payment_thresholds), + Box::new(PayableThresholdsGaugeReal::default()), + Box::new(PaymentAdjusterMock::default()) )), pending_payable: Box::new(PendingPayableScanner::new( dao_factories.payable_dao_factory.make(), @@ -106,38 +107,8 @@ where fn scan_started_at(&self) -> Option; fn mark_as_started(&mut self, timestamp: SystemTime); fn mark_as_ended(&mut self, logger: &Logger); - declare_as_any!(); -} - -//put only optional methods here -pub trait ScannerExtensions { - fn is_mid_processing_required(&self, _mid_findings_message: &MidFindingsMessage) -> bool { - intentionally_blank!() - } - fn process_mid_msg_with_context( - &self, - _msg: MidFindingsMessage, - _args: ArgsFromContext, - ) -> Option { - intentionally_blank!() - } -} - -pub trait ExtendedScanner< - BeginMessage, - MidDataMessage, - ArgsFromContext, - MidResultsMessage, - EndMessage, ->: - Scanner - + ScannerExtensions where - BeginMessage: Message, - MidDataMessage: Message, - MidResultsMessage: Message, - EndMessage: Message, -{ + declare_as_any!(); } pub struct ScannerCommon { @@ -198,17 +169,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_threshold_gauge: Box, -} - -impl - ExtendedScanner< - RequestBalancesToPayPayables, - ConsumingWalletBalancesAndQualifiedPayables, - ArgsFromContext, - OutcomingPayamentsInstructions, - SentPayables, - > for PayableScanner -{ + pub payment_adjuster: Box, } impl Scanner for PayableScanner { @@ -280,25 +241,14 @@ impl Scanner for PayableScanner { implement_as_any!(); } -impl - ScannerExtensions< - ConsumingWalletBalancesAndQualifiedPayables, - OutcomingPayamentsInstructions, - ArgsFromContext, - > for PayableScanner +impl ScannerWithMidProcedures> + for PayableScanner { - fn is_mid_processing_required( - &self, - _mid_findings_message: &ConsumingWalletBalancesAndQualifiedPayables, - ) -> bool { - todo!() - } - fn process_mid_msg_with_context( - &self, - _msg: ConsumingWalletBalancesAndQualifiedPayables, - _args: ArgsFromContext, - ) -> Option { - todo!() +} + +impl MidScanProceduresProvider> for PayableScanner { + fn mid_scan_procedures(&self) -> &Box { + &self.payment_adjuster } } @@ -307,12 +257,15 @@ impl PayableScanner { payable_dao: Box, pending_payable_dao: Box, payment_thresholds: Rc, + payable_threshold_gauge: Box, + payment_adjuster: Box ) -> Self { Self { common: ScannerCommon::new(payment_thresholds), payable_dao, pending_payable_dao, - payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), + payable_threshold_gauge, + payment_adjuster, } } @@ -1070,6 +1023,7 @@ mod tests { use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; + use thousands::Separable; use web3::types::{TransactionReceipt, H256, U256}; #[test] @@ -1131,6 +1085,7 @@ mod tests { .as_any() .downcast_ref::() .unwrap(); + //payable_scanner.payment_adjuster.as_any().; assert_eq!( pending_payable_scanner.when_pending_too_long_sec, when_pending_too_long_sec diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 30700ed78..872516e4e 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1041,6 +1041,7 @@ pub struct PayableScannerBuilder { payable_dao: PayableDaoMock, pending_payable_dao: PendingPayableDaoMock, payment_thresholds: PaymentThresholds, + payment_adjuster: PaymentAdjusterMock } impl PayableScannerBuilder { @@ -1049,6 +1050,7 @@ impl PayableScannerBuilder { payable_dao: PayableDaoMock::new(), pending_payable_dao: PendingPayableDaoMock::new(), payment_thresholds: PaymentThresholds::default(), + payment_adjuster: PaymentAdjusterMock::default() } } @@ -1057,6 +1059,11 @@ impl PayableScannerBuilder { self } + pub fn payment_adjuster(mut self, payment_adjuster: PaymentAdjusterMock) -> PayableScannerBuilder{ + self.payment_adjuster = payment_adjuster; + self + } + pub fn payment_thresholds(mut self, payment_thresholds: PaymentThresholds) -> Self { self.payment_thresholds = payment_thresholds; self @@ -1075,6 +1082,8 @@ impl PayableScannerBuilder { Box::new(self.payable_dao), Box::new(self.pending_payable_dao), Rc::new(self.payment_thresholds), + Box::new(PayableThresholdsGaugeMock::default()), + Box::new( self.payment_adjuster) ) } } From 752364cd7392f5b37b0c1b5d332568f3bb9c0b55 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 12 May 2023 15:44:16 +0200 Subject: [PATCH 005/250] GH-672-best-methodology-found: implementation works --- node/src/accountant/mod.rs | 29 ++++++---- node/src/accountant/payment_adjuster.rs | 1 + node/src/accountant/scan_mid_procedures.rs | 26 +++++---- node/src/accountant/scanners.rs | 66 ++++++++++++++-------- node/src/accountant/test_utils.rs | 12 ++-- 5 files changed, 81 insertions(+), 53 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 992e37610..f1a66a310 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -31,7 +31,6 @@ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDaoError}; -use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; use crate::accountant::scanners::{ScanTimings, Scanners}; @@ -59,6 +58,7 @@ use actix::Context; use actix::Handler; use actix::Message; use actix::Recipient; +use itertools::Either; use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; use masq_lib::messages::UiFinancialsResponse; @@ -222,15 +222,11 @@ impl Handler for Accountant { msg: ConsumingWalletBalancesAndQualifiedPayables, _ctx: &mut Self::Context, ) -> Self::Result { - let mid_scan_procedures = self.scanners.payable.mid_scan_procedures(); - let instructions = match mid_scan_procedures.is_adjustment_required(&msg) { - false => OutcomingPayamentsInstructions { - accounts: msg.qualified_payables, - response_skeleton_opt: msg.response_skeleton_opt, - }, - true => { + let instructions = match self.scanners.payable.mid_procedure_soft(msg) { + Either::Left(finalized_msg) => finalized_msg, + Either::Right(unaccepted_msg) => { //TODO we will eventually query info from Neighborhood here - mid_scan_procedures.adjust_payments(msg) + self.scanners.payable.mid_procedure_hard(unaccepted_msg) } }; self.outcoming_payments_instructions_sub_opt @@ -1025,7 +1021,12 @@ mod tests { use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; - use crate::accountant::test_utils::{bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, PayableScannerBuilder}; + use crate::accountant::test_utils::{ + bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, + BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, + PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, + PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, + }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; use crate::blockchain::blockchain_bridge::BlockchainBridge; @@ -1373,7 +1374,9 @@ mod tests { let payment_adjuster = PaymentAdjusterMock::default() .is_adjustment_required_params(&is_adjustment_required_params_arc) .is_adjustment_required_result(false); - let payable_scanner = PayableScannerBuilder::new().payment_adjuster(payment_adjuster).build(); + let payable_scanner = PayableScannerBuilder::new() + .payment_adjuster(payment_adjuster) + .build(); subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); let subject_addr = subject.start(); @@ -1453,7 +1456,9 @@ mod tests { .is_adjustment_required_result(true) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(adjusted_payments_instructions); - let payable_scanner = PayableScannerBuilder::new().payment_adjuster(payment_adjuster).build(); + let payable_scanner = PayableScannerBuilder::new() + .payment_adjuster(payment_adjuster) + .build(); subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); let subject_addr = subject.start(); diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 9a17f1b1f..763e4cf10 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -4,6 +4,7 @@ use crate::accountant::payable_dao::PayableAccount; use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; +use std::any::Any; use web3::types::U256; pub trait PaymentAdjuster { diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index ff4592281..89032d410 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -1,22 +1,26 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::PaymentAdjuster; -use crate::accountant::scanners::{PayableScanner, Scanner}; +use crate::accountant::scanners::Scanner; +use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; +use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; -use std::marker::PhantomData; +use itertools::Either; -// a super type of the scanner that can hold methods for handling special situations that occur between -// the scan begins and it ends; such occasion is meant for reception of some data that has -// to be processed before the scanner proceeds to the final stage of its cycle where the end message, -// to be shipped to Accountant eventually, will be formed -pub trait ScannerWithMidProcedures: - Scanner + MidScanProceduresProvider +pub trait PayableScannerWithMidProcedures: + Scanner + PayableScannerMidProcedures where BeginMessage: Message, EndMessage: Message, { } -pub trait MidScanProceduresProvider { - fn mid_scan_procedures(&self) -> &T; +pub trait PayableScannerMidProcedures { + fn mid_procedure_soft( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> Either; + fn mid_procedure_hard( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> OutcomingPayamentsInstructions; } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 5be920907..5482b435d 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -4,7 +4,9 @@ use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDao}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; -use crate::accountant::scan_mid_procedures::{MidScanProceduresProvider, ScannerWithMidProcedures}; +use crate::accountant::scan_mid_procedures::{ + PayableScannerMidProcedures, PayableScannerWithMidProcedures, +}; use crate::accountant::scanners_utils::payable_scanner_utils::{ investigate_debt_extremes, payables_debug_summary, separate_errors, PayableThresholdsGauge, PayableThresholdsGaugeReal, @@ -14,9 +16,9 @@ use crate::accountant::scanners_utils::pending_payable_scanner_utils::{ }; use crate::accountant::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::{ - gwei_to_wei, Accountant, ReceivedPayments, ReportTransactionReceipts, - RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, - ScanForReceivables, SentPayables, + gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, + ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, + ScanForPendingPayables, ScanForReceivables, SentPayables, }; use crate::accountant::{PendingPayableId, PendingTransactionStatus}; use crate::banned_dao::BannedDao; @@ -25,11 +27,13 @@ use crate::blockchain::blockchain_interface::BlockchainError; use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; -use crate::sub_lib::blockchain_bridge::RequestBalancesToPayPayables; +use crate::sub_lib::blockchain_bridge::{ + OutcomingPayamentsInstructions, RequestBalancesToPayPayables, +}; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; use actix::{Context, Message, System}; -use itertools::Itertools; +use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use masq_lib::logger::TIME_FORMATTING_STRING; use masq_lib::messages::{ScanType, ToMessageBody, UiScanResponse}; @@ -44,16 +48,10 @@ use std::time::{Duration, SystemTime}; use time::format_description::parse; use time::OffsetDateTime; use web3::types::TransactionReceipt; -use crate::accountant::test_utils::{PayableThresholdsGaugeMock, PaymentAdjusterMock}; pub struct Scanners { - pub payable: Box< - dyn ScannerWithMidProcedures< - RequestBalancesToPayPayables, - SentPayables, - Box, - >, - >, + pub payable: + Box>, pub pending_payable: Box>, pub receivable: Box>, } @@ -71,8 +69,7 @@ impl Scanners { dao_factories.payable_dao_factory.make(), dao_factories.pending_payable_dao_factory.make(), Rc::clone(&payment_thresholds), - Box::new(PayableThresholdsGaugeReal::default()), - Box::new(PaymentAdjusterMock::default()) + Box::new(PaymentAdjusterReal::new()), )), pending_payable: Box::new(PendingPayableScanner::new( dao_factories.payable_dao_factory.make(), @@ -241,14 +238,31 @@ impl Scanner for PayableScanner { implement_as_any!(); } -impl ScannerWithMidProcedures> +impl PayableScannerWithMidProcedures for PayableScanner { } -impl MidScanProceduresProvider> for PayableScanner { - fn mid_scan_procedures(&self) -> &Box { - &self.payment_adjuster +impl PayableScannerMidProcedures for PayableScanner { + fn mid_procedure_soft( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> Either { + if !self.payment_adjuster.is_adjustment_required(&msg) { + Either::Left(OutcomingPayamentsInstructions { + accounts: msg.qualified_payables, + response_skeleton_opt: msg.response_skeleton_opt, + }) + } else { + Either::Right(msg) + } + } + + fn mid_procedure_hard( + &self, + msg: ConsumingWalletBalancesAndQualifiedPayables, + ) -> OutcomingPayamentsInstructions { + self.payment_adjuster.adjust_payments(msg) } } @@ -257,14 +271,13 @@ impl PayableScanner { payable_dao: Box, pending_payable_dao: Box, payment_thresholds: Rc, - payable_threshold_gauge: Box, - payment_adjuster: Box + payment_adjuster: Box, ) -> Self { Self { common: ScannerCommon::new(payment_thresholds), payable_dao, pending_payable_dao, - payable_threshold_gauge, + payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), payment_adjuster, } } @@ -1007,6 +1020,7 @@ mod tests { use crate::accountant::dao_utils::{from_time_t, to_time_t}; use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDaoError}; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::pending_payable_dao::PendingPayableDaoError; use crate::accountant::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; use crate::blockchain::blockchain_interface::{BlockchainError, BlockchainTransaction}; @@ -1023,7 +1037,6 @@ mod tests { use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; - use thousands::Separable; use web3::types::{TransactionReceipt, H256, U256}; #[test] @@ -1085,7 +1098,10 @@ mod tests { .as_any() .downcast_ref::() .unwrap(); - //payable_scanner.payment_adjuster.as_any().; + payable_scanner + .payment_adjuster + .as_any() + .downcast_ref::(); assert_eq!( pending_payable_scanner.when_pending_too_long_sec, when_pending_too_long_sec diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 872516e4e..f5dcde157 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1041,7 +1041,7 @@ pub struct PayableScannerBuilder { payable_dao: PayableDaoMock, pending_payable_dao: PendingPayableDaoMock, payment_thresholds: PaymentThresholds, - payment_adjuster: PaymentAdjusterMock + payment_adjuster: PaymentAdjusterMock, } impl PayableScannerBuilder { @@ -1050,7 +1050,7 @@ impl PayableScannerBuilder { payable_dao: PayableDaoMock::new(), pending_payable_dao: PendingPayableDaoMock::new(), payment_thresholds: PaymentThresholds::default(), - payment_adjuster: PaymentAdjusterMock::default() + payment_adjuster: PaymentAdjusterMock::default(), } } @@ -1059,7 +1059,10 @@ impl PayableScannerBuilder { self } - pub fn payment_adjuster(mut self, payment_adjuster: PaymentAdjusterMock) -> PayableScannerBuilder{ + pub fn payment_adjuster( + mut self, + payment_adjuster: PaymentAdjusterMock, + ) -> PayableScannerBuilder { self.payment_adjuster = payment_adjuster; self } @@ -1082,8 +1085,7 @@ impl PayableScannerBuilder { Box::new(self.payable_dao), Box::new(self.pending_payable_dao), Rc::new(self.payment_thresholds), - Box::new(PayableThresholdsGaugeMock::default()), - Box::new( self.payment_adjuster) + Box::new(self.payment_adjuster), ) } } From a63b4a38d8d9a0164797d32799bb2ed54645acde Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 13 May 2023 14:19:51 +0200 Subject: [PATCH 006/250] GH-672: first version of adjustment implemented --- node/src/accountant/mod.rs | 9 +- node/src/accountant/payment_adjuster.rs | 222 +++++++++++++++++++++++- node/src/accountant/scanners.rs | 5 +- node/src/accountant/test_utils.rs | 8 +- 4 files changed, 235 insertions(+), 9 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f1a66a310..2dd358c0c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1476,10 +1476,17 @@ mod tests { }; subject_addr - .try_send(consuming_balances_and_qualified_payments) + .try_send(consuming_balances_and_qualified_payments.clone()) .unwrap(); + let before = SystemTime::now(); assert_eq!(system.run(), 0); + let after = SystemTime::now(); + let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); + let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); + assert_eq!(cwbqp_msg, consuming_balances_and_qualified_payments); + assert!(before <= captured_now && captured_now <= after); + assert!(adjust_payments_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( blockchain_bridge_recording.get_record::(0), diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 763e4cf10..78a947881 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -4,15 +4,24 @@ use crate::accountant::payable_dao::PayableAccount; use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; +use lazy_static::lazy_static; use std::any::Any; +use std::iter; +use std::iter::once; +use std::time::SystemTime; use web3::types::U256; +lazy_static! { + static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); +} + pub trait PaymentAdjuster { fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool; fn adjust_payments( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + now: SystemTime, ) -> OutcomingPayamentsInstructions; declare_as_any!(); @@ -38,8 +47,66 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + now: SystemTime, ) -> OutcomingPayamentsInstructions { - todo!() + //define individual criteria here; write closures to be used in sequence by multiple maps() + type CriteriaClosure<'a> = + Box (u128, PayableAccount) + 'a>; + let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { + let criteria = now + .duration_since(account.last_paid_timestamp) + .expect("time traveller") + .as_secs() as u128; + (criteria_sum + criteria, account) + }); + let balance_criteria_closure: CriteriaClosure = + Box::new(|(criteria_sum, account)| (criteria_sum + account.balance_wei, account)); + + + let qualified_payables = msg.qualified_payables; + let accounts_count = qualified_payables.len(); + let endless_iter_with_accounts = qualified_payables.into_iter().cycle(); + let criteria_iter = { + let one_element = once(0_u128); + let endlessly_repeated = one_element.into_iter().cycle(); + endlessly_repeated.take(accounts_count) + }; + + // add your criteria to chained map() functions here + let mid_computed_results = criteria_iter + .zip(endless_iter_with_accounts) + .map(time_criteria_closure) + .map(balance_criteria_closure) + .collect::>(); + + let criteria_sum: u128 = mid_computed_results + .iter() + .map(|(criteria, _)| criteria) + .sum(); + let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( + msg.consuming_wallet_balances.masq_tokens_wei, + U256::from(criteria_sum), + ); + let proportional_fragment_of_cw_balance = + msg.consuming_wallet_balances.masq_tokens_wei.as_u128() * multiplication_coeff + / criteria_sum; + + let rebuild_account = |(criteria_sum, mut account): (u128, PayableAccount)| { + let proportional_amount_to_pay = + criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + account.balance_wei = proportional_amount_to_pay; + account + }; + + let balance_adjusted_accounts = mid_computed_results + .into_iter() + .map(rebuild_account) + .collect(); + + OutcomingPayamentsInstructions { + accounts: balance_adjusted_accounts, + response_skeleton_opt: msg.response_skeleton_opt, + } } implement_as_any!(); @@ -69,14 +136,26 @@ impl PaymentAdjusterReal { .balance_wei .into() } + + fn find_multiplication_coeff(consuming_wallet_balance: U256, final_criteria_sum: U256) -> u128 { + ((final_criteria_sum / consuming_wallet_balance) * *MULTI_COEFF_BY_100).as_u128() + } } #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; + use crate::accountant::dao_utils::to_time_t; + use crate::accountant::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::{MULTI_COEFF_BY_100, PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ConsumingWalletBalancesAndQualifiedPayables}; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + use crate::sub_lib::blockchain_bridge::{ + ConsumingWalletBalances, OutcomingPayamentsInstructions, + }; + use crate::test_utils::make_wallet; + use itertools::fold; + use std::time::{Duration, SystemTime}; + use std::vec; use web3::types::U256; fn type_definite_conversion(gwei: u64) -> u128 { @@ -153,4 +232,141 @@ mod tests { assert_eq!(min, U256::from(111_000_000_000_u128)) } + + #[test] + fn multiplication_coeff_to_get_integers_above_one_instead_of_fractional_numbers_works() { + let final_criteria_sum = U256::from(5_000_000_000_000_u64); + let consuming_wallet_balances = vec![ + U256::from(222_222_222_222_u64), + U256::from(100_000), + U256::from(123_456_789), + ]; + + let result = consuming_wallet_balances + .clone() + .into_iter() + .map(|cw_balance| { + PaymentAdjusterReal::find_multiplication_coeff(cw_balance, final_criteria_sum) + }) + .collect::>(); + + let expected_coefficients = { + let co_1 = + ((final_criteria_sum / consuming_wallet_balances[0]) * *MULTI_COEFF_BY_100).as_u128(); + assert_eq!(co_1, 220); + let co_2 = + ((final_criteria_sum / consuming_wallet_balances[1]) * *MULTI_COEFF_BY_100).as_u128(); + assert_eq!(co_2, 500_000_000); + let co_3 = + ((final_criteria_sum / consuming_wallet_balances[2]) * *MULTI_COEFF_BY_100).as_u128(); + assert_eq!(co_3, 405_000); + vec![co_1, co_2, co_3] + }; + assert_eq!(result, expected_coefficients) + } + + #[test] + fn adjust_payments_works() { + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 444_444_444_444_444, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1234)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 666_666_666_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(500)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 22_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5678)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let subject = PaymentAdjusterReal::new(); + let accounts_sum: u128 = 444_444_444_444_444 + 666_666_666_000_000_000 + 22_000_000_000_000; //= 667_133_110_444_444_444 + let consuming_wallet_masq_balance = U256::from(accounts_sum - 300_000_000_000_000); + let msg = ConsumingWalletBalancesAndQualifiedPayables { + qualified_payables, + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(150), + masq_tokens_wei: consuming_wallet_masq_balance, + }, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(msg, now); + + let expected_criteria_computation_output = { + let time_criteria = vec![ + secs_elapsed(account_1.last_paid_timestamp, now), + secs_elapsed(account_2.last_paid_timestamp, now), + secs_elapsed(account_3.last_paid_timestamp, now), + ]; + let amount_criteria = vec![ + account_1.balance_wei, + account_2.balance_wei, + account_3.balance_wei, + ]; + let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( + vec![0, 0, 0], + |acc: Vec, current| { + vec![ + acc[0] + current[0], + acc[1] + current[1], + acc[2] + current[2], + ] + }, + ); + let final_criteria_sum = U256::from(final_criteria.iter().sum::()); + let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( + consuming_wallet_masq_balance, + final_criteria_sum, + ); + let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance + * U256::from(multiplication_coeff) + / final_criteria_sum) + .as_u128(); + let balanced_portions = vec![ + in_ratio_fragment_of_available_balance * final_criteria[0] / multiplication_coeff, + in_ratio_fragment_of_available_balance * final_criteria[1] / multiplication_coeff, + in_ratio_fragment_of_available_balance * final_criteria[2] / multiplication_coeff, + ]; + let new_total_amount_to_pay = balanced_portions.iter().sum::(); + assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance.as_u128()); + assert!( + new_total_amount_to_pay >= (consuming_wallet_masq_balance.as_u128() * 100) / 102, + "new total amount to pay: {}, consuming wallet masq balance: {}", + new_total_amount_to_pay, + consuming_wallet_masq_balance + ); + let mut account_1_adjusted = account_1; + account_1_adjusted.balance_wei = balanced_portions[0]; + let mut account_2_adjusted = account_2; + account_2_adjusted.balance_wei = balanced_portions[1]; + let mut account_3_adjusted = account_3; + account_3_adjusted.balance_wei = balanced_portions[2]; + vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] + }; + assert_eq!( + result, + OutcomingPayamentsInstructions { + accounts: expected_criteria_computation_output, + response_skeleton_opt: None + } + ); + } + + fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { + now.duration_since(timestamp).unwrap().as_secs() as u128 + } + + #[test] + fn output_with_response_skeleton_opt_some() { + //TODO rather include into some other special test?? + } } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 5482b435d..42b70d6d8 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -44,7 +44,7 @@ use std::any::Any; use std::cell::RefCell; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use std::time::{Duration, SystemTime}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use time::format_description::parse; use time::OffsetDateTime; use web3::types::TransactionReceipt; @@ -262,7 +262,8 @@ impl PayableScannerMidProcedures for PayableScanner { &self, msg: ConsumingWalletBalancesAndQualifiedPayables, ) -> OutcomingPayamentsInstructions { - self.payment_adjuster.adjust_payments(msg) + let now = SystemTime::now(); + self.payment_adjuster.adjust_payments(msg, now) } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index f5dcde157..fb26b0759 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1374,7 +1374,8 @@ impl PayableThresholdsGaugeMock { pub struct PaymentAdjusterMock { is_adjustment_required_params: Arc>>, is_adjustment_required_results: RefCell>, - adjust_payments_params: Arc>>, + adjust_payments_params: + Arc>>, adjust_payments_results: RefCell>, } @@ -1390,8 +1391,9 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + now: SystemTime, ) -> OutcomingPayamentsInstructions { - self.adjust_payments_params.lock().unwrap().push(msg); + self.adjust_payments_params.lock().unwrap().push((msg, now)); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1414,7 +1416,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self From 279c2a01f1cf92f873c33a230724c1fbc837c99f Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 16 May 2023 01:28:40 +0200 Subject: [PATCH 007/250] GH-672: finished implementing the main test; not refactored yet --- node/src/accountant/payment_adjuster.rs | 109 ++++++++++++++++++------ node/src/accountant/scanners.rs | 2 +- 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 78a947881..c20057ff1 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -6,8 +6,7 @@ use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; use std::any::Any; -use std::iter; -use std::iter::once; +use std::iter::{once, successors}; use std::time::SystemTime; use web3::types::U256; @@ -59,9 +58,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { .as_secs() as u128; (criteria_sum + criteria, account) }); - let balance_criteria_closure: CriteriaClosure = - Box::new(|(criteria_sum, account)| (criteria_sum + account.balance_wei, account)); - + let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { + let digits_weight = log_10(account.balance_wei); + let additional_criteria = account.balance_wei * digits_weight as u128; + (criteria_sum + additional_criteria, account) + }); let qualified_payables = msg.qualified_payables; let accounts_count = qualified_payables.len(); @@ -142,18 +143,25 @@ impl PaymentAdjusterReal { } } +// replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` +// which will be introduced by Rust 1.67.0; this was written with 1.63.0 +fn log_10(num: u128) -> usize { + successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() +} + #[cfg(test)] mod tests { - use crate::accountant::dao_utils::to_time_t; use crate::accountant::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::{MULTI_COEFF_BY_100, PaymentAdjuster, PaymentAdjusterReal}; + use crate::accountant::payment_adjuster::{ + log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, + }; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ConsumingWalletBalancesAndQualifiedPayables}; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPayamentsInstructions, }; use crate::test_utils::make_wallet; - use itertools::fold; + use itertools::Itertools; use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; @@ -233,6 +241,19 @@ mod tests { assert_eq!(min, U256::from(111_000_000_000_u128)) } + #[test] + fn log_10_works() { + [ + (4_565_u128, 4), + (1_666_777, 7), + (3, 1), + (123, 3), + (111_111_111_111_111_111, 18), + ] + .into_iter() + .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) + } + #[test] fn multiplication_coeff_to_get_integers_above_one_instead_of_fractional_numbers_works() { let final_criteria_sum = U256::from(5_000_000_000_000_u64); @@ -251,15 +272,15 @@ mod tests { .collect::>(); let expected_coefficients = { - let co_1 = - ((final_criteria_sum / consuming_wallet_balances[0]) * *MULTI_COEFF_BY_100).as_u128(); - assert_eq!(co_1, 220); - let co_2 = - ((final_criteria_sum / consuming_wallet_balances[1]) * *MULTI_COEFF_BY_100).as_u128(); - assert_eq!(co_2, 500_000_000); - let co_3 = - ((final_criteria_sum / consuming_wallet_balances[2]) * *MULTI_COEFF_BY_100).as_u128(); - assert_eq!(co_3, 405_000); + let co_1 = ((final_criteria_sum / consuming_wallet_balances[0]) * *MULTI_COEFF_BY_100) + .as_u128(); + assert_eq!(co_1, 22_000); + let co_2 = ((final_criteria_sum / consuming_wallet_balances[1]) * *MULTI_COEFF_BY_100) + .as_u128(); + assert_eq!(co_2, 50_000_000_000); + let co_3 = ((final_criteria_sum / consuming_wallet_balances[2]) * *MULTI_COEFF_BY_100) + .as_u128(); + assert_eq!(co_3, 40_500_000); vec![co_1, co_2, co_3] }; assert_eq!(result, expected_coefficients) @@ -270,26 +291,27 @@ mod tests { let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 444_444_444_444_444, + balance_wei: 444_444_444_444_444_444, last_paid_timestamp: now.checked_sub(Duration::from_secs(1234)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 666_666_666_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(500)).unwrap(), + balance_wei: 666_666_666_666_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghk"), balance_wei: 22_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5678)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(78910)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let subject = PaymentAdjusterReal::new(); - let accounts_sum: u128 = 444_444_444_444_444 + 666_666_666_000_000_000 + 22_000_000_000_000; //= 667_133_110_444_444_444 - let consuming_wallet_masq_balance = U256::from(accounts_sum - 300_000_000_000_000); + let accounts_sum: u128 = + 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 + let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); let msg = ConsumingWalletBalancesAndQualifiedPayables { qualified_payables, consuming_wallet_balances: ConsumingWalletBalances { @@ -308,9 +330,9 @@ mod tests { secs_elapsed(account_3.last_paid_timestamp, now), ]; let amount_criteria = vec![ - account_1.balance_wei, - account_2.balance_wei, - account_3.balance_wei, + account_1.balance_wei * log_10(account_1.balance_wei) as u128, + account_2.balance_wei * log_10(account_2.balance_wei) as u128, + account_3.balance_wei * log_10(account_3.balance_wei) as u128, ]; let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( vec![0, 0, 0], @@ -359,6 +381,21 @@ mod tests { response_skeleton_opt: None } ); + + // Example of the current adjustment; + // printed with `visual_check_balance_before_after(.., ..)` + // + // BEFORE + // AFTER + // + // 444444444444444444 + // 333000000000000051 + // --- + // 666666666666000000000000 + // 665999999999334000000004 + // --- + // 22000000000000 + // 12820500003284 } fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { @@ -369,4 +406,24 @@ mod tests { fn output_with_response_skeleton_opt_some() { //TODO rather include into some other special test?? } + + #[allow(dead_code)] + fn visual_check_balance_before_after( + accounts_before: &[PayableAccount], + accounts_after: &[PayableAccount], + ) { + let sorting = |a: &&PayableAccount, b: &&PayableAccount| { + Ord::cmp(&a.wallet.to_string(), &b.wallet.to_string()) + }; + accounts_before + .into_iter() + .sorted_by(sorting) + .zip(accounts_after.into_iter().sorted_by(sorting)) + .for_each(|(original_payable, adjusted_payable)| { + eprintln!( + "{}\n{}\n---", + original_payable.balance_wei, adjusted_payable.balance_wei + ) + }) + } } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 42b70d6d8..38bc2bb59 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -44,7 +44,7 @@ use std::any::Any; use std::cell::RefCell; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime}; use time::format_description::parse; use time::OffsetDateTime; use web3::types::TransactionReceipt; From f51de9ba1efa563d261c44ac423e74bd589a219d Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 18 May 2023 23:38:27 +0200 Subject: [PATCH 008/250] GH-672: payment adjuster refactored; tuning logging; but wanna master features that are missing here --- node/src/accountant/mod.rs | 2 +- node/src/accountant/payment_adjuster.rs | 189 ++++++++++++++------- node/src/accountant/scan_mid_procedures.rs | 2 + node/src/accountant/scanners.rs | 3 +- node/src/accountant/test_utils.rs | 2 +- 5 files changed, 129 insertions(+), 69 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 2dd358c0c..652e45556 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -222,7 +222,7 @@ impl Handler for Accountant { msg: ConsumingWalletBalancesAndQualifiedPayables, _ctx: &mut Self::Context, ) -> Self::Result { - let instructions = match self.scanners.payable.mid_procedure_soft(msg) { + let instructions = match self.scanners.payable.mid_procedure_soft(msg, &self.logger) { Either::Left(finalized_msg) => finalized_msg, Either::Right(unaccepted_msg) => { //TODO we will eventually query info from Neighborhood here diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index c20057ff1..344ae265a 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -9,13 +9,14 @@ use std::any::Any; use std::iter::{once, successors}; use std::time::SystemTime; use web3::types::U256; +use masq_lib::logger::Logger; lazy_static! { static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); } pub trait PaymentAdjuster { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool; + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool; fn adjust_payments( &self, @@ -29,80 +30,34 @@ pub trait PaymentAdjuster { pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool { - let sum = Self::sum_payable_balances(&msg.qualified_payables); + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool { + let sum = Self::sum_as_u256(&msg.qualified_payables, |payable| payable.balance_wei); let consuming_wallet_balance = msg.consuming_wallet_balances.masq_tokens_wei; - if U256::from(sum) > consuming_wallet_balance { - true + if U256::from(sum) < consuming_wallet_balance { + false } else if U256::from(Self::find_smallest_debt(&msg.qualified_payables)) > consuming_wallet_balance { todo!() } else { - false + // warning!(logger, "Qualified payables for wallets {} make total of {} wei which cannot be satisfied with consuming wallet balance {} wei. Payments adjustment ordered.", ); + true } } + //TODO add logs ...warnings fn adjust_payments( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, now: SystemTime, ) -> OutcomingPayamentsInstructions { - //define individual criteria here; write closures to be used in sequence by multiple maps() - type CriteriaClosure<'a> = - Box (u128, PayableAccount) + 'a>; - let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let criteria = now - .duration_since(account.last_paid_timestamp) - .expect("time traveller") - .as_secs() as u128; - (criteria_sum + criteria, account) - }); - let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let digits_weight = log_10(account.balance_wei); - let additional_criteria = account.balance_wei * digits_weight as u128; - (criteria_sum + additional_criteria, account) - }); - - let qualified_payables = msg.qualified_payables; - let accounts_count = qualified_payables.len(); - let endless_iter_with_accounts = qualified_payables.into_iter().cycle(); - let criteria_iter = { - let one_element = once(0_u128); - let endlessly_repeated = one_element.into_iter().cycle(); - endlessly_repeated.take(accounts_count) - }; - - // add your criteria to chained map() functions here - let mid_computed_results = criteria_iter - .zip(endless_iter_with_accounts) - .map(time_criteria_closure) - .map(balance_criteria_closure) - .collect::>(); - - let criteria_sum: u128 = mid_computed_results - .iter() - .map(|(criteria, _)| criteria) - .sum(); - let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( + let accounts_with_zero_criteria = Self::initialize_zero_criteria(msg.qualified_payables); + let accounts_with_individual_criteria = + Self::apply_criteria(accounts_with_zero_criteria, now); + let balance_adjusted_accounts = Self::handle_adjustment( msg.consuming_wallet_balances.masq_tokens_wei, - U256::from(criteria_sum), + accounts_with_individual_criteria, ); - let proportional_fragment_of_cw_balance = - msg.consuming_wallet_balances.masq_tokens_wei.as_u128() * multiplication_coeff - / criteria_sum; - - let rebuild_account = |(criteria_sum, mut account): (u128, PayableAccount)| { - let proportional_amount_to_pay = - criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - account.balance_wei = proportional_amount_to_pay; - account - }; - - let balance_adjusted_accounts = mid_computed_results - .into_iter() - .map(rebuild_account) - .collect(); OutcomingPayamentsInstructions { accounts: balance_adjusted_accounts, @@ -118,6 +73,13 @@ impl PaymentAdjusterReal { Self {} } + fn sum_as_u256(collection: &[T], arranger: F) -> U256 + where + F: Fn(&T) -> u128, + { + collection.iter().map(arranger).sum::().into() + } + fn sum_payable_balances(qualified_accounts: &[PayableAccount]) -> U256 { qualified_accounts .iter() @@ -138,8 +100,87 @@ impl PaymentAdjusterReal { .into() } - fn find_multiplication_coeff(consuming_wallet_balance: U256, final_criteria_sum: U256) -> u128 { - ((final_criteria_sum / consuming_wallet_balance) * *MULTI_COEFF_BY_100).as_u128() + fn find_multiplication_coeff(cw_masq_balance: U256, criteria_sum: U256) -> u128 { + ((criteria_sum / cw_masq_balance) * *MULTI_COEFF_BY_100).as_u128() + } + + fn initialize_zero_criteria( + qualified_payables: Vec, + ) -> impl Iterator { + fn just_zero_criteria_iterator(accounts_count: usize) -> impl Iterator { + let one_element = once(0_u128); + let endlessly_repeated = one_element.into_iter().cycle(); + endlessly_repeated.take(accounts_count) + } + + let accounts_count = qualified_payables.len(); + let criteria_iterator = just_zero_criteria_iterator(accounts_count); + criteria_iterator.zip(qualified_payables.into_iter()) + } + + fn recreate_accounts_with_proportioned_balances( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + proportional_fragment_of_cw_balance: u128, + multiplication_coeff: u128, + ) -> Vec { + let rebuild_account = |(criteria_sum, mut account): (u128, PayableAccount)| { + let proportional_amount_to_pay = + criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + account.balance_wei = proportional_amount_to_pay; + account + }; + + accounts_with_individual_criteria + .into_iter() + .map(rebuild_account) + .collect() + } + + fn handle_adjustment( + cw_masq_balance: U256, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + ) -> Vec { + let criteria_sum = + Self::sum_as_u256(&accounts_with_individual_criteria, |(criteria, _)| { + *criteria + }); + let multiplication_coeff = + PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_sum); + let proportional_fragment_of_cw_balance = + cw_masq_balance.as_u128() * multiplication_coeff / criteria_sum.as_u128(); + + Self::recreate_accounts_with_proportioned_balances( + accounts_with_individual_criteria, + proportional_fragment_of_cw_balance, + multiplication_coeff, + ) + } + + fn apply_criteria( + accounts_with_zero_criteria: impl Iterator, + now: SystemTime, + ) -> Vec<(u128, PayableAccount)> { + type CriteriaClosure<'a> = + Box (u128, PayableAccount) + 'a>; + //define individual criteria as closures to be used in a map() + + let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { + let criteria = now + .duration_since(account.last_paid_timestamp) + .expect("time traveller") + .as_secs() as u128; + (criteria_sum + criteria, account) + }); + let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { + let digits_weight = log_10(account.balance_wei); + let additional_criteria = account.balance_wei * digits_weight as u128; + (criteria_sum + additional_criteria, account) + }); + + accounts_with_zero_criteria + .map(time_criteria_closure) + .map(balance_criteria_closure) + .collect() } } @@ -165,6 +206,8 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -205,15 +248,29 @@ mod tests { } #[test] - fn is_adjustment_required_works_for_non_error_cases() { + fn is_adjustment_required_works_for_negative_answer() { + init_test_logging(); + let test_name = "is_adjustment_required_works_for_negative_answer"; let subject = PaymentAdjusterReal::new(); + let logger = Logger::new(test_name); let msg_1 = make_cw_balance_and_q_payables_msg(vec![85, 14], 100); let msg_2 = make_cw_balance_and_q_payables_msg(vec![85, 15], 100); + + assert_eq!(subject.is_adjustment_required(&msg_1, &logger), false); + assert_eq!(subject.is_adjustment_required(&msg_2, &logger), false); + TestLogHandler::new().exists_no_log_containing(&format!("WARNING: {test_name}:")); + } + + #[test] + fn is_adjustment_required_works_for_positive_answer() { + init_test_logging(); + let test_name = "is_adjustment_required_works_for_positive_answer"; + let logger = Logger::new(test_name); + let subject = PaymentAdjusterReal::new(); let msg_3 = make_cw_balance_and_q_payables_msg(vec![85, 16], 100); - assert_eq!(subject.is_adjustment_required(&msg_1), false); - assert_eq!(subject.is_adjustment_required(&msg_2), false); - assert_eq!(subject.is_adjustment_required(&msg_3), true) + assert_eq!(subject.is_adjustment_required(&msg_3, &logger), true); + TestLogHandler::new().exists_log_containing(&format!("WARNING: {test_name}: Qualified payables for wallets grr make total of 101 wei which cannot be satisfied with consuming wallet balance 100 wei. Payments adjustment ordered.")); } #[test] @@ -404,7 +461,7 @@ mod tests { #[test] fn output_with_response_skeleton_opt_some() { - //TODO rather include into some other special test?? + todo!("rather include into some other special test??") } #[allow(dead_code)] diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 89032d410..693981294 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -5,6 +5,7 @@ use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; use itertools::Either; +use masq_lib::logger::Logger; pub trait PayableScannerWithMidProcedures: Scanner + PayableScannerMidProcedures @@ -18,6 +19,7 @@ pub trait PayableScannerMidProcedures { fn mid_procedure_soft( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger ) -> Either; fn mid_procedure_hard( &self, diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 38bc2bb59..6d8af61a0 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -247,8 +247,9 @@ impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_soft( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger ) -> Either { - if !self.payment_adjuster.is_adjustment_required(&msg) { + if !self.payment_adjuster.is_adjustment_required(&msg, logger) { Either::Left(OutcomingPayamentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index fb26b0759..bbef60734 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1380,7 +1380,7 @@ pub struct PaymentAdjusterMock { } impl PaymentAdjuster for PaymentAdjusterMock { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables) -> bool { + fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool { self.is_adjustment_required_params .lock() .unwrap() From 0a7dc320898d80a1b451a0e32986a3dc2a4f1a9b Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 19 May 2023 00:06:08 +0200 Subject: [PATCH 009/250] GH-672: some logging integrated --- node/src/accountant/payment_adjuster.rs | 58 ++++++++++++++++------ node/src/accountant/scan_mid_procedures.rs | 2 +- node/src/accountant/scanners.rs | 15 +++--- node/src/accountant/test_utils.rs | 11 +++- node/src/blockchain/blockchain_bridge.rs | 10 +++- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 344ae265a..fa463c3c5 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,22 +1,27 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_dao::PayableAccount; -use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::{comma_joined_stringifiable, ConsumingWalletBalancesAndQualifiedPayables}; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; +use masq_lib::logger::Logger; use std::any::Any; use std::iter::{once, successors}; use std::time::SystemTime; +use thousands::Separable; use web3::types::U256; -use masq_lib::logger::Logger; lazy_static! { static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); } pub trait PaymentAdjuster { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool; + fn is_adjustment_required( + &self, + msg: &ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger, + ) -> bool; fn adjust_payments( &self, @@ -30,22 +35,25 @@ pub trait PaymentAdjuster { pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool { - let sum = Self::sum_as_u256(&msg.qualified_payables, |payable| payable.balance_wei); - let consuming_wallet_balance = msg.consuming_wallet_balances.masq_tokens_wei; - if U256::from(sum) < consuming_wallet_balance { + fn is_adjustment_required( + &self, + msg: &ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger, + ) -> bool { + let qualified_payables = &msg.qualified_payables; + let sum = Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); + let cw_masq_balance = msg.consuming_wallet_balances.masq_tokens_wei; + if U256::from(sum) <= cw_masq_balance { false - } else if U256::from(Self::find_smallest_debt(&msg.qualified_payables)) - > consuming_wallet_balance - { + } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { todo!() } else { - // warning!(logger, "Qualified payables for wallets {} make total of {} wei which cannot be satisfied with consuming wallet balance {} wei. Payments adjustment ordered.", ); + Self::log_adjustment_required(logger, qualified_payables, sum, cw_masq_balance); + true } } - //TODO add logs ...warnings fn adjust_payments( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, @@ -182,6 +190,22 @@ impl PaymentAdjusterReal { .map(balance_criteria_closure) .collect() } + + fn log_adjustment_required( + logger: &Logger, + payables: &[PayableAccount], + payables_sum: U256, + cw_masq_balance: U256, + ) { + warning!( + logger, + "Qualified payables for wallets {} make total of {} wei which cannot be satisfied \ + with consuming wallet balance {} wei. Payments adjustment ordered.", + comma_joined_stringifiable(payables, |p| p.wallet.to_string()), + payables_sum.separate_with_commas(), + cw_masq_balance.separate_with_commas() + ) + } } // replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` @@ -203,11 +227,11 @@ mod tests { }; use crate::test_utils::make_wallet; use itertools::Itertools; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; - use masq_lib::logger::Logger; - use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -258,7 +282,7 @@ mod tests { assert_eq!(subject.is_adjustment_required(&msg_1, &logger), false); assert_eq!(subject.is_adjustment_required(&msg_2, &logger), false); - TestLogHandler::new().exists_no_log_containing(&format!("WARNING: {test_name}:")); + TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); } #[test] @@ -270,7 +294,9 @@ mod tests { let msg_3 = make_cw_balance_and_q_payables_msg(vec![85, 16], 100); assert_eq!(subject.is_adjustment_required(&msg_3, &logger), true); - TestLogHandler::new().exists_log_containing(&format!("WARNING: {test_name}: Qualified payables for wallets grr make total of 101 wei which cannot be satisfied with consuming wallet balance 100 wei. Payments adjustment ordered.")); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Qualified payables for wallets \ + 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make total of 101,000,000,000 \ + wei which cannot be satisfied with consuming wallet balance 100,000,000,000 wei. Payments adjustment ordered.")); } #[test] diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 693981294..19f952ce8 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -19,7 +19,7 @@ pub trait PayableScannerMidProcedures { fn mid_procedure_soft( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, - logger: &Logger + logger: &Logger, ) -> Either; fn mid_procedure_hard( &self, diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 0f0c1c086..2684dac86 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -4,12 +4,12 @@ use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable} use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; -use crate::accountant::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ - LocallyCausedError, RemotelyCausedErrors, -}; use crate::accountant::scan_mid_procedures::{ PayableScannerMidProcedures, PayableScannerWithMidProcedures, }; +use crate::accountant::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ + LocallyCausedError, RemotelyCausedErrors, +}; use crate::accountant::scanners_utils::payable_scanner_utils::{ debugging_summary_after_error_separation, err_msg_if_failed_without_existing_fingerprints, investigate_debt_extremes, mark_pending_payable_fatal_error, payables_debug_summary, @@ -24,9 +24,10 @@ use crate::accountant::scanners_utils::pending_payable_scanner_utils::{ use crate::accountant::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::PendingPayableId; use crate::accountant::{ - comma_joined_stringifiable, gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, - ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, - ScanForPendingPayables, ScanForReceivables, SentPayables, + comma_joined_stringifiable, gwei_to_wei, Accountant, + ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, ReportTransactionReceipts, + RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, + ScanForReceivables, SentPayables, }; use crate::banned_dao::BannedDao; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; @@ -258,7 +259,7 @@ impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_soft( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, - logger: &Logger + logger: &Logger, ) -> Either { if !self.payment_adjuster.is_adjustment_required(&msg, logger) { Either::Left(OutcomingPayamentsInstructions { diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 18292f5cc..2b10224e1 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -15,7 +15,10 @@ use crate::accountant::receivable_dao::{ }; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; -use crate::accountant::{gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, DEFAULT_PENDING_TOO_LONG_SEC}; +use crate::accountant::{ + gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, + DEFAULT_PENDING_TOO_LONG_SEC, +}; use crate::banned_dao::{BannedDao, BannedDaoFactory}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; @@ -1385,7 +1388,11 @@ pub struct PaymentAdjusterMock { } impl PaymentAdjuster for PaymentAdjusterMock { - fn is_adjustment_required(&self, msg: &ConsumingWalletBalancesAndQualifiedPayables, logger: &Logger) -> bool { + fn is_adjustment_required( + &self, + msg: &ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger, + ) -> bool { self.is_adjustment_required_params .lock() .unwrap() diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index eab524171..904e7f60b 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -14,7 +14,10 @@ use crate::db_config::config_dao::ConfigDaoReal; use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; -use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, RequestBalancesToPayPayables, OutcomingPayamentsInstructions, ConsumingWalletBalances}; +use crate::sub_lib::blockchain_bridge::{ + BlockchainBridgeSubs, ConsumingWalletBalances, OutcomingPayamentsInstructions, + RequestBalancesToPayPayables, +}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::utils::{db_connection_launch_panic, handle_ui_crash_request}; @@ -309,7 +312,10 @@ impl BlockchainBridge { Ok(()) } - fn handle_report_accounts_payable(&mut self, msg: OutcomingPayamentsInstructions) -> Result<(), String> { + fn handle_report_accounts_payable( + &mut self, + msg: OutcomingPayamentsInstructions, + ) -> Result<(), String> { let skeleton_opt = msg.response_skeleton_opt; let result = self.process_payments(&msg); From 89dc99a9a957f467753cc56a320588ebf3ee0f7b Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 May 2023 10:55:06 +0200 Subject: [PATCH 010/250] GH-672: fine debug log --- node/src/accountant/mod.rs | 4 ++- node/src/accountant/payment_adjuster.rs | 40 ++++++++++++++++++---- node/src/accountant/scan_mid_procedures.rs | 1 + node/src/accountant/scanners.rs | 3 +- node/src/accountant/test_utils.rs | 1 + 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 75cecc6f9..c40f14f40 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -229,7 +229,9 @@ impl Handler for Accountant { Either::Left(finalized_msg) => finalized_msg, Either::Right(unaccepted_msg) => { //TODO we will eventually query info from Neighborhood here - self.scanners.payable.mid_procedure_hard(unaccepted_msg) + self.scanners + .payable + .mid_procedure_hard(unaccepted_msg, &self.logger) } }; self.outcoming_payments_instructions_sub_opt diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index fa463c3c5..8200ecc11 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -27,6 +27,7 @@ pub trait PaymentAdjuster { &self, msg: ConsumingWalletBalancesAndQualifiedPayables, now: SystemTime, + logger: &Logger, ) -> OutcomingPayamentsInstructions; declare_as_any!(); @@ -58,7 +59,10 @@ impl PaymentAdjuster for PaymentAdjusterReal { &self, msg: ConsumingWalletBalancesAndQualifiedPayables, now: SystemTime, + logger: &Logger, ) -> OutcomingPayamentsInstructions { + let debug_log_printer_opt = logger.debug_enabled().then_some(Self::before_and_after_debug_msg_printer(&msg.qualified_payables)); + let accounts_with_zero_criteria = Self::initialize_zero_criteria(msg.qualified_payables); let accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); @@ -67,6 +71,12 @@ impl PaymentAdjuster for PaymentAdjusterReal { accounts_with_individual_criteria, ); + debug!( + logger, + "{}", + debug_log_printer_opt.expect("debug message missing")(&balance_adjusted_accounts) + ); + OutcomingPayamentsInstructions { accounts: balance_adjusted_accounts, response_skeleton_opt: msg.response_skeleton_opt, @@ -191,6 +201,21 @@ impl PaymentAdjusterReal { .collect() } + fn format_brief_accounts_summary(accounts: &[PayableAccount]) -> String { + todo!() + } + + fn before_and_after_debug_msg_printer(before: &[PayableAccount]) -> impl Fn(&[PayableAccount])-> String{ + let accounts_before = Self::format_brief_accounts_summary(before); + move |adjusted_balances: &[PayableAccount]| { + format!( + "Original payable accounts {}, accounts after the adjustment {}", + accounts_before, + Self::format_brief_accounts_summary(adjusted_balances) + ) + } + } + fn log_adjustment_required( logger: &Logger, payables: &[PayableAccount], @@ -199,8 +224,9 @@ impl PaymentAdjusterReal { ) { warning!( logger, - "Qualified payables for wallets {} make total of {} wei which cannot be satisfied \ - with consuming wallet balance {} wei. Payments adjustment ordered.", + "Payments for wallets {} make total of {} wei while the consuming wallet holds \ + only {} wei. Going to adjust them to fit in the limit, by cutting back the number \ + of payments or their size.", comma_joined_stringifiable(payables, |p| p.wallet.to_string()), payables_sum.separate_with_commas(), cw_masq_balance.separate_with_commas() @@ -294,9 +320,11 @@ mod tests { let msg_3 = make_cw_balance_and_q_payables_msg(vec![85, 16], 100); assert_eq!(subject.is_adjustment_required(&msg_3, &logger), true); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Qualified payables for wallets \ - 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make total of 101,000,000,000 \ - wei which cannot be satisfied with consuming wallet balance 100,000,000,000 wei. Payments adjustment ordered.")); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ + 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make \ + total of 101,000,000,000 wei while the consuming wallet holds only 100,000,000,000 wei. \ + Going to adjust them to fit in the limit, by cutting back the number of payments or their \ + size..")); } #[test] @@ -404,7 +432,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(msg, now); + let result = subject.adjust_payments(msg, now, &Logger::new("test")); let expected_criteria_computation_output = { let time_criteria = vec![ diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 19f952ce8..9557998ee 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -24,5 +24,6 @@ pub trait PayableScannerMidProcedures { fn mid_procedure_hard( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger, ) -> OutcomingPayamentsInstructions; } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 2684dac86..e78fea1f3 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -274,9 +274,10 @@ impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_hard( &self, msg: ConsumingWalletBalancesAndQualifiedPayables, + logger: &Logger, ) -> OutcomingPayamentsInstructions { let now = SystemTime::now(); - self.payment_adjuster.adjust_payments(msg, now) + self.payment_adjuster.adjust_payments(msg, now, logger) } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 2b10224e1..67f01732b 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1404,6 +1404,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { &self, msg: ConsumingWalletBalancesAndQualifiedPayables, now: SystemTime, + logger: &Logger, ) -> OutcomingPayamentsInstructions { self.adjust_payments_params.lock().unwrap().push((msg, now)); self.adjust_payments_results.borrow_mut().remove(0) From d448ece7252fb9900276b6e7b908dde4ff0e4340 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 May 2023 11:43:02 +0200 Subject: [PATCH 011/250] GH-672: interim commit --- node/src/accountant/mod.rs | 30 ++++++++------------ node/src/accountant/payment_adjuster.rs | 33 ++++++++++++++-------- node/src/accountant/scan_mid_procedures.rs | 2 +- node/src/accountant/scanners.rs | 8 +++--- node/src/accountant/test_utils.rs | 6 ++-- node/src/blockchain/blockchain_bridge.rs | 5 ++-- node/src/sub_lib/accountant.rs | 29 +++++++++++++++---- node/src/test_utils/recorder.rs | 3 +- 8 files changed, 67 insertions(+), 49 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c40f14f40..94f4903ec 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -25,15 +25,16 @@ use masq_lib::messages::{ use masq_lib::ui_gateway::{MessageBody, MessagePath}; use crate::accountant::dao_utils::{ - remap_payable_accounts, remap_receivable_accounts, CustomQuery, DaoFactoryReal, + CustomQuery, DaoFactoryReal, remap_payable_accounts, remap_receivable_accounts, }; use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::payable_dao::{PayableAccount, PayableDaoError}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; -use crate::accountant::scanners::{ScanTimings, Scanners}; +use crate::accountant::scanners::{Scanners, ScanTimings}; use crate::blockchain::blockchain_bridge::{ PendingPayableFingerprint, PendingPayableFingerprintSeeds, RetrieveTransactions, }; @@ -81,7 +82,7 @@ use std::ops::{Div, Mul}; use std::path::Path; use std::rc::Rc; use std::time::SystemTime; -use web3::types::{TransactionReceipt, H256}; +use web3::types::{H256, TransactionReceipt}; pub const CRASH_KEY: &str = "ACCOUNTANT"; pub const DEFAULT_PENDING_TOO_LONG_SEC: u64 = 21_600; //6 hours @@ -138,13 +139,6 @@ pub struct SentPayables { pub response_skeleton_opt: Option, } -#[derive(Debug, Message, PartialEq, Eq, Clone)] -pub struct ConsumingWalletBalancesAndQualifiedPayables { - pub qualified_payables: Vec, - pub consuming_wallet_balances: ConsumingWalletBalances, - pub response_skeleton_opt: Option, -} - #[derive(Debug, Message, Default, PartialEq, Eq, Clone, Copy)] pub struct ScanForPayables { pub response_skeleton_opt: Option, @@ -1014,7 +1008,7 @@ pub mod check_sqlite_fns { mod tests { use super::*; use crate::accountant::dao_utils::from_time_t; - use crate::accountant::dao_utils::{to_time_t, CustomQuery}; + use crate::accountant::dao_utils::{CustomQuery, to_time_t}; use crate::accountant::payable_dao::{ PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, }; @@ -1025,8 +1019,8 @@ mod tests { ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, - BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, + BannedDaoFactoryMock, bc_from_earning_wallet, bc_from_wallets, make_payable_account, + make_payables, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, }; @@ -1035,11 +1029,11 @@ mod tests { use crate::blockchain::blockchain_bridge::BlockchainBridge; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; - use crate::blockchain::test_utils::{make_tx_hash, BlockchainInterfaceMock}; + use crate::blockchain::test_utils::{BlockchainInterfaceMock, make_tx_hash}; use crate::match_every_type_id; use crate::sub_lib::accountant::{ - ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, - DEFAULT_PAYMENT_THRESHOLDS, + DEFAULT_PAYMENT_THRESHOLDS, ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, + ScanIntervals, }; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use crate::sub_lib::utils::NotifyLaterHandleReal; @@ -1050,8 +1044,8 @@ mod tests { use crate::test_utils::recorder_stop_conditions::{StopCondition, StopConditions}; 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_bc_with_defaults, - prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, + assert_on_initialization_with_panic_on_migration, AssertionsMessage, + make_bc_with_defaults, prove_that_crash_request_handler_is_hooked_up, }; use crate::test_utils::{make_paying_wallet, make_wallet}; use actix::{Arbiter, System}; diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 8200ecc11..c58502239 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,7 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::comma_joined_stringifiable; +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::payable_dao::PayableAccount; -use crate::accountant::{comma_joined_stringifiable, ConsumingWalletBalancesAndQualifiedPayables}; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; @@ -61,7 +62,12 @@ impl PaymentAdjuster for PaymentAdjusterReal { now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { - let debug_log_printer_opt = logger.debug_enabled().then_some(Self::before_and_after_debug_msg_printer(&msg.qualified_payables)); + let debug_log_printer_opt = + logger + .debug_enabled() + .then_some(Self::before_and_after_debug_msg_printer( + &msg.qualified_payables, + )); let accounts_with_zero_criteria = Self::initialize_zero_criteria(msg.qualified_payables); let accounts_with_individual_criteria = @@ -205,15 +211,17 @@ impl PaymentAdjusterReal { todo!() } - fn before_and_after_debug_msg_printer(before: &[PayableAccount]) -> impl Fn(&[PayableAccount])-> String{ - let accounts_before = Self::format_brief_accounts_summary(before); - move |adjusted_balances: &[PayableAccount]| { - format!( - "Original payable accounts {}, accounts after the adjustment {}", - accounts_before, - Self::format_brief_accounts_summary(adjusted_balances) - ) - } + fn before_and_after_debug_msg_printer( + before: &[PayableAccount], + ) -> impl Fn(&[PayableAccount]) -> String { + let accounts_before = Self::format_brief_accounts_summary(before); + move |adjusted_balances: &[PayableAccount]| { + format!( + "Original payable accounts {}, accounts after the adjustment {}", + accounts_before, + Self::format_brief_accounts_summary(adjusted_balances) + ) + } } fn log_adjustment_required( @@ -242,12 +250,12 @@ fn log_10(num: u128) -> usize { #[cfg(test)] mod tests { + use crate::accountant::gwei_to_wei; use crate::accountant::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, }; use crate::accountant::test_utils::make_payable_account; - use crate::accountant::{gwei_to_wei, ConsumingWalletBalancesAndQualifiedPayables}; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPayamentsInstructions, }; @@ -258,6 +266,7 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; + use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 9557998ee..c1d670e6a 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::scanners::Scanner; -use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; use itertools::Either; diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index e78fea1f3..3fe740fd1 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -1,5 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; @@ -24,10 +25,9 @@ use crate::accountant::scanners_utils::pending_payable_scanner_utils::{ use crate::accountant::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::PendingPayableId; use crate::accountant::{ - comma_joined_stringifiable, gwei_to_wei, Accountant, - ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, ReportTransactionReceipts, - RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, - ScanForReceivables, SentPayables, + comma_joined_stringifiable, gwei_to_wei, Accountant, ReceivedPayments, + ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, + ScanForPendingPayables, ScanForReceivables, SentPayables, }; use crate::banned_dao::BannedDao; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 67f01732b..8ab137d2f 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -3,6 +3,7 @@ #![cfg(test)] use crate::accountant::dao_utils::{from_time_t, to_time_t, CustomQuery}; +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; @@ -15,10 +16,7 @@ use crate::accountant::receivable_dao::{ }; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; -use crate::accountant::{ - gwei_to_wei, Accountant, ConsumingWalletBalancesAndQualifiedPayables, - DEFAULT_PENDING_TOO_LONG_SEC, -}; +use crate::accountant::{gwei_to_wei, Accountant, DEFAULT_PENDING_TOO_LONG_SEC}; use crate::banned_dao::{BannedDao, BannedDaoFactory}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 904e7f60b..9f66b5808 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,8 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::{ - ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, ResponseSkeleton, ScanError, - SentPayables, SkeletonOptHolder, + ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, }; use crate::accountant::{ReportTransactionReceipts, RequestTransactionReceipts}; use crate::blockchain::blockchain_interface::{ @@ -497,7 +497,6 @@ mod tests { use crate::accountant::dao_utils::from_time_t; use crate::accountant::payable_dao::{PayableAccount, PendingPayable}; use crate::accountant::test_utils::make_pending_payable_fingerprint; - use crate::accountant::ConsumingWalletBalancesAndQualifiedPayables; use crate::blockchain::bip32::Bip32ECKeyProvider; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; use crate::blockchain::blockchain_interface::{ diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 7409ef84d..834987993 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,10 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; use crate::accountant::payable_dao::PayableDaoFactory; use crate::accountant::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::receivable_dao::ReceivableDaoFactory; use crate::accountant::{ - checked_conversion, Accountant, ConsumingWalletBalancesAndQualifiedPayables, ReceivedPayments, - ReportTransactionReceipts, ScanError, SentPayables, + Accountant, checked_conversion, ReceivedPayments, ReportTransactionReceipts, ScanError, + SentPayables, }; use crate::banned_dao::BannedDaoFactory; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; @@ -20,6 +21,8 @@ use std::fmt::{Debug, Formatter}; use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; use std::time::{Duration, SystemTime}; +use crate::accountant; +use crate::blockchain::blockchain_bridge; lazy_static! { pub static ref DEFAULT_EARNING_WALLET: Wallet = Wallet::from_str("0x27d9A2AC83b493f88ce9B4532EDcf74e95B9788d").expect("Internal error"); @@ -195,11 +198,11 @@ impl MessageIdGenerator for MessageIdGeneratorReal { #[cfg(test)] mod tests { use crate::accountant::test_utils::AccountantBuilder; - use crate::accountant::{checked_conversion, Accountant}; + use crate::accountant::{Accountant, checked_conversion}; use crate::sub_lib::accountant::{ - AccountantSubsFactory, AccountantSubsFactoryReal, MessageIdGenerator, - MessageIdGeneratorReal, PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET, - DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, MSG_ID_INCREMENTER, + AccountantSubsFactory, AccountantSubsFactoryReal, DEFAULT_EARNING_WALLET, + DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, MessageIdGenerator, MessageIdGeneratorReal, + MSG_ID_INCREMENTER, PaymentThresholds, ScanIntervals, TEMPORARY_CONSUMING_WALLET, }; use crate::sub_lib::wallet::Wallet; @@ -291,3 +294,17 @@ mod tests { assert_eq!(id, 0) } } + +pub mod inter_actor_communication_for_payable_scanner { + use crate::accountant::payable_dao::PayableAccount; + use crate::accountant::ResponseSkeleton; + use actix::Message; + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + + #[derive(Debug, Message, PartialEq, Eq, Clone)] + pub struct ConsumingWalletBalancesAndQualifiedPayables { + pub qualified_payables: Vec, + pub consuming_wallet_balances: ConsumingWalletBalances, + pub response_skeleton_opt: Option, + } +} diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index c0b1b5e9c..e2de01c81 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::accountant::{ConsumingWalletBalancesAndQualifiedPayables, ReportTransactionReceipts}; +use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayables, From 0f45f2ef3875c5b60172a8ee8325df658ee4e4ee Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 26 May 2023 10:45:08 +0200 Subject: [PATCH 012/250] GH-672: interim commit --- node/src/accountant/mod.rs | 23 +++++--- .../src/accountant/payable_scan_setup_msgs.rs | 46 ++++++++++++++++ node/src/accountant/payment_adjuster.rs | 52 +++++++++++-------- node/src/accountant/scan_mid_procedures.rs | 8 +-- node/src/accountant/scanners.rs | 10 ++-- node/src/accountant/test_utils.rs | 14 ++--- node/src/blockchain/blockchain_bridge.rs | 32 ++++++------ node/src/sub_lib/accountant.rs | 19 ++----- node/src/test_utils/recorder.rs | 7 +-- 9 files changed, 131 insertions(+), 80 deletions(-) create mode 100644 node/src/accountant/payable_scan_setup_msgs.rs diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 94f4903ec..1c15e6681 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -8,6 +8,7 @@ pub mod payment_adjuster; pub mod pending_payable_dao; pub mod receivable_dao; pub mod scan_mid_procedures; +pub mod payable_scan_setup_msgs; pub mod scanners; pub mod scanners_utils; @@ -30,7 +31,7 @@ use crate::accountant::dao_utils::{ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{PayableAccount, PayableDaoError}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; @@ -211,12 +212,12 @@ impl Handler for Accountant { } } -impl Handler for Accountant { +impl Handler> for Accountant { type Result = (); fn handle( &mut self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, _ctx: &mut Self::Context, ) -> Self::Result { let instructions = match self.scanners.payable.mid_procedure_soft(msg, &self.logger) { @@ -478,7 +479,7 @@ impl Accountant { report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - ConsumingWalletBalancesAndQualifiedPayables + PayableScannerPaymentSetupMessage ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -1408,12 +1409,15 @@ mod tests { let account_2 = make_payable_account(333_333); let system = System::new("test"); let consuming_balances_and_qualified_payments = - ConsumingWalletBalancesAndQualifiedPayables { + PayableScannerPaymentSetupMessage{ qualified_payables: vec![account_1.clone(), account_2.clone()], - consuming_wallet_balances: ConsumingWalletBalances { + current_stage_data: ConsumingWalletBalancesAndGasPrice { + consuming_wallet_balances: + ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(u32::MAX), }, + gas_price: 3333333333333333,}, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -1490,7 +1494,12 @@ mod tests { let account_2 = make_payable_account(222_222); let system = System::new("test"); let consuming_balances_and_qualified_payments = - ConsumingWalletBalancesAndQualifiedPayables { + PayableScannerPaymentSetupMessage{ + qualified_payables: vec![], + current_stage_data: (), + response_skeleton_opt: None, + } + ConsumingWalletBalancesAndGasPrice { qualified_payables: vec![account_1.clone(), account_2.clone()], consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), diff --git a/node/src/accountant/payable_scan_setup_msgs.rs b/node/src/accountant/payable_scan_setup_msgs.rs new file mode 100644 index 000000000..b901a6f61 --- /dev/null +++ b/node/src/accountant/payable_scan_setup_msgs.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +//TODO remove this mod frame around +pub mod inter_actor_communication_for_payable_scanner { + use crate::accountant::payable_dao::PayableAccount; + use crate::accountant::ResponseSkeleton; + use actix::Message; + use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, RequestBalancesToPayPayables}; + + #[derive(Debug, Message, PartialEq, Eq, Clone)] + pub struct PayableScannerPaymentSetupMessage{ + //this field should stay private for anybody outside Accountant + pub (in crate::accountant) qualified_payables: Vec, + pub current_stage_data: T, + pub response_skeleton_opt: Option + } + + impl PayableScannerPaymentSetupMessage{ + pub fn qualified_payables(&self)->&[PayableAccount]{ + todo!() + } + } + + #[derive(Debug, PartialEq, Eq, Clone)] + pub struct ConsumingWalletBalancesAndGasPrice { + pub consuming_wallet_balances: ConsumingWalletBalances, + pub gas_price: u64 + } + + impl From> for Vec{ + fn from(_: PayableScannerPaymentSetupMessage) -> Self { + todo!() + } + } + + impl From<(RequestBalancesToPayPayables, ConsumingWalletBalancesAndGasPrice)> for PayableScannerPaymentSetupMessage{ + fn from((previous_msg, current_stage_data):(RequestBalancesToPayPayables, ConsumingWalletBalancesAndGasPrice)) -> Self { + PayableScannerPaymentSetupMessage{ + qualified_payables: previous_msg.accounts, + current_stage_data, + response_skeleton_opt: previous_msg.response_skeleton_opt, + } + } + } +} + diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index c58502239..6fe06e88f 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::comma_joined_stringifiable; -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::PayableAccount; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; @@ -20,13 +20,13 @@ lazy_static! { pub trait PaymentAdjuster { fn is_adjustment_required( &self, - msg: &ConsumingWalletBalancesAndQualifiedPayables, + msg: &PayableScannerPaymentSetupMessage, logger: &Logger, ) -> bool; fn adjust_payments( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions; @@ -39,12 +39,12 @@ pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { fn is_adjustment_required( &self, - msg: &ConsumingWalletBalancesAndQualifiedPayables, + msg: &PayableScannerPaymentSetupMessage, logger: &Logger, ) -> bool { - let qualified_payables = &msg.qualified_payables; + let qualified_payables = msg.qualified_payables(); let sum = Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); - let cw_masq_balance = msg.consuming_wallet_balances.masq_tokens_wei; + let cw_masq_balance = msg.current_stage_data.consuming_wallet_balances.masq_tokens_wei; if U256::from(sum) <= cw_masq_balance { false } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { @@ -58,22 +58,24 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { + let current_stage_data = msg.current_stage_data; + let qualified_payables: Vec = msg.into(); let debug_log_printer_opt = logger .debug_enabled() .then_some(Self::before_and_after_debug_msg_printer( - &msg.qualified_payables, + &qualified_payables, )); - let accounts_with_zero_criteria = Self::initialize_zero_criteria(msg.qualified_payables); + let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); let accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); let balance_adjusted_accounts = Self::handle_adjustment( - msg.consuming_wallet_balances.masq_tokens_wei, + current_stage_data.consuming_wallet_balances.masq_tokens_wei, accounts_with_individual_criteria, ); @@ -266,7 +268,7 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; - use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; + use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -288,19 +290,22 @@ mod tests { assert_eq!(result, U256::from(expected_result)) } - fn make_cw_balance_and_q_payables_msg( + fn make_msg_with_q_payables_cw_balance_and_gas_price( qualified_payables_balances_gwei: Vec, masq_balance_gwei: u64, - ) -> ConsumingWalletBalancesAndQualifiedPayables { + ) -> PayableScannerPaymentSetupMessage { let qualified_payables = qualified_payables_balances_gwei .into_iter() .map(|balance| make_payable_account(balance)) .collect(); - ConsumingWalletBalancesAndQualifiedPayables { + PayableScannerPaymentSetupMessage{ qualified_payables, - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::zero(), - masq_tokens_wei: gwei_to_wei(masq_balance_gwei), + current_stage_data: ConsumingWalletBalancesAndGasPrice { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::zero(), + masq_tokens_wei: gwei_to_wei(masq_balance_gwei), + }, + gas_price: 0, }, response_skeleton_opt: None, } @@ -312,8 +317,8 @@ mod tests { let test_name = "is_adjustment_required_works_for_negative_answer"; let subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); - let msg_1 = make_cw_balance_and_q_payables_msg(vec![85, 14], 100); - let msg_2 = make_cw_balance_and_q_payables_msg(vec![85, 15], 100); + let msg_1 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 14], 100); + let msg_2 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 15], 100); assert_eq!(subject.is_adjustment_required(&msg_1, &logger), false); assert_eq!(subject.is_adjustment_required(&msg_2, &logger), false); @@ -326,7 +331,7 @@ mod tests { let test_name = "is_adjustment_required_works_for_positive_answer"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); - let msg_3 = make_cw_balance_and_q_payables_msg(vec![85, 16], 100); + let msg_3 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 16], 100); assert_eq!(subject.is_adjustment_required(&msg_3, &logger), true); TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ @@ -432,12 +437,15 @@ mod tests { let accounts_sum: u128 = 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); - let msg = ConsumingWalletBalancesAndQualifiedPayables { + let msg = PayableScannerPaymentSetupMessage{ qualified_payables, - consuming_wallet_balances: ConsumingWalletBalances { + current_stage_data:ConsumingWalletBalancesAndGasPrice{ + consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(150), masq_tokens_wei: consuming_wallet_masq_balance, + }, + gas_price: 222222222222222222}, response_skeleton_opt: None, }; diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index c1d670e6a..32bff5635 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; @@ -18,12 +18,12 @@ where pub trait PayableScannerMidProcedures { fn mid_procedure_soft( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, logger: &Logger, - ) -> Either; + ) -> Either>; fn mid_procedure_hard( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, logger: &Logger, ) -> OutcomingPayamentsInstructions; } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 3fe740fd1..ef25977d8 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; @@ -258,12 +258,12 @@ impl PayableScannerWithMidProcedures impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_soft( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, logger: &Logger, - ) -> Either { + ) -> Either> { if !self.payment_adjuster.is_adjustment_required(&msg, logger) { Either::Left(OutcomingPayamentsInstructions { - accounts: msg.qualified_payables, + accounts: msg.into(), response_skeleton_opt: msg.response_skeleton_opt, }) } else { @@ -273,7 +273,7 @@ impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_hard( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, logger: &Logger, ) -> OutcomingPayamentsInstructions { let now = SystemTime::now(); diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 8ab137d2f..8fa87346d 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -3,7 +3,7 @@ #![cfg(test)] use crate::accountant::dao_utils::{from_time_t, to_time_t, CustomQuery}; -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; @@ -1378,17 +1378,17 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - is_adjustment_required_params: Arc>>, + is_adjustment_required_params: Arc>>>, is_adjustment_required_results: RefCell>, adjust_payments_params: - Arc>>, + Arc, SystemTime)>>>, adjust_payments_results: RefCell>, } impl PaymentAdjuster for PaymentAdjusterMock { fn is_adjustment_required( &self, - msg: &ConsumingWalletBalancesAndQualifiedPayables, + msg: &PayableScannerPaymentSetupMessage, logger: &Logger, ) -> bool { self.is_adjustment_required_params @@ -1400,7 +1400,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, - msg: ConsumingWalletBalancesAndQualifiedPayables, + msg: PayableScannerPaymentSetupMessage, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { @@ -1412,7 +1412,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn is_adjustment_required_params( mut self, - params: &Arc>>, + params: &Arc>>>, ) -> Self { self.is_adjustment_required_params = params.clone(); self @@ -1427,7 +1427,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc, SystemTime)>>>, ) -> Self { self.adjust_payments_params = params.clone(); self diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 9f66b5808..c20e54a54 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{PayableScannerPaymentSetupMessage, ConsumingWalletBalancesAndGasPrice}; use crate::accountant::{ ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, }; @@ -47,7 +47,7 @@ pub struct BlockchainBridge { persistent_config: Box, set_consuming_wallet_subs_opt: Option>>, sent_payable_subs_opt: Option>, - balances_and_payables_sub_opt: Option>, + balances_and_payables_sub_opt: Option>>, received_payments_subs_opt: Option>, scan_error_subs_opt: Option>, crashable: bool, @@ -299,14 +299,14 @@ impl BlockchainBridge { masq_tokens_wei: token_balance, } }; + let msg: PayableScannerPaymentSetupMessage = (msg, ConsumingWalletBalancesAndGasPrice{ + consuming_wallet_balances, + gas_price: u64::MAX + }).into(); self.balances_and_payables_sub_opt .as_ref() .expect("Accountant is unbound") - .try_send(ConsumingWalletBalancesAndQualifiedPayables { - qualified_payables: msg.accounts, - consuming_wallet_balances, - response_skeleton_opt: msg.response_skeleton_opt, - }) + .try_send(msg) .expect("Accountant is dead"); Ok(()) @@ -706,18 +706,18 @@ mod tests { assert_eq!(*get_token_balance_params, vec![consuming_wallet]); let accountant_received_payment = accountant_recording_arc.lock().unwrap(); assert_eq!(accountant_received_payment.len(), 1); - let reported_balances_and_qualified_accounts: &ConsumingWalletBalancesAndQualifiedPayables = + let reported_balances_and_qualified_accounts: &PayableScannerPaymentSetupMessage = accountant_received_payment.get_record(0); + let expected_msg: PayableScannerPaymentSetupMessage = (RequestBalancesToPayPayables{ accounts: qualified_accounts, response_skeleton_opt: Some(ResponseSkeleton { + client_id: 11122, + context_id: 444 + }) },ConsumingWalletBalancesAndGasPrice { + consuming_wallet_balances: wallet_balances_found, + gas_price: 11111111111111111111111111 + } ).into(); assert_eq!( reported_balances_and_qualified_accounts, - &ConsumingWalletBalancesAndQualifiedPayables { - qualified_payables: qualified_accounts, - consuming_wallet_balances: wallet_balances_found, - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 11122, - context_id: 444 - }) - } + &expected_msg ); } diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 834987993..20cb4880d 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,5 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; use crate::accountant::payable_dao::PayableDaoFactory; use crate::accountant::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::receivable_dao::ReceivableDaoFactory; @@ -23,6 +23,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::time::{Duration, SystemTime}; use crate::accountant; use crate::blockchain::blockchain_bridge; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayableScannerPaymentSetupMessage; lazy_static! { pub static ref DEFAULT_EARNING_WALLET: Wallet = Wallet::from_str("0x27d9A2AC83b493f88ce9B4532EDcf74e95B9788d").expect("Internal error"); @@ -98,7 +99,7 @@ pub struct AccountantSubs { pub report_exit_service_provided: Recipient, pub report_services_consumed: Recipient, pub report_consuming_wallet_balances_and_qualified_payables: - Recipient, + Recipient>, pub report_inbound_payments: Recipient, pub init_pending_payable_fingerprints: Recipient, pub report_transaction_receipts: Recipient, @@ -294,17 +295,3 @@ mod tests { assert_eq!(id, 0) } } - -pub mod inter_actor_communication_for_payable_scanner { - use crate::accountant::payable_dao::PayableAccount; - use crate::accountant::ResponseSkeleton; - use actix::Message; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; - - #[derive(Debug, Message, PartialEq, Eq, Clone)] - pub struct ConsumingWalletBalancesAndQualifiedPayables { - pub qualified_payables: Vec, - pub consuming_wallet_balances: ConsumingWalletBalances, - pub response_skeleton_opt: Option, - } -} diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index e2de01c81..f28e3ed19 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::sub_lib::accountant::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndQualifiedPayables; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, @@ -41,6 +41,7 @@ use crate::sub_lib::proxy_server::ProxyServerSubs; use crate::sub_lib::proxy_server::{ AddReturnRouteMessage, AddRouteMessage, ClientRequestPayload_0v1, }; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayableScannerPaymentSetupMessage; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; @@ -127,7 +128,7 @@ recorder_message_handler!(ReportServicesConsumedMessage); recorder_message_handler!(ReportExitServiceProvidedMessage); recorder_message_handler!(ReportRoutingServiceProvidedMessage); recorder_message_handler!(ScanError); -recorder_message_handler!(ConsumingWalletBalancesAndQualifiedPayables); +recorder_message_handler!(PayableScannerPaymentSetupMessage); recorder_message_handler!(SentPayables); recorder_message_handler!(SetConsumingWalletMessage); recorder_message_handler!(RequestBalancesToPayPayables); @@ -426,7 +427,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - ConsumingWalletBalancesAndQualifiedPayables + PayableScannerPaymentSetupMessage ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), From a0f22c3dc712b7ffb3edcda65ff794c24ce80ec0 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 26 May 2023 17:32:42 +0200 Subject: [PATCH 013/250] GH-672: another package of big changes (partly-private msgs; payment adjuster) --- node/src/accountant/mod.rs | 74 +++++++-------- .../src/accountant/payable_scan_setup_msgs.rs | 45 +++++---- node/src/accountant/payment_adjuster.rs | 45 +++++---- node/src/accountant/scan_mid_procedures.rs | 13 ++- node/src/accountant/scanners.rs | 15 ++- node/src/accountant/test_utils.rs | 32 +++++-- node/src/blockchain/blockchain_bridge.rs | 94 +++++++++++++++---- node/src/sub_lib/accountant.rs | 20 ++-- node/src/test_utils/recorder.rs | 6 +- 9 files changed, 213 insertions(+), 131 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 1c15e6681..1863d1cff 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -4,11 +4,11 @@ pub mod big_int_processing; pub mod dao_utils; pub mod financials; pub mod payable_dao; +pub mod payable_scan_setup_msgs; pub mod payment_adjuster; pub mod pending_payable_dao; pub mod receivable_dao; pub mod scan_mid_procedures; -pub mod payable_scan_setup_msgs; pub mod scanners; pub mod scanners_utils; @@ -26,16 +26,15 @@ use masq_lib::messages::{ use masq_lib::ui_gateway::{MessageBody, MessagePath}; use crate::accountant::dao_utils::{ - CustomQuery, DaoFactoryReal, remap_payable_accounts, remap_receivable_accounts, + remap_payable_accounts, remap_receivable_accounts, CustomQuery, DaoFactoryReal, }; use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; -use payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{PayableAccount, PayableDaoError}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; -use crate::accountant::scanners::{Scanners, ScanTimings}; +use crate::accountant::scanners::{ScanTimings, Scanners}; use crate::blockchain::blockchain_bridge::{ PendingPayableFingerprint, PendingPayableFingerprintSeeds, RetrieveTransactions, }; @@ -74,6 +73,9 @@ use masq_lib::ui_gateway::MessageTarget::ClientId; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::ExpectValue; use payable_dao::PayableDao; +use payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use receivable_dao::ReceivableDao; use std::any::type_name; #[cfg(test)] @@ -83,7 +85,7 @@ use std::ops::{Div, Mul}; use std::path::Path; use std::rc::Rc; use std::time::SystemTime; -use web3::types::{H256, TransactionReceipt}; +use web3::types::{TransactionReceipt, H256}; pub const CRASH_KEY: &str = "ACCOUNTANT"; pub const DEFAULT_PENDING_TOO_LONG_SEC: u64 = 21_600; //6 hours @@ -212,12 +214,12 @@ impl Handler for Accountant { } } -impl Handler> for Accountant { +impl Handler> for Accountant { type Result = (); fn handle( &mut self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, _ctx: &mut Self::Context, ) -> Self::Result { let instructions = match self.scanners.payable.mid_procedure_soft(msg, &self.logger) { @@ -479,7 +481,7 @@ impl Accountant { report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayableScannerPaymentSetupMessage + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -1009,7 +1011,7 @@ pub mod check_sqlite_fns { mod tests { use super::*; use crate::accountant::dao_utils::from_time_t; - use crate::accountant::dao_utils::{CustomQuery, to_time_t}; + use crate::accountant::dao_utils::{to_time_t, CustomQuery}; use crate::accountant::payable_dao::{ PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, }; @@ -1020,8 +1022,8 @@ mod tests { ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - BannedDaoFactoryMock, bc_from_earning_wallet, bc_from_wallets, make_payable_account, - make_payables, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, + bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, + BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, }; @@ -1030,11 +1032,11 @@ mod tests { use crate::blockchain::blockchain_bridge::BlockchainBridge; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; - use crate::blockchain::test_utils::{BlockchainInterfaceMock, make_tx_hash}; + use crate::blockchain::test_utils::{make_tx_hash, BlockchainInterfaceMock}; use crate::match_every_type_id; use crate::sub_lib::accountant::{ - DEFAULT_PAYMENT_THRESHOLDS, ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, - ScanIntervals, + ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, + DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use crate::sub_lib::utils::NotifyLaterHandleReal; @@ -1045,8 +1047,8 @@ mod tests { use crate::test_utils::recorder_stop_conditions::{StopCondition, StopConditions}; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use crate::test_utils::unshared_test_utils::{ - assert_on_initialization_with_panic_on_migration, AssertionsMessage, - make_bc_with_defaults, prove_that_crash_request_handler_is_hooked_up, + assert_on_initialization_with_panic_on_migration, make_bc_with_defaults, + prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, }; use crate::test_utils::{make_paying_wallet, make_wallet}; use actix::{Arbiter, System}; @@ -1408,21 +1410,20 @@ mod tests { let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); let system = System::new("test"); - let consuming_balances_and_qualified_payments = - PayableScannerPaymentSetupMessage{ - qualified_payables: vec![account_1.clone(), account_2.clone()], - current_stage_data: ConsumingWalletBalancesAndGasPrice { - consuming_wallet_balances: - ConsumingWalletBalances { + let consuming_balances_and_qualified_payments = PayablePaymentSetup { + qualified_payables: vec![account_1.clone(), account_2.clone()], + this_stage_data: ConsumingWalletBalancesAndGasPrice { + consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(u32::MAX), }, - gas_price: 3333333333333333,}, - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 1234, - context_id: 4321, - }), - }; + preferred_gas_price: 123, + }, + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }), + }; subject_addr .try_send(consuming_balances_and_qualified_payments.clone()) @@ -1493,20 +1494,17 @@ mod tests { let account_1 = make_payable_account(111_111); let account_2 = make_payable_account(222_222); let system = System::new("test"); - let consuming_balances_and_qualified_payments = - PayableScannerPaymentSetupMessage{ - qualified_payables: vec![], - current_stage_data: (), - response_skeleton_opt: None, - } - ConsumingWalletBalancesAndGasPrice { - qualified_payables: vec![account_1.clone(), account_2.clone()], + let consuming_balances_and_qualified_payments = PayablePaymentSetup { + qualified_payables: vec![account_1.clone(), account_2.clone()], + this_stage_data: ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(150_000_000_000_u64), }, - response_skeleton_opt: Some(response_skeleton), - }; + preferred_gas_price: 0, + }, + response_skeleton_opt: Some(response_skeleton), + }; subject_addr .try_send(consuming_balances_and_qualified_payments.clone()) diff --git a/node/src/accountant/payable_scan_setup_msgs.rs b/node/src/accountant/payable_scan_setup_msgs.rs index b901a6f61..874b12999 100644 --- a/node/src/accountant/payable_scan_setup_msgs.rs +++ b/node/src/accountant/payable_scan_setup_msgs.rs @@ -4,43 +4,42 @@ pub mod inter_actor_communication_for_payable_scanner { use crate::accountant::payable_dao::PayableAccount; use crate::accountant::ResponseSkeleton; + use crate::sub_lib::blockchain_bridge::{ + ConsumingWalletBalances, RequestBalancesToPayPayables, + }; use actix::Message; - use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, RequestBalancesToPayPayables}; #[derive(Debug, Message, PartialEq, Eq, Clone)] - pub struct PayableScannerPaymentSetupMessage{ + pub struct PayablePaymentSetup { //this field should stay private for anybody outside Accountant - pub (in crate::accountant) qualified_payables: Vec, - pub current_stage_data: T, - pub response_skeleton_opt: Option - } - - impl PayableScannerPaymentSetupMessage{ - pub fn qualified_payables(&self)->&[PayableAccount]{ - todo!() - } + pub(in crate::accountant) qualified_payables: Vec, + pub this_stage_data: T, + pub response_skeleton_opt: Option, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct ConsumingWalletBalancesAndGasPrice { pub consuming_wallet_balances: ConsumingWalletBalances, - pub gas_price: u64 + pub preferred_gas_price: u64, } - impl From> for Vec{ - fn from(_: PayableScannerPaymentSetupMessage) -> Self { - todo!() - } - } - - impl From<(RequestBalancesToPayPayables, ConsumingWalletBalancesAndGasPrice)> for PayableScannerPaymentSetupMessage{ - fn from((previous_msg, current_stage_data):(RequestBalancesToPayPayables, ConsumingWalletBalancesAndGasPrice)) -> Self { - PayableScannerPaymentSetupMessage{ + impl + From<( + RequestBalancesToPayPayables, + ConsumingWalletBalancesAndGasPrice, + )> for PayablePaymentSetup + { + fn from( + (previous_msg, current_stage_data): ( + RequestBalancesToPayPayables, + ConsumingWalletBalancesAndGasPrice, + ), + ) -> Self { + PayablePaymentSetup { qualified_payables: previous_msg.accounts, - current_stage_data, + this_stage_data: current_stage_data, response_skeleton_opt: previous_msg.response_skeleton_opt, } } } } - diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 6fe06e88f..68cfd6883 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,8 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::comma_joined_stringifiable; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::PayableAccount; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; @@ -20,13 +22,13 @@ lazy_static! { pub trait PaymentAdjuster { fn is_adjustment_required( &self, - msg: &PayableScannerPaymentSetupMessage, + msg: &PayablePaymentSetup, logger: &Logger, ) -> bool; fn adjust_payments( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions; @@ -39,12 +41,15 @@ pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { fn is_adjustment_required( &self, - msg: &PayableScannerPaymentSetupMessage, + msg: &PayablePaymentSetup, logger: &Logger, ) -> bool { - let qualified_payables = msg.qualified_payables(); + let qualified_payables = &msg.qualified_payables; let sum = Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); - let cw_masq_balance = msg.current_stage_data.consuming_wallet_balances.masq_tokens_wei; + let cw_masq_balance = msg + .this_stage_data + .consuming_wallet_balances + .masq_tokens_wei; if U256::from(sum) <= cw_masq_balance { false } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { @@ -58,12 +63,12 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { - let current_stage_data = msg.current_stage_data; - let qualified_payables: Vec = msg.into(); + let current_stage_data = msg.this_stage_data; + let qualified_payables: Vec = msg.qualified_payables; let debug_log_printer_opt = logger .debug_enabled() @@ -268,7 +273,7 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; - use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; + use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup}; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -293,19 +298,19 @@ mod tests { fn make_msg_with_q_payables_cw_balance_and_gas_price( qualified_payables_balances_gwei: Vec, masq_balance_gwei: u64, - ) -> PayableScannerPaymentSetupMessage { + ) -> PayablePaymentSetup { let qualified_payables = qualified_payables_balances_gwei .into_iter() .map(|balance| make_payable_account(balance)) .collect(); - PayableScannerPaymentSetupMessage{ + PayablePaymentSetup { qualified_payables, - current_stage_data: ConsumingWalletBalancesAndGasPrice { + this_stage_data: ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::zero(), masq_tokens_wei: gwei_to_wei(masq_balance_gwei), }, - gas_price: 0, + preferred_gas_price: 0, }, response_skeleton_opt: None, } @@ -437,15 +442,15 @@ mod tests { let accounts_sum: u128 = 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); - let msg = PayableScannerPaymentSetupMessage{ + let msg = PayablePaymentSetup { qualified_payables, - current_stage_data:ConsumingWalletBalancesAndGasPrice{ + this_stage_data: ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(150), - masq_tokens_wei: consuming_wallet_masq_balance, - + gas_currency_wei: U256::from(150), + masq_tokens_wei: consuming_wallet_masq_balance, + }, + preferred_gas_price: 222222222222222222, }, - gas_price: 222222222222222222}, response_skeleton_opt: None, }; diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 32bff5635..e50f97bf2 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -1,6 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; @@ -18,12 +20,15 @@ where pub trait PayableScannerMidProcedures { fn mid_procedure_soft( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, logger: &Logger, - ) -> Either>; + ) -> Either< + OutcomingPayamentsInstructions, + PayablePaymentSetup, + >; fn mid_procedure_hard( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, logger: &Logger, ) -> OutcomingPayamentsInstructions; } diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index ef25977d8..01d87bdba 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -1,7 +1,9 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable}; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; @@ -258,12 +260,15 @@ impl PayableScannerWithMidProcedures impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_soft( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, logger: &Logger, - ) -> Either> { + ) -> Either< + OutcomingPayamentsInstructions, + PayablePaymentSetup, + > { if !self.payment_adjuster.is_adjustment_required(&msg, logger) { Either::Left(OutcomingPayamentsInstructions { - accounts: msg.into(), + accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, }) } else { @@ -273,7 +278,7 @@ impl PayableScannerMidProcedures for PayableScanner { fn mid_procedure_hard( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, logger: &Logger, ) -> OutcomingPayamentsInstructions { let now = SystemTime::now(); diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 8fa87346d..15ea0546b 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -3,10 +3,12 @@ #![cfg(test)] use crate::accountant::dao_utils::{from_time_t, to_time_t, CustomQuery}; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayableScannerPaymentSetupMessage}; use crate::accountant::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use crate::accountant::payment_adjuster::PaymentAdjuster; use crate::accountant::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoFactory, @@ -1378,17 +1380,24 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - is_adjustment_required_params: Arc>>>, + is_adjustment_required_params: + Arc>>>, is_adjustment_required_results: RefCell>, - adjust_payments_params: - Arc, SystemTime)>>>, + adjust_payments_params: Arc< + Mutex< + Vec<( + PayablePaymentSetup, + SystemTime, + )>, + >, + >, adjust_payments_results: RefCell>, } impl PaymentAdjuster for PaymentAdjusterMock { fn is_adjustment_required( &self, - msg: &PayableScannerPaymentSetupMessage, + msg: &PayablePaymentSetup, logger: &Logger, ) -> bool { self.is_adjustment_required_params @@ -1400,7 +1409,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, - msg: PayableScannerPaymentSetupMessage, + msg: PayablePaymentSetup, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { @@ -1412,7 +1421,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn is_adjustment_required_params( mut self, - params: &Arc>>>, + params: &Arc>>>, ) -> Self { self.is_adjustment_required_params = params.clone(); self @@ -1427,7 +1436,14 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc, SystemTime)>>>, + params: &Arc< + Mutex< + Vec<( + PayablePaymentSetup, + SystemTime, + )>, + >, + >, ) -> Self { self.adjust_payments_params = params.clone(); self diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index c20e54a54..51330bb93 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,6 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{PayableScannerPaymentSetupMessage, ConsumingWalletBalancesAndGasPrice}; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ + ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, +}; use crate::accountant::{ ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, }; @@ -35,7 +37,7 @@ use masq_lib::ui_gateway::NodeFromUiMessage; use std::path::PathBuf; use std::time::SystemTime; use web3::transports::Http; -use web3::types::{TransactionReceipt, H256}; +use web3::types::{TransactionReceipt, H256, U256}; use web3::Transport; pub const CRASH_KEY: &str = "BLOCKCHAINBRIDGE"; @@ -47,7 +49,8 @@ pub struct BlockchainBridge { persistent_config: Box, set_consuming_wallet_subs_opt: Option>>, sent_payable_subs_opt: Option>, - balances_and_payables_sub_opt: Option>>, + balances_and_payables_sub_opt: + Option>>, received_payments_subs_opt: Option>, scan_error_subs_opt: Option>, crashable: bool, @@ -299,10 +302,19 @@ impl BlockchainBridge { masq_tokens_wei: token_balance, } }; - let msg: PayableScannerPaymentSetupMessage = (msg, ConsumingWalletBalancesAndGasPrice{ + let preferred_gas_price = self + .persistent_config + .gas_price() + .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; + + let this_stage_data = ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances, - gas_price: u64::MAX - }).into(); + preferred_gas_price, + }; + + let msg: PayablePaymentSetup = + (msg, this_stage_data).into(); + self.balances_and_payables_sub_opt .as_ref() .expect("Accountant is unbound") @@ -669,7 +681,8 @@ mod tests { .get_token_balance_params(&get_token_balance_params_arc) .get_token_balance_result(Ok(token_balance)); let consuming_wallet = make_paying_wallet(b"somewallet"); - let persistent_configuration = PersistentConfigurationMock::default(); + let persistent_configuration = + PersistentConfigurationMock::default().gas_price_result(Ok(146)); let qualified_accounts = vec![PayableAccount { wallet: make_wallet("booga"), balance_wei: 78_654_321, @@ -706,19 +719,24 @@ mod tests { assert_eq!(*get_token_balance_params, vec![consuming_wallet]); let accountant_received_payment = accountant_recording_arc.lock().unwrap(); assert_eq!(accountant_received_payment.len(), 1); - let reported_balances_and_qualified_accounts: &PayableScannerPaymentSetupMessage = - accountant_received_payment.get_record(0); - let expected_msg: PayableScannerPaymentSetupMessage = (RequestBalancesToPayPayables{ accounts: qualified_accounts, response_skeleton_opt: Some(ResponseSkeleton { - client_id: 11122, - context_id: 444 - }) },ConsumingWalletBalancesAndGasPrice { - consuming_wallet_balances: wallet_balances_found, - gas_price: 11111111111111111111111111 - } ).into(); - assert_eq!( - reported_balances_and_qualified_accounts, - &expected_msg - ); + let reported_balances_and_qualified_accounts: &PayablePaymentSetup< + ConsumingWalletBalancesAndGasPrice, + > = accountant_received_payment.get_record(0); + let expected_msg: PayablePaymentSetup = ( + RequestBalancesToPayPayables { + accounts: qualified_accounts, + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 11122, + context_id: 444, + }), + }, + ConsumingWalletBalancesAndGasPrice { + consuming_wallet_balances: wallet_balances_found, + preferred_gas_price: 146, + }, + ) + .into(); + assert_eq!(reported_balances_and_qualified_accounts, &expected_msg); } fn assert_failure_during_balance_inspection( @@ -838,6 +856,42 @@ mod tests { ) } + #[test] + fn handle_request_balances_to_pay_payables_fails_on_gas_price_query() { + let blockchain_interface = BlockchainInterfaceMock::default() + .get_gas_balance_result(Ok(U256::from(456789))) + .get_token_balance_result(Ok(U256::from(7890123456_u64))); + let persistent_configuration = PersistentConfigurationMock::default().gas_price_result( + Err(PersistentConfigError::DatabaseError("siesta".to_string())), + ); + let consuming_wallet = make_wallet("our wallet"); + let mut subject = BlockchainBridge::new( + Box::new(blockchain_interface), + Box::new(persistent_configuration), + false, + Some(consuming_wallet), + ); + let request = RequestBalancesToPayPayables { + accounts: vec![PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 123456, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }], + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 123, + context_id: 222, + }), + }; + + let result = subject.handle_request_balances_to_pay_payables(request); + + assert_eq!( + result, + Err("Couldn't query the gas price: DatabaseError(\"siesta\")".to_string()) + ) + } + #[test] fn handle_report_accounts_payable_transacts_and_sends_finished_payments_back_to_accountant() { let system = diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 20cb4880d..4b8cdb83d 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,13 +1,16 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; +use crate::accountant; use crate::accountant::payable_dao::PayableDaoFactory; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; use crate::accountant::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::receivable_dao::ReceivableDaoFactory; use crate::accountant::{ - Accountant, checked_conversion, ReceivedPayments, ReportTransactionReceipts, ScanError, + checked_conversion, Accountant, ReceivedPayments, ReportTransactionReceipts, ScanError, SentPayables, }; use crate::banned_dao::BannedDaoFactory; +use crate::blockchain::blockchain_bridge; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::wallet::Wallet; @@ -21,9 +24,6 @@ use std::fmt::{Debug, Formatter}; use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; use std::time::{Duration, SystemTime}; -use crate::accountant; -use crate::blockchain::blockchain_bridge; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayableScannerPaymentSetupMessage; lazy_static! { pub static ref DEFAULT_EARNING_WALLET: Wallet = Wallet::from_str("0x27d9A2AC83b493f88ce9B4532EDcf74e95B9788d").expect("Internal error"); @@ -99,7 +99,7 @@ pub struct AccountantSubs { pub report_exit_service_provided: Recipient, pub report_services_consumed: Recipient, pub report_consuming_wallet_balances_and_qualified_payables: - Recipient>, + Recipient>, pub report_inbound_payments: Recipient, pub init_pending_payable_fingerprints: Recipient, pub report_transaction_receipts: Recipient, @@ -199,11 +199,11 @@ impl MessageIdGenerator for MessageIdGeneratorReal { #[cfg(test)] mod tests { use crate::accountant::test_utils::AccountantBuilder; - use crate::accountant::{Accountant, checked_conversion}; + use crate::accountant::{checked_conversion, Accountant}; use crate::sub_lib::accountant::{ - AccountantSubsFactory, AccountantSubsFactoryReal, DEFAULT_EARNING_WALLET, - DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, MessageIdGenerator, MessageIdGeneratorReal, - MSG_ID_INCREMENTER, PaymentThresholds, ScanIntervals, + AccountantSubsFactory, AccountantSubsFactoryReal, MessageIdGenerator, + MessageIdGeneratorReal, PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET, + DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, MSG_ID_INCREMENTER, TEMPORARY_CONSUMING_WALLET, }; use crate::sub_lib::wallet::Wallet; diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index f28e3ed19..8dbfc8c6c 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, @@ -41,7 +42,6 @@ use crate::sub_lib::proxy_server::ProxyServerSubs; use crate::sub_lib::proxy_server::{ AddReturnRouteMessage, AddRouteMessage, ClientRequestPayload_0v1, }; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayableScannerPaymentSetupMessage; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; @@ -128,7 +128,7 @@ recorder_message_handler!(ReportServicesConsumedMessage); recorder_message_handler!(ReportExitServiceProvidedMessage); recorder_message_handler!(ReportRoutingServiceProvidedMessage); recorder_message_handler!(ScanError); -recorder_message_handler!(PayableScannerPaymentSetupMessage); +recorder_message_handler!(PayablePaymentSetup); recorder_message_handler!(SentPayables); recorder_message_handler!(SetConsumingWalletMessage); recorder_message_handler!(RequestBalancesToPayPayables); @@ -427,7 +427,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayableScannerPaymentSetupMessage + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), From b076beef1d83d49183fec935f3336cb318f9b6c5 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 26 May 2023 22:51:42 +0200 Subject: [PATCH 014/250] GH-672: decent debug log for adjusted payables --- node/src/accountant/mod.rs | 2 +- node/src/accountant/payment_adjuster.rs | 120 ++++++++++++++--------- node/src/blockchain/blockchain_bridge.rs | 2 +- node/src/sub_lib/accountant.rs | 2 - 4 files changed, 73 insertions(+), 53 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 1863d1cff..f71088eb0 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -31,7 +31,7 @@ use crate::accountant::dao_utils::{ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; -use crate::accountant::payable_dao::{PayableAccount, PayableDaoError}; +use crate::accountant::payable_dao::PayableDaoError; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDaoError; use crate::accountant::scanners::{ScanTimings, Scanners}; diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 68cfd6883..22169c7dd 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -72,7 +72,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let debug_log_printer_opt = logger .debug_enabled() - .then_some(Self::before_and_after_debug_msg_printer( + .then_some(Self::before_and_after_debug_msg_formatter( &qualified_payables, )); @@ -214,20 +214,61 @@ impl PaymentAdjusterReal { .collect() } - fn format_brief_accounts_summary(accounts: &[PayableAccount]) -> String { - todo!() + fn format_brief_accounts_summary( + original_accounts: impl Iterator, + adjusted_accounts: impl Iterator, + ) -> String { + original_accounts + .zip(adjusted_accounts) + .map(|(original, adjusted)| format!("{}\n{}", original, adjusted)) + .join("\n") } - fn before_and_after_debug_msg_printer( - before: &[PayableAccount], - ) -> impl Fn(&[PayableAccount]) -> String { - let accounts_before = Self::format_brief_accounts_summary(before); - move |adjusted_balances: &[PayableAccount]| { + fn prefabricated_formatted_accounts<'a>( + accounts: &'a [PayableAccount], + display_wallet: bool, + ) -> impl Iterator + 'a { + accounts.iter().map(move |account| { + let wallet_opt = if display_wallet { + Some(account.wallet.to_string()) + } else { + None + }; format!( - "Original payable accounts {}, accounts after the adjustment {}", - accounts_before, - Self::format_brief_accounts_summary(adjusted_balances) + "{:<42} {}", + wallet_opt.as_ref().map(|w| w.as_str()).unwrap_or(""), + account.balance_wei ) + }) + } + + fn before_and_after_debug_msg_formatter( + original: &[PayableAccount], + ) -> impl FnOnce(&[PayableAccount]) -> String { + let original_prefabricated = + Self::prefabricated_formatted_accounts(original, true).collect::>(); + move |adjusted_accounts: &[PayableAccount]| { + let prefabricated_adjusted = + Self::prefabricated_formatted_accounts(adjusted_accounts, false); + format!( + "\nAdjusted payables:\n\ + {:<42} {}\n\ + {: <42} {}\n\ + {: <42} {}\n\ + \n\ + {}", + "Account wallet", + "Balance wei", + "", + "Original", + "", + "Adjusted", + Self::format_brief_accounts_summary( + original_prefabricated.into_iter(), + prefabricated_adjusted + ) + ) + //TODO mention accounts that will be excluded completely } } @@ -267,7 +308,6 @@ mod tests { ConsumingWalletBalances, OutcomingPayamentsInstructions, }; use crate::test_utils::make_wallet; - use itertools::Itertools; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; @@ -343,7 +383,7 @@ mod tests { 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make \ total of 101,000,000,000 wei while the consuming wallet holds only 100,000,000,000 wei. \ Going to adjust them to fit in the limit, by cutting back the number of payments or their \ - size..")); + size.")); } #[test] @@ -418,6 +458,8 @@ mod tests { #[test] fn adjust_payments_works() { + init_test_logging(); + let test_name = "adjust_payments_works"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -454,7 +496,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(msg, now, &Logger::new("test")); + let result = subject.adjust_payments(msg, now, &Logger::new(test_name)); let expected_criteria_computation_output = { let time_criteria = vec![ @@ -514,21 +556,21 @@ mod tests { response_skeleton_opt: None } ); - - // Example of the current adjustment; - // printed with `visual_check_balance_before_after(.., ..)` - // - // BEFORE - // AFTER - // - // 444444444444444444 - // 333000000000000051 - // --- - // 666666666666000000000000 - // 665999999999334000000004 - // --- - // 22000000000000 - // 12820500003284 + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Adjusted payables: +|Account wallet Balance wei +| Original +| Adjusted +| +|0x0000000000000000000000000000000000616263 444444444444444444 +| 333000000000000051 +|0x0000000000000000000000000000000000646566 666666666666000000000000 +| 665999999999334000000004 +|0x000000000000000000000000000000000067686b 22000000000000 +| 12820500003284" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { @@ -539,24 +581,4 @@ mod tests { fn output_with_response_skeleton_opt_some() { todo!("rather include into some other special test??") } - - #[allow(dead_code)] - fn visual_check_balance_before_after( - accounts_before: &[PayableAccount], - accounts_after: &[PayableAccount], - ) { - let sorting = |a: &&PayableAccount, b: &&PayableAccount| { - Ord::cmp(&a.wallet.to_string(), &b.wallet.to_string()) - }; - accounts_before - .into_iter() - .sorted_by(sorting) - .zip(accounts_after.into_iter().sorted_by(sorting)) - .for_each(|(original_payable, adjusted_payable)| { - eprintln!( - "{}\n{}\n---", - original_payable.balance_wei, adjusted_payable.balance_wei - ) - }) - } } diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 51330bb93..03ec3b8ee 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -37,7 +37,7 @@ use masq_lib::ui_gateway::NodeFromUiMessage; use std::path::PathBuf; use std::time::SystemTime; use web3::transports::Http; -use web3::types::{TransactionReceipt, H256, U256}; +use web3::types::{TransactionReceipt, H256}; use web3::Transport; pub const CRASH_KEY: &str = "BLOCKCHAINBRIDGE"; diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 4b8cdb83d..d48d3f702 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,5 +1,4 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant; use crate::accountant::payable_dao::PayableDaoFactory; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; @@ -10,7 +9,6 @@ use crate::accountant::{ SentPayables, }; use crate::banned_dao::BannedDaoFactory; -use crate::blockchain::blockchain_bridge; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::wallet::Wallet; From 58db009c62ca61f1ebae14f793e5433fa694b673 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 27 May 2023 16:04:02 +0200 Subject: [PATCH 015/250] GH-672: broadening the adjustment capabilities with gas; rough framework --- node/src/accountant/mod.rs | 51 ++++--- .../src/accountant/payable_scan_setup_msgs.rs | 2 +- node/src/accountant/payment_adjuster.rs | 127 +++++++++++++----- node/src/accountant/scan_mid_procedures.rs | 28 +++- node/src/accountant/scanners.rs | 33 ++--- node/src/accountant/test_utils.rs | 17 +-- node/src/blockchain/blockchain_bridge.rs | 6 +- 7 files changed, 178 insertions(+), 86 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f71088eb0..355c90e82 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -222,20 +222,8 @@ impl Handler> for Accoun msg: PayablePaymentSetup, _ctx: &mut Self::Context, ) -> Self::Result { - let instructions = match self.scanners.payable.mid_procedure_soft(msg, &self.logger) { - Either::Left(finalized_msg) => finalized_msg, - Either::Right(unaccepted_msg) => { - //TODO we will eventually query info from Neighborhood here - self.scanners - .payable - .mid_procedure_hard(unaccepted_msg, &self.logger) - } - }; - self.outcoming_payments_instructions_sub_opt - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(instructions) - .expect("BlockchainBridge is dead") + self.handle_payable_payment_setup(msg); + todo!("send msg to UIGateway...") } } @@ -258,6 +246,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_payable(msg.response_skeleton_opt); + //TODO handling error with msg to the UI is missing! self.scan_timings .payable .schedule_another_periodic_scan(ctx); @@ -269,6 +258,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPendingPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_pending_payable(msg.response_skeleton_opt); + //TODO handling error with msg to the UI is missing! self.scan_timings .pending_payable .schedule_another_periodic_scan(ctx); @@ -280,6 +270,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); + //TODO handling error with msg to the UI is missing! self.scan_timings .receivable .schedule_another_periodic_scan(ctx); @@ -674,6 +665,26 @@ impl Accountant { }) } + fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup)-> Option{ + let bb_instructions = match self.scanners.payable.process_softly(msg, &self.logger) { + Ok(Either::Left(finalized_msg)) => finalized_msg, + Ok(Either::Right(unaccepted_msg)) => { + //TODO we will eventually query info from Neighborhood here + self.scanners + .payable + .process_with_adjustment(unaccepted_msg, &self.logger) + } + Err(e) => todo!() + }; + self.outcoming_payments_instructions_sub_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(bb_instructions) + .expect("BlockchainBridge is dead"); + + todo!() + } + fn handle_financials(&self, msg: &UiFinancialsRequest, client_id: u64, context_id: u64) { let body: MessageBody = self.compute_financials(msg, context_id); self.ui_message_sub_opt @@ -1077,6 +1088,8 @@ mod tests { use std::time::Duration; use std::vec; use web3::types::{TransactionReceipt, U256}; + use crate::accountant::payment_adjuster::Adjustment; + use crate::accountant::scan_mid_procedures::AwaitingAdjustment; impl Handler> for Accountant { type Result = (); @@ -1400,7 +1413,7 @@ mod tests { let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() .is_adjustment_required_params(&is_adjustment_required_params_arc) - .is_adjustment_required_result(false); + .is_adjustment_required_result(Ok(None)); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -1417,7 +1430,7 @@ mod tests { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(u32::MAX), }, - preferred_gas_price: 123, + desired_gas_price: 123, }, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1482,7 +1495,7 @@ mod tests { response_skeleton_opt: Some(response_skeleton), }; let payment_adjuster = PaymentAdjusterMock::default() - .is_adjustment_required_result(true) + .is_adjustment_required_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(adjusted_payments_instructions); let payable_scanner = PayableScannerBuilder::new() @@ -1501,7 +1514,7 @@ mod tests { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(150_000_000_000_u64), }, - preferred_gas_price: 0, + desired_gas_price: 0, }, response_skeleton_opt: Some(response_skeleton), }; @@ -1515,7 +1528,7 @@ mod tests { let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); - assert_eq!(cwbqp_msg, consuming_balances_and_qualified_payments); + assert_eq!(cwbqp_msg, AwaitingAdjustment{original_msg: consuming_balances_and_qualified_payments, adjustment: Adjustment::MasqToken}); assert!(before <= captured_now && captured_now <= after); assert!(adjust_payments_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); diff --git a/node/src/accountant/payable_scan_setup_msgs.rs b/node/src/accountant/payable_scan_setup_msgs.rs index 874b12999..16d0d9ffa 100644 --- a/node/src/accountant/payable_scan_setup_msgs.rs +++ b/node/src/accountant/payable_scan_setup_msgs.rs @@ -20,7 +20,7 @@ pub mod inter_actor_communication_for_payable_scanner { #[derive(Debug, PartialEq, Eq, Clone)] pub struct ConsumingWalletBalancesAndGasPrice { pub consuming_wallet_balances: ConsumingWalletBalances, - pub preferred_gas_price: u64, + pub desired_gas_price: u64, } impl diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 22169c7dd..6410e717e 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,10 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::comma_joined_stringifiable; use crate::accountant::payable_dao::PayableAccount; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, }; +use crate::accountant::{comma_joined_stringifiable, gwei_to_wei}; use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; @@ -14,6 +14,7 @@ use std::iter::{once, successors}; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +use crate::accountant::scan_mid_procedures::AwaitingAdjustment; lazy_static! { static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); @@ -24,11 +25,11 @@ pub trait PaymentAdjuster { &self, msg: &PayablePaymentSetup, logger: &Logger, - ) -> bool; + ) -> Result, AnalysisError>; fn adjust_payments( &self, - msg: PayablePaymentSetup, + setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions; @@ -43,14 +44,15 @@ impl PaymentAdjuster for PaymentAdjusterReal { &self, msg: &PayablePaymentSetup, logger: &Logger, - ) -> bool { - let qualified_payables = &msg.qualified_payables; + ) -> Result, AnalysisError> { + let qualified_payables = msg.qualified_payables.as_slice(); let sum = Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); let cw_masq_balance = msg .this_stage_data .consuming_wallet_balances .masq_tokens_wei; - if U256::from(sum) <= cw_masq_balance { + + let required_by_masq_token = if U256::from(sum) <= cw_masq_balance { false } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { todo!() @@ -58,15 +60,33 @@ impl PaymentAdjuster for PaymentAdjusterReal { Self::log_adjustment_required(logger, qualified_payables, sum, cw_masq_balance); true - } + }; + + let total_gas_required = gwei_to_wei::( + qualified_payables.len() as u64 * msg.this_stage_data.desired_gas_price, + ); + eprintln!("total gas required {}", total_gas_required); + let required_by_gas = if total_gas_required + <= msg + .this_stage_data + .consuming_wallet_balances + .gas_currency_wei + { + //TODO drive in both < and = + false + } else { + todo!() + }; + todo!() } fn adjust_payments( &self, - msg: PayablePaymentSetup, + setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { + let msg = setup.original_msg; let current_stage_data = msg.this_stage_data; let qualified_payables: Vec = msg.qualified_payables; let debug_log_printer_opt = @@ -296,13 +316,21 @@ fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } +#[derive(Debug, PartialEq, Eq)] +pub enum Adjustment { + MasqToken, + Gas, + Both, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum AnalysisError {} + #[cfg(test)] mod tests { use crate::accountant::gwei_to_wei; use crate::accountant::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::{ - log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, - }; + use crate::accountant::payment_adjuster::{log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, Adjustment}; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPayamentsInstructions, @@ -314,6 +342,7 @@ mod tests { use std::vec; use web3::types::U256; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup}; + use crate::accountant::scan_mid_procedures::AwaitingAdjustment; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -336,49 +365,80 @@ mod tests { } fn make_msg_with_q_payables_cw_balance_and_gas_price( - qualified_payables_balances_gwei: Vec, - masq_balance_gwei: u64, + q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, + gas_price_opt: Option, ) -> PayablePaymentSetup { - let qualified_payables = qualified_payables_balances_gwei - .into_iter() - .map(|balance| make_payable_account(balance)) - .collect(); + let (qualified_payables_gwei, consuming_wallet_masq_gwei) = + q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); + + let (desired_gas_price, number_of_payments, cw_balance_gas_gwei) = match gas_price_opt { + Some(conditions) => ( + conditions.desired_gas_price_gwei, + conditions.number_of_payments, + conditions.consuming_wallet_masq_gwei, + ), + None => (120, qualified_payables_gwei.len(), u64::MAX), + }; + + let qualified_payables = match number_of_payments != qualified_payables_gwei.len() { + true => (0..number_of_payments) + .map(|idx| make_payable_account(idx as u64)) + .collect(), + false => qualified_payables_gwei + .into_iter() + .map(|balance| make_payable_account(balance)) + .collect(), + }; PayablePaymentSetup { qualified_payables, this_stage_data: ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::zero(), - masq_tokens_wei: gwei_to_wei(masq_balance_gwei), + gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), + masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), }, - preferred_gas_price: 0, + desired_gas_price: gwei_to_wei(desired_gas_price), }, response_skeleton_opt: None, } } + struct GasTestConditions { + desired_gas_price_gwei: u64, + number_of_payments: usize, + consuming_wallet_masq_gwei: u64, + } + #[test] - fn is_adjustment_required_works_for_negative_answer() { + fn is_adjustment_required_works_for_negative_answer_because_of_masq_token() { init_test_logging(); - let test_name = "is_adjustment_required_works_for_negative_answer"; + let test_name = "is_adjustment_required_works_for_negative_answer_because_of_masq_token"; let subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); - let msg_1 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 14], 100); - let msg_2 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 15], 100); + let msg_1 = + make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 14], 100)), None); + let msg_2 = + make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 15], 100)), None); - assert_eq!(subject.is_adjustment_required(&msg_1, &logger), false); - assert_eq!(subject.is_adjustment_required(&msg_2, &logger), false); + let result_1 = subject.is_adjustment_required(&msg_1, &logger); + let result_2 = subject.is_adjustment_required(&msg_2, &logger); + + assert_eq!(result_1, Ok(None)); + assert_eq!(result_2, Ok(None)); TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); } #[test] - fn is_adjustment_required_works_for_positive_answer() { + fn is_adjustment_required_works_for_positive_answer_because_of_masq_token() { init_test_logging(); - let test_name = "is_adjustment_required_works_for_positive_answer"; + let test_name = "is_adjustment_required_works_for_positive_answer_because_of_masq_token"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); - let msg_3 = make_msg_with_q_payables_cw_balance_and_gas_price(vec![85, 16], 100); + let msg = + make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 16], 100)), None); + + let result = subject.is_adjustment_required(&msg, &logger); - assert_eq!(subject.is_adjustment_required(&msg_3, &logger), true); + assert_eq!(result, Ok(Some(Adjustment::MasqToken))); TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make \ total of 101,000,000,000 wei while the consuming wallet holds only 100,000,000,000 wei. \ @@ -484,19 +544,20 @@ mod tests { let accounts_sum: u128 = 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); - let msg = PayablePaymentSetup { + let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data: ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(150), masq_tokens_wei: consuming_wallet_masq_balance, }, - preferred_gas_price: 222222222222222222, + desired_gas_price: 222222222222222222, }, response_skeleton_opt: None, }; + let adjustment_setup = AwaitingAdjustment{ original_msg: setup_msg, adjustment: Adjustment::MasqToken }; //TODO what to do with the required adjustment? - let result = subject.adjust_payments(msg, now, &Logger::new(test_name)); + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); let expected_criteria_computation_output = { let time_criteria = vec![ diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index e50f97bf2..39e4cab60 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -8,6 +8,7 @@ use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; use actix::Message; use itertools::Either; use masq_lib::logger::Logger; +use crate::accountant::payment_adjuster::{Adjustment}; pub trait PayableScannerWithMidProcedures: Scanner + PayableScannerMidProcedures @@ -18,17 +19,32 @@ where } pub trait PayableScannerMidProcedures { - fn mid_procedure_soft( + fn process_softly( &self, msg: PayablePaymentSetup, logger: &Logger, - ) -> Either< - OutcomingPayamentsInstructions, - PayablePaymentSetup, + ) -> Result< + Either< + OutcomingPayamentsInstructions, + AwaitingAdjustment, + >, + String, >; - fn mid_procedure_hard( + fn process_with_adjustment( &self, - msg: PayablePaymentSetup, + setup: AwaitingAdjustment, logger: &Logger, ) -> OutcomingPayamentsInstructions; } + +#[derive(Debug, PartialEq, Eq)] +pub struct AwaitingAdjustment{ + pub original_msg: PayablePaymentSetup, + pub adjustment: Adjustment +} + +impl AwaitingAdjustment{ + pub fn new(original_msg: PayablePaymentSetup, adjustment: Adjustment)->Self{ + todo!() + } +} \ No newline at end of file diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 01d87bdba..bbb9e0cd6 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -4,12 +4,10 @@ use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable} use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, }; -use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; +use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; -use crate::accountant::scan_mid_procedures::{ - PayableScannerMidProcedures, PayableScannerWithMidProcedures, -}; +use crate::accountant::scan_mid_procedures::{AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; @@ -258,31 +256,34 @@ impl PayableScannerWithMidProcedures } impl PayableScannerMidProcedures for PayableScanner { - fn mid_procedure_soft( + fn process_softly( &self, msg: PayablePaymentSetup, logger: &Logger, - ) -> Either< - OutcomingPayamentsInstructions, - PayablePaymentSetup, + ) -> Result< + Either< + OutcomingPayamentsInstructions, + AwaitingAdjustment, + >, + String, > { - if !self.payment_adjuster.is_adjustment_required(&msg, logger) { - Either::Left(OutcomingPayamentsInstructions { + match self.payment_adjuster.is_adjustment_required(&msg, logger) { + Ok(None) => Ok(Either::Left(OutcomingPayamentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, - }) - } else { - Either::Right(msg) + })), + Ok(Some(adjustment)) => Ok(Either::Right(AwaitingAdjustment::new(msg, adjustment))), + Err(e) => todo!(), } } - fn mid_procedure_hard( + fn process_with_adjustment( &self, - msg: PayablePaymentSetup, + setup: AwaitingAdjustment, logger: &Logger, ) -> OutcomingPayamentsInstructions { let now = SystemTime::now(); - self.payment_adjuster.adjust_payments(msg, now, logger) + self.payment_adjuster.adjust_payments(setup, now, logger) } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 15ea0546b..44ccb35fa 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -9,7 +9,7 @@ use crate::accountant::payable_dao::{ use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, }; -use crate::accountant::payment_adjuster::PaymentAdjuster; +use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoFactory, }; @@ -43,6 +43,7 @@ use std::fmt::Debug; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::SystemTime; +use crate::accountant::scan_mid_procedures::AwaitingAdjustment; pub fn make_receivable_account(n: u64, expected_delinquent: bool) -> ReceivableAccount { let now = to_time_t(SystemTime::now()); @@ -1382,11 +1383,11 @@ impl PayableThresholdsGaugeMock { pub struct PaymentAdjusterMock { is_adjustment_required_params: Arc>>>, - is_adjustment_required_results: RefCell>, + is_adjustment_required_results: RefCell,AnalysisError>>>, adjust_payments_params: Arc< Mutex< Vec<( - PayablePaymentSetup, + AwaitingAdjustment, SystemTime, )>, >, @@ -1399,7 +1400,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { &self, msg: &PayablePaymentSetup, logger: &Logger, - ) -> bool { + ) -> Result, AnalysisError> { self.is_adjustment_required_params .lock() .unwrap() @@ -1409,11 +1410,11 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, - msg: PayablePaymentSetup, + setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, ) -> OutcomingPayamentsInstructions { - self.adjust_payments_params.lock().unwrap().push((msg, now)); + self.adjust_payments_params.lock().unwrap().push((setup, now)); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1427,7 +1428,7 @@ impl PaymentAdjusterMock { self } - pub fn is_adjustment_required_result(self, result: bool) -> Self { + pub fn is_adjustment_required_result(self, result: Result,AnalysisError>) -> Self { self.is_adjustment_required_results .borrow_mut() .push(result); @@ -1439,7 +1440,7 @@ impl PaymentAdjusterMock { params: &Arc< Mutex< Vec<( - PayablePaymentSetup, + AwaitingAdjustment, SystemTime, )>, >, diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 03ec3b8ee..970cca31e 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -302,14 +302,14 @@ impl BlockchainBridge { masq_tokens_wei: token_balance, } }; - let preferred_gas_price = self + let desired_gas_price = self .persistent_config .gas_price() .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; let this_stage_data = ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances, - preferred_gas_price, + desired_gas_price, }; let msg: PayablePaymentSetup = @@ -732,7 +732,7 @@ mod tests { }, ConsumingWalletBalancesAndGasPrice { consuming_wallet_balances: wallet_balances_found, - preferred_gas_price: 146, + desired_gas_price: 146, }, ) .into(); From 8f5c60416386c591d62a1af9762731ce1910876d Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 27 May 2023 23:33:59 +0200 Subject: [PATCH 016/250] GH-672: savepoint; before experimenting with early gas_limit computation --- node/src/accountant/payment_adjuster.rs | 65 ++++++++++++++++++++----- node/src/accountant/scanners.rs | 2 +- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 6410e717e..301f2cfd0 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -66,6 +66,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { qualified_payables.len() as u64 * msg.this_stage_data.desired_gas_price, ); eprintln!("total gas required {}", total_gas_required); + eprintln!("cons balance {:?}", msg.this_stage_data.consuming_wallet_balances.gas_currency_wei); let required_by_gas = if total_gas_required <= msg .this_stage_data @@ -77,7 +78,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { } else { todo!() }; - todo!() + + match (required_by_masq_token, required_by_gas){ + (false, false) => Ok(None), + (true, false) => Ok(Some(Adjustment::MasqToken)), + (false, true) => todo!(), + (true, true) => todo!() + } } fn adjust_payments( @@ -396,7 +403,7 @@ mod tests { gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), }, - desired_gas_price: gwei_to_wei(desired_gas_price), + desired_gas_price, }, response_skeleton_opt: None, } @@ -409,28 +416,41 @@ mod tests { } #[test] - fn is_adjustment_required_works_for_negative_answer_because_of_masq_token() { + fn is_adjustment_required_negative_answer() { init_test_logging(); - let test_name = "is_adjustment_required_works_for_negative_answer_because_of_masq_token"; + let test_name = "is_adjustment_required_negative_answer"; let subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); + //masq balance > payments let msg_1 = make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 14], 100)), None); + //masq balance = payments let msg_2 = make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 15], 100)), None); + //gas balance > payments + let msg_3 = + make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + desired_gas_price_gwei: 111, + number_of_payments: 5, + consuming_wallet_masq_gwei: 556, + })); + //gas balance = payments + let msg_4 = + make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + desired_gas_price_gwei: 100, + number_of_payments: 6, + consuming_wallet_masq_gwei: 600, + })); + + [msg_1,msg_2,msg_3,msg_4].into_iter().for_each(|msg| assert_eq!(subject.is_adjustment_required(&msg, &logger), Ok(None), "failed for msg {:?}", msg)); - let result_1 = subject.is_adjustment_required(&msg_1, &logger); - let result_2 = subject.is_adjustment_required(&msg_2, &logger); - - assert_eq!(result_1, Ok(None)); - assert_eq!(result_2, Ok(None)); TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); } #[test] - fn is_adjustment_required_works_for_positive_answer_because_of_masq_token() { + fn is_adjustment_required_positive_for_masq_token() { init_test_logging(); - let test_name = "is_adjustment_required_works_for_positive_answer_because_of_masq_token"; + let test_name = "is_adjustment_required_positive_for_masq_token"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); let msg = @@ -446,6 +466,29 @@ mod tests { size.")); } + #[test] + fn is_adjustment_required_positive_for_gas() { + init_test_logging(); + let test_name = "is_adjustment_required_positive_for_gas"; + let logger = Logger::new(test_name); + let subject = PaymentAdjusterReal::new(); + let msg = + make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + desired_gas_price_gwei: 100, + number_of_payments: 3, + consuming_wallet_masq_gwei: 299, + })); + + let result = subject.is_adjustment_required(&msg, &logger); + + assert_eq!(result, Ok(Some(Adjustment::MasqToken))); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ + 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 would \ + require 100 gwei wei while the consuming wallet holds only 100,000,000,000 wei. \ + Going to adjust them to fit in the limit, by cutting back the number of payments or their \ + size.")); + } + #[test] fn find_smallest_debt_works() { let mut payable_1 = make_payable_account(111); diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index bbb9e0cd6..3885c2c1f 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -4,7 +4,7 @@ use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable} use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, }; -use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; +use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; use crate::accountant::scan_mid_procedures::{AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures}; From 557c51f6f1a8a625ca4482d537570134ae9cac7e Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 29 May 2023 23:27:31 +0200 Subject: [PATCH 017/250] GH-672: continuing in implementation of the check for gas availability. --- node/src/accountant/mod.rs | 61 +++-- .../src/accountant/payable_scan_setup_msgs.rs | 11 +- node/src/accountant/payment_adjuster.rs | 244 ++++++++++++------ node/src/accountant/scan_mid_procedures.rs | 33 ++- node/src/accountant/scanners.rs | 22 +- node/src/accountant/test_utils.rs | 48 ++-- node/src/blockchain/blockchain_bridge.rs | 109 ++++---- node/src/blockchain/blockchain_interface.rs | 104 ++++---- node/src/blockchain/test_utils.rs | 51 ++-- node/src/sub_lib/accountant.rs | 4 +- node/src/sub_lib/blockchain_bridge.rs | 6 +- node/src/test_utils/recorder.rs | 12 +- 12 files changed, 410 insertions(+), 295 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 355c90e82..a35b6b0f9 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -51,7 +51,7 @@ use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::accountant::{MessageIdGenerator, MessageIdGeneratorReal}; use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, OutcomingPayamentsInstructions, RequestBalancesToPayPayables, + ConsumingWalletBalances, OutcomingPaymentsInstructions, RequestBalancesToPayPayables, }; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; @@ -74,7 +74,7 @@ use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::ExpectValue; use payable_dao::PayableDao; use payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use receivable_dao::ReceivableDao; use std::any::type_name; @@ -101,7 +101,7 @@ pub struct Accountant { scanners: Scanners, scan_timings: ScanTimings, financial_statistics: Rc>, - outcoming_payments_instructions_sub_opt: Option>, + outcoming_payments_instructions_sub_opt: Option>, request_balances_to_pay_payables_sub_opt: Option>, retrieve_transactions_sub_opt: Option>, request_transaction_receipts_subs_opt: Option>, @@ -214,12 +214,12 @@ impl Handler for Accountant { } } -impl Handler> for Accountant { +impl Handler> for Accountant { type Result = (); fn handle( &mut self, - msg: PayablePaymentSetup, + msg: PayablePaymentSetup, _ctx: &mut Self::Context, ) -> Self::Result { self.handle_payable_payment_setup(msg); @@ -472,7 +472,7 @@ impl Accountant { report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayablePaymentSetup + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -665,7 +665,10 @@ impl Accountant { }) } - fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup)-> Option{ + fn handle_payable_payment_setup( + &mut self, + msg: PayablePaymentSetup, + ) -> Option { let bb_instructions = match self.scanners.payable.process_softly(msg, &self.logger) { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { @@ -674,7 +677,7 @@ impl Accountant { .payable .process_with_adjustment(unaccepted_msg, &self.logger) } - Err(e) => todo!() + Err(e) => todo!(), }; self.outcoming_payments_instructions_sub_opt .as_ref() @@ -1026,8 +1029,10 @@ mod tests { use crate::accountant::payable_dao::{ PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, }; + use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::pending_payable_dao::PendingPayableDaoError; use crate::accountant::receivable_dao::ReceivableAccount; + use crate::accountant::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::NullScanner; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, @@ -1049,7 +1054,7 @@ mod tests { ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, }; - use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; + use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; @@ -1088,8 +1093,6 @@ mod tests { use std::time::Duration; use std::vec; use web3::types::{TransactionReceipt, U256}; - use crate::accountant::payment_adjuster::Adjustment; - use crate::accountant::scan_mid_procedures::AwaitingAdjustment; impl Handler> for Accountant { type Result = (); @@ -1407,7 +1410,7 @@ mod tests { let is_adjustment_required_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(OutcomingPayamentsInstructions)) + .system_stop_conditions(match_every_type_id!(OutcomingPaymentsInstructions)) .start() .recipient(); let mut subject = AccountantBuilder::default().build(); @@ -1425,12 +1428,13 @@ mod tests { let system = System::new("test"); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data: ConsumingWalletBalancesAndGasPrice { + this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(u32::MAX), }, - desired_gas_price: 123, + estimated_gas_limit_per_transaction: 112_000, + desired_gas_price_gwei: 123, }, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1450,8 +1454,8 @@ mod tests { ); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &OutcomingPayamentsInstructions { + blockchain_bridge_recording.get_record::(0), + &OutcomingPaymentsInstructions { accounts: vec![account_1, account_2], response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1472,7 +1476,7 @@ mod tests { let adjust_payments_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(OutcomingPayamentsInstructions)) + .system_stop_conditions(match_every_type_id!(OutcomingPaymentsInstructions)) .start() .recipient(); let mut subject = AccountantBuilder::default().build(); @@ -1490,7 +1494,7 @@ mod tests { client_id: 12, context_id: 55, }; - let adjusted_payments_instructions = OutcomingPayamentsInstructions { + let adjusted_payments_instructions = OutcomingPaymentsInstructions { accounts: vec![adjusted_account_1.clone(), adjusted_account_2.clone()], response_skeleton_opt: Some(response_skeleton), }; @@ -1509,12 +1513,13 @@ mod tests { let system = System::new("test"); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data: ConsumingWalletBalancesAndGasPrice { + this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(150_000_000_000_u64), }, - desired_gas_price: 0, + estimated_gas_limit_per_transaction: 110_000, + desired_gas_price_gwei: 0, }, response_skeleton_opt: Some(response_skeleton), }; @@ -1528,13 +1533,19 @@ mod tests { let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); - assert_eq!(cwbqp_msg, AwaitingAdjustment{original_msg: consuming_balances_and_qualified_payments, adjustment: Adjustment::MasqToken}); + assert_eq!( + cwbqp_msg, + AwaitingAdjustment { + original_msg: consuming_balances_and_qualified_payments, + adjustment: Adjustment::MasqToken + } + ); assert!(before <= captured_now && captured_now <= after); assert!(adjust_payments_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &OutcomingPayamentsInstructions { + blockchain_bridge_recording.get_record::(0), + &OutcomingPaymentsInstructions { accounts: vec![adjusted_account_1, adjusted_account_2], response_skeleton_opt: Some(response_skeleton) } @@ -2211,7 +2222,7 @@ mod tests { ); let blockchain_bridge_addr: Addr = blockchain_bridge.start(); let report_accounts_payable_sub = - blockchain_bridge_addr.recipient::(); + blockchain_bridge_addr.recipient::(); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_daos(vec![ForPayableScanner(payable_dao)]) @@ -3123,7 +3134,7 @@ mod tests { //because we cannot have both, resolution on the high level and also of what's inside blockchain interface, //there is one component missing in this wholesome test - the part where we send a request for //a fingerprint of that payable in the DB - this happens inside send_raw_transaction() - .send_payables_within_batch_result(Ok(vec![ + .send_batch_of_payables_result(Ok(vec![ Correct(PendingPayable { recipient_wallet: wallet_account_1.clone(), hash: pending_tx_hash_1, diff --git a/node/src/accountant/payable_scan_setup_msgs.rs b/node/src/accountant/payable_scan_setup_msgs.rs index 16d0d9ffa..9e8ed4b24 100644 --- a/node/src/accountant/payable_scan_setup_msgs.rs +++ b/node/src/accountant/payable_scan_setup_msgs.rs @@ -18,21 +18,22 @@ pub mod inter_actor_communication_for_payable_scanner { } #[derive(Debug, PartialEq, Eq, Clone)] - pub struct ConsumingWalletBalancesAndGasPrice { + pub struct ConsumingWalletBalancesAndGasParams { pub consuming_wallet_balances: ConsumingWalletBalances, - pub desired_gas_price: u64, + pub estimated_gas_limit_per_transaction: u64, + pub desired_gas_price_gwei: u64, } impl From<( RequestBalancesToPayPayables, - ConsumingWalletBalancesAndGasPrice, - )> for PayablePaymentSetup + ConsumingWalletBalancesAndGasParams, + )> for PayablePaymentSetup { fn from( (previous_msg, current_stage_data): ( RequestBalancesToPayPayables, - ConsumingWalletBalancesAndGasPrice, + ConsumingWalletBalancesAndGasParams, ), ) -> Self { PayablePaymentSetup { diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 301f2cfd0..18989b8bd 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -2,10 +2,12 @@ use crate::accountant::payable_dao::PayableAccount; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; +use crate::accountant::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::{comma_joined_stringifiable, gwei_to_wei}; -use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; +use crate::masq_lib::utils::ExpectValue; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use itertools::Itertools; use lazy_static::lazy_static; use masq_lib::logger::Logger; @@ -14,7 +16,7 @@ use std::iter::{once, successors}; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; -use crate::accountant::scan_mid_procedures::AwaitingAdjustment; +use websocket::header::q; lazy_static! { static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); @@ -23,7 +25,7 @@ lazy_static! { pub trait PaymentAdjuster { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, + msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError>; @@ -32,7 +34,7 @@ pub trait PaymentAdjuster { setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, - ) -> OutcomingPayamentsInstructions; + ) -> OutcomingPaymentsInstructions; declare_as_any!(); } @@ -42,48 +44,62 @@ pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, + msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError> { let qualified_payables = msg.qualified_payables.as_slice(); - let sum = Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); + + // let total_gas_required_gwei = + // U256::from(msg.this_stage_data.estimated_gas_limit_per_transaction) + // * U256::from(qualified_payables.len()) + // * U256::from(msg.this_stage_data.desired_gas_price_gwei); + // eprintln!("total gwei required: {}", total_gas_required_gwei); + // let total_gas_required_wei = gwei_to_wei::(total_gas_required_gwei); + // eprintln!("available wei: {}", msg.this_stage_data.consuming_wallet_balances.gas_currency_wei); + // let limit_by_gas_opt = if total_gas_required_wei + // <= msg + // .this_stage_data + // .consuming_wallet_balances + // .gas_currency_wei + // { + // //TODO drive in both < and = + // false + // } else { + // true + // }; + + //TODO use question mark later + let limit_by_gas_opt = match Self::determine_feasible_count_to_pay_regarding_gas( + &msg.this_stage_data, + qualified_payables.len(), + ) { + Ok(None) => None, + Ok(Some(limiting_count)) => Some(limiting_count), + Err(e) => todo!(), + }; + + let required_masq_sum = + Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); let cw_masq_balance = msg .this_stage_data .consuming_wallet_balances .masq_tokens_wei; - let required_by_masq_token = if U256::from(sum) <= cw_masq_balance { + let required_by_masq_token = if U256::from(required_masq_sum) <= cw_masq_balance { false } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { todo!() } else { - Self::log_adjustment_required(logger, qualified_payables, sum, cw_masq_balance); + Self::log_adjustment_required(logger, required_masq_sum, cw_masq_balance); true }; - let total_gas_required = gwei_to_wei::( - qualified_payables.len() as u64 * msg.this_stage_data.desired_gas_price, - ); - eprintln!("total gas required {}", total_gas_required); - eprintln!("cons balance {:?}", msg.this_stage_data.consuming_wallet_balances.gas_currency_wei); - let required_by_gas = if total_gas_required - <= msg - .this_stage_data - .consuming_wallet_balances - .gas_currency_wei - { - //TODO drive in both < and = - false - } else { - todo!() - }; - - match (required_by_masq_token, required_by_gas){ - (false, false) => Ok(None), - (true, false) => Ok(Some(Adjustment::MasqToken)), - (false, true) => todo!(), - (true, true) => todo!() + match (limit_by_gas_opt, required_by_masq_token) { + (None, false) => Ok(None), + (None, true) => Ok(Some(Adjustment::MasqToken)), + (Some(limiting_count), false) => Ok(Some(Adjustment::Gas { limiting_count })), + (Some(limiting_count), true) => todo!(), } } @@ -92,7 +108,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, - ) -> OutcomingPayamentsInstructions { + ) -> OutcomingPaymentsInstructions { let msg = setup.original_msg; let current_stage_data = msg.this_stage_data; let qualified_payables: Vec = msg.qualified_payables; @@ -117,7 +133,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { debug_log_printer_opt.expect("debug message missing")(&balance_adjusted_accounts) ); - OutcomingPayamentsInstructions { + OutcomingPaymentsInstructions { accounts: balance_adjusted_accounts, response_skeleton_opt: msg.response_skeleton_opt, } @@ -158,6 +174,31 @@ impl PaymentAdjusterReal { .into() } + fn determine_feasible_count_to_pay_regarding_gas( + tech_info: &ConsumingWalletBalancesAndGasParams, + required_max_count: usize, + ) -> Result, AnalysisError> { + let gas_required_per_transaction_gwei = + u128::try_from(tech_info.estimated_gas_limit_per_transaction) + .expectv("small number for gas limit") + * u128::try_from(tech_info.desired_gas_price_gwei) + .expectv("small number for gas price"); + let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); + let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; + eprintln!("available wei: {:?}", available_wei); + eprintln!("wei per tx: {:?}", grpt_in_wei); + let possible_payment_count = (available_wei / grpt_in_wei).as_u128(); + if possible_payment_count == 0 { + todo!() + } else if possible_payment_count >= required_max_count as u128 { + Ok(None) + } else { + let type_limited_possible_count = + u16::try_from(possible_payment_count).expectv("small number for possible tx count"); + Ok(Some(type_limited_possible_count)) + } + } + fn find_multiplication_coeff(cw_masq_balance: U256, criteria_sum: U256) -> u128 { ((criteria_sum / cw_masq_balance) * *MULTI_COEFF_BY_100).as_u128() } @@ -276,6 +317,7 @@ impl PaymentAdjusterReal { Self::prefabricated_formatted_accounts(original, true).collect::>(); move |adjusted_accounts: &[PayableAccount]| { let prefabricated_adjusted = + //TODO extend the collection of adjusted up to the initial length using Option Self::prefabricated_formatted_accounts(adjusted_accounts, false); format!( "\nAdjusted payables:\n\ @@ -299,18 +341,12 @@ impl PaymentAdjusterReal { } } - fn log_adjustment_required( - logger: &Logger, - payables: &[PayableAccount], - payables_sum: U256, - cw_masq_balance: U256, - ) { + fn log_adjustment_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { warning!( logger, - "Payments for wallets {} make total of {} wei while the consuming wallet holds \ - only {} wei. Going to adjust them to fit in the limit, by cutting back the number \ - of payments or their size.", - comma_joined_stringifiable(payables, |p| p.wallet.to_string()), + "Total of {} wei in MASQ was ordered while the consuming wallet held \ + only {} wei of the MASQ token. Adjustment in their count or the amounts \ + is required.", payables_sum.separate_with_commas(), cw_masq_balance.separate_with_commas() ) @@ -326,7 +362,7 @@ fn log_10(num: u128) -> usize { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - Gas, + Gas { limiting_count: u16 }, Both, } @@ -335,12 +371,13 @@ pub enum AnalysisError {} #[cfg(test)] mod tests { + use std::iter::once; use crate::accountant::gwei_to_wei; use crate::accountant::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, Adjustment}; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, OutcomingPayamentsInstructions, + ConsumingWalletBalances, OutcomingPaymentsInstructions, }; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; @@ -348,7 +385,7 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; - use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup}; + use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; use crate::accountant::scan_mid_procedures::AwaitingAdjustment; fn type_definite_conversion(gwei: u64) -> u128 { @@ -371,23 +408,29 @@ mod tests { assert_eq!(result, U256::from(expected_result)) } - fn make_msg_with_q_payables_cw_balance_and_gas_price( + fn make_payable_setup_msg_coming_from_blockchain_bridge( q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, gas_price_opt: Option, - ) -> PayablePaymentSetup { + ) -> PayablePaymentSetup { let (qualified_payables_gwei, consuming_wallet_masq_gwei) = q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); - let (desired_gas_price, number_of_payments, cw_balance_gas_gwei) = match gas_price_opt { + let ( + desired_gas_price, + number_of_payments, + estimated_gas_limit_per_tx, + cw_balance_gas_gwei, + ) = match gas_price_opt { Some(conditions) => ( conditions.desired_gas_price_gwei, conditions.number_of_payments, + conditions.estimated_gas_limit_per_transaction, conditions.consuming_wallet_masq_gwei, ), - None => (120, qualified_payables_gwei.len(), u64::MAX), + None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), }; - let qualified_payables = match number_of_payments != qualified_payables_gwei.len() { + let qualified_payables: Vec<_> = match number_of_payments != qualified_payables_gwei.len() { true => (0..number_of_payments) .map(|idx| make_payable_account(idx as u64)) .collect(), @@ -396,14 +439,16 @@ mod tests { .map(|balance| make_payable_account(balance)) .collect(), }; + PayablePaymentSetup { qualified_payables, - this_stage_data: ConsumingWalletBalancesAndGasPrice { + this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), }, - desired_gas_price, + estimated_gas_limit_per_transaction: estimated_gas_limit_per_tx, + desired_gas_price_gwei: desired_gas_price, }, response_skeleton_opt: None, } @@ -412,6 +457,7 @@ mod tests { struct GasTestConditions { desired_gas_price_gwei: u64, number_of_payments: usize, + estimated_gas_limit_per_transaction: u64, consuming_wallet_masq_gwei: u64, } @@ -423,26 +469,39 @@ mod tests { let logger = Logger::new(test_name); //masq balance > payments let msg_1 = - make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 14], 100)), None); + make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 14], 100)), None); //masq balance = payments let msg_2 = - make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 15], 100)), None); + make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 15], 100)), None); //gas balance > payments - let msg_3 = - make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( + None, + Some(GasTestConditions { desired_gas_price_gwei: 111, number_of_payments: 5, - consuming_wallet_masq_gwei: 556, - })); + estimated_gas_limit_per_transaction: 53_000, + consuming_wallet_masq_gwei: (111 * 5 * 53_000) + 1, + }), + ); //gas balance = payments - let msg_4 = - make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + let msg_4 = make_payable_setup_msg_coming_from_blockchain_bridge( + None, + Some(GasTestConditions { desired_gas_price_gwei: 100, number_of_payments: 6, - consuming_wallet_masq_gwei: 600, - })); + estimated_gas_limit_per_transaction: 53_000, + consuming_wallet_masq_gwei: 100 * 6 * 53_000, + }), + ); - [msg_1,msg_2,msg_3,msg_4].into_iter().for_each(|msg| assert_eq!(subject.is_adjustment_required(&msg, &logger), Ok(None), "failed for msg {:?}", msg)); + [msg_1, msg_2, msg_3, msg_4].into_iter().for_each(|msg| { + assert_eq!( + subject.is_adjustment_required(&msg, &logger), + Ok(None), + "failed for msg {:?}", + msg + ) + }); TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); } @@ -454,16 +513,14 @@ mod tests { let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); let msg = - make_msg_with_q_payables_cw_balance_and_gas_price(Some((vec![85, 16], 100)), None); + make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 16], 100)), None); let result = subject.is_adjustment_required(&msg, &logger); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ - 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 make \ - total of 101,000,000,000 wei while the consuming wallet holds only 100,000,000,000 wei. \ - Going to adjust them to fit in the limit, by cutting back the number of payments or their \ - size.")); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Total of 101,000,000,000 \ + wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ + Adjustment in their count or the amounts is required.")); } #[test] @@ -472,21 +529,32 @@ mod tests { let test_name = "is_adjustment_required_positive_for_gas"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); - let msg = - make_msg_with_q_payables_cw_balance_and_gas_price(None, Some(GasTestConditions{ + let number_of_payments = 3; + let msg = make_payable_setup_msg_coming_from_blockchain_bridge( + None, + Some(GasTestConditions { desired_gas_price_gwei: 100, - number_of_payments: 3, - consuming_wallet_masq_gwei: 299, - })); + number_of_payments, + estimated_gas_limit_per_transaction: 55_000, + consuming_wallet_masq_gwei: 100 * 3 * 55_000 - 1, + }), + ); let result = subject.is_adjustment_required(&msg, &logger); - assert_eq!(result, Ok(Some(Adjustment::MasqToken))); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ - 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 would \ - require 100 gwei wei while the consuming wallet holds only 100,000,000,000 wei. \ - Going to adjust them to fit in the limit, by cutting back the number of payments or their \ - size.")); + let expected_limiting_count = number_of_payments as u16 - 1; + assert_eq!( + result, + Ok(Some(Adjustment::Gas { + limiting_count: expected_limiting_count + })) + ); + // TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ + // 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 would \ + // require 100 gwei wei while the consuming wallet holds only 100,000,000,000 wei. \ + // Going to adjust them to fit in the limit, by cutting back the number of payments or their \ + // size.")); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: blaaaah msg")); } #[test] @@ -587,18 +655,22 @@ mod tests { let accounts_sum: u128 = 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); - let setup_msg = PayablePaymentSetup { + let setup_msg = PayablePaymentSetup { qualified_payables, - this_stage_data: ConsumingWalletBalancesAndGasPrice { + this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(150), masq_tokens_wei: consuming_wallet_masq_balance, }, - desired_gas_price: 222222222222222222, + estimated_gas_limit_per_transaction: 165_000, + desired_gas_price_gwei: 222222222222222222, }, response_skeleton_opt: None, }; - let adjustment_setup = AwaitingAdjustment{ original_msg: setup_msg, adjustment: Adjustment::MasqToken }; //TODO what to do with the required adjustment? + let adjustment_setup = AwaitingAdjustment { + original_msg: setup_msg, + adjustment: Adjustment::MasqToken, + }; //TODO what to do with the required adjustment? let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); @@ -655,7 +727,7 @@ mod tests { }; assert_eq!( result, - OutcomingPayamentsInstructions { + OutcomingPaymentsInstructions { accounts: expected_criteria_computation_output, response_skeleton_opt: None } diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scan_mid_procedures.rs index 39e4cab60..0c4576a77 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scan_mid_procedures.rs @@ -1,14 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; +use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::Scanner; -use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use actix::Message; use itertools::Either; use masq_lib::logger::Logger; -use crate::accountant::payment_adjuster::{Adjustment}; pub trait PayableScannerWithMidProcedures: Scanner + PayableScannerMidProcedures @@ -21,30 +21,27 @@ where pub trait PayableScannerMidProcedures { fn process_softly( &self, - msg: PayablePaymentSetup, + msg: PayablePaymentSetup, logger: &Logger, - ) -> Result< - Either< - OutcomingPayamentsInstructions, - AwaitingAdjustment, - >, - String, - >; + ) -> Result, String>; fn process_with_adjustment( &self, setup: AwaitingAdjustment, logger: &Logger, - ) -> OutcomingPayamentsInstructions; + ) -> OutcomingPaymentsInstructions; } #[derive(Debug, PartialEq, Eq)] -pub struct AwaitingAdjustment{ - pub original_msg: PayablePaymentSetup, - pub adjustment: Adjustment +pub struct AwaitingAdjustment { + pub original_msg: PayablePaymentSetup, + pub adjustment: Adjustment, } -impl AwaitingAdjustment{ - pub fn new(original_msg: PayablePaymentSetup, adjustment: Adjustment)->Self{ +impl AwaitingAdjustment { + pub fn new( + original_msg: PayablePaymentSetup, + adjustment: Adjustment, + ) -> Self { todo!() } -} \ No newline at end of file +} diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs index 3885c2c1f..615cfead5 100644 --- a/node/src/accountant/scanners.rs +++ b/node/src/accountant/scanners.rs @@ -2,12 +2,14 @@ use crate::accountant::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::pending_payable_dao::PendingPayableDao; use crate::accountant::receivable_dao::ReceivableDao; -use crate::accountant::scan_mid_procedures::{AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures}; +use crate::accountant::scan_mid_procedures::{ + AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures, +}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; @@ -36,7 +38,7 @@ use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; use crate::sub_lib::blockchain_bridge::{ - OutcomingPayamentsInstructions, RequestBalancesToPayPayables, + OutcomingPaymentsInstructions, RequestBalancesToPayPayables, }; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; @@ -258,17 +260,11 @@ impl PayableScannerWithMidProcedures impl PayableScannerMidProcedures for PayableScanner { fn process_softly( &self, - msg: PayablePaymentSetup, + msg: PayablePaymentSetup, logger: &Logger, - ) -> Result< - Either< - OutcomingPayamentsInstructions, - AwaitingAdjustment, - >, - String, - > { + ) -> Result, String> { match self.payment_adjuster.is_adjustment_required(&msg, logger) { - Ok(None) => Ok(Either::Left(OutcomingPayamentsInstructions { + Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, })), @@ -281,7 +277,7 @@ impl PayableScannerMidProcedures for PayableScanner { &self, setup: AwaitingAdjustment, logger: &Logger, - ) -> OutcomingPayamentsInstructions { + ) -> OutcomingPaymentsInstructions { let now = SystemTime::now(); self.payment_adjuster.adjust_payments(setup, now, logger) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 44ccb35fa..c02493f28 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -7,7 +7,7 @@ use crate::accountant::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::pending_payable_dao::{ @@ -16,6 +16,7 @@ use crate::accountant::pending_payable_dao::{ use crate::accountant::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; +use crate::accountant::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; use crate::accountant::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::{gwei_to_wei, Accountant, DEFAULT_PENDING_TOO_LONG_SEC}; @@ -28,7 +29,7 @@ use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics}; use crate::sub_lib::accountant::{MessageIdGenerator, PaymentThresholds}; -use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::make_bc_with_defaults; @@ -43,7 +44,6 @@ use std::fmt::Debug; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::SystemTime; -use crate::accountant::scan_mid_procedures::AwaitingAdjustment; pub fn make_receivable_account(n: u64, expected_delinquent: bool) -> ReceivableAccount { let now = to_time_t(SystemTime::now()); @@ -1382,23 +1382,16 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { is_adjustment_required_params: - Arc>>>, - is_adjustment_required_results: RefCell,AnalysisError>>>, - adjust_payments_params: Arc< - Mutex< - Vec<( - AwaitingAdjustment, - SystemTime, - )>, - >, - >, - adjust_payments_results: RefCell>, + Arc>>>, + is_adjustment_required_results: RefCell, AnalysisError>>>, + adjust_payments_params: Arc>>, + adjust_payments_results: RefCell>, } impl PaymentAdjuster for PaymentAdjusterMock { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, + msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError> { self.is_adjustment_required_params @@ -1413,8 +1406,11 @@ impl PaymentAdjuster for PaymentAdjusterMock { setup: AwaitingAdjustment, now: SystemTime, logger: &Logger, - ) -> OutcomingPayamentsInstructions { - self.adjust_payments_params.lock().unwrap().push((setup, now)); + ) -> OutcomingPaymentsInstructions { + self.adjust_payments_params + .lock() + .unwrap() + .push((setup, now)); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1422,13 +1418,16 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn is_adjustment_required_params( mut self, - params: &Arc>>>, + params: &Arc>>>, ) -> Self { self.is_adjustment_required_params = params.clone(); self } - pub fn is_adjustment_required_result(self, result: Result,AnalysisError>) -> Self { + pub fn is_adjustment_required_result( + self, + result: Result, AnalysisError>, + ) -> Self { self.is_adjustment_required_results .borrow_mut() .push(result); @@ -1437,20 +1436,13 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc< - Mutex< - Vec<( - AwaitingAdjustment, - SystemTime, - )>, - >, - >, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self } - pub fn adjust_payments_result(self, result: OutcomingPayamentsInstructions) -> Self { + pub fn adjust_payments_result(self, result: OutcomingPaymentsInstructions) -> Self { self.adjust_payments_results.borrow_mut().push(result); self } diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 970cca31e..3bdcaa666 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ - ConsumingWalletBalancesAndGasPrice, PayablePaymentSetup, + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::{ ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, @@ -17,7 +17,7 @@ use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; use crate::sub_lib::blockchain_bridge::{ - BlockchainBridgeSubs, ConsumingWalletBalances, OutcomingPayamentsInstructions, + BlockchainBridgeSubs, ConsumingWalletBalances, OutcomingPaymentsInstructions, RequestBalancesToPayPayables, }; use crate::sub_lib::peer_actors::BindMessage; @@ -38,19 +38,18 @@ use std::path::PathBuf; use std::time::SystemTime; use web3::transports::Http; use web3::types::{TransactionReceipt, H256}; -use web3::Transport; pub const CRASH_KEY: &str = "BLOCKCHAINBRIDGE"; -pub struct BlockchainBridge { +pub struct BlockchainBridge { consuming_wallet_opt: Option, - blockchain_interface: Box>, + blockchain_interface: Box, logger: Logger, persistent_config: Box, set_consuming_wallet_subs_opt: Option>>, sent_payable_subs_opt: Option>, balances_and_payables_sub_opt: - Option>>, + Option>>, received_payments_subs_opt: Option>, scan_error_subs_opt: Option>, crashable: bool, @@ -153,10 +152,10 @@ impl Handler for BlockchainBridge { } } -impl Handler for BlockchainBridge { +impl Handler for BlockchainBridge { type Result = (); - fn handle(&mut self, msg: OutcomingPayamentsInstructions, _ctx: &mut Self::Context) { + fn handle(&mut self, msg: OutcomingPaymentsInstructions, _ctx: &mut Self::Context) { self.handle_scan( Self::handle_report_accounts_payable, ScanType::Payables, @@ -252,7 +251,7 @@ impl BlockchainBridge { pub fn make_subs_from(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, OutcomingPayamentsInstructions), + report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), @@ -260,6 +259,7 @@ impl BlockchainBridge { } } + //TODO rename to something more summarizing fn handle_request_balances_to_pay_payables( &mut self, msg: RequestBalancesToPayPayables, @@ -274,7 +274,8 @@ impl BlockchainBridge { ) } }; - //TODO rewrite this into a batch call as soon as GH-629 gets into master + // TODO rewrite this into a batch call as soon as GH-629 gets into master + // New card GH-707 will address this let gas_balance = match self.blockchain_interface.get_gas_balance(consuming_wallet) { Ok(gas_balance) => gas_balance, Err(e) => { @@ -302,17 +303,22 @@ impl BlockchainBridge { masq_tokens_wei: token_balance, } }; - let desired_gas_price = self + let desired_gas_price_gwei = self .persistent_config .gas_price() .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; - let this_stage_data = ConsumingWalletBalancesAndGasPrice { + let estimated_gas_limit_per_transaction = self + .blockchain_interface + .estimated_gas_limit_per_transaction(); + + let this_stage_data = ConsumingWalletBalancesAndGasParams { consuming_wallet_balances, - desired_gas_price, + estimated_gas_limit_per_transaction, + desired_gas_price_gwei, }; - let msg: PayablePaymentSetup = + let msg: PayablePaymentSetup = (msg, this_stage_data).into(); self.balances_and_payables_sub_opt @@ -326,7 +332,7 @@ impl BlockchainBridge { fn handle_report_accounts_payable( &mut self, - msg: OutcomingPayamentsInstructions, + msg: OutcomingPaymentsInstructions, ) -> Result<(), String> { let skeleton_opt = msg.response_skeleton_opt; let result = self.process_payments(&msg); @@ -458,7 +464,7 @@ impl BlockchainBridge { fn process_payments( &self, - msg: &OutcomingPayamentsInstructions, + msg: &OutcomingPaymentsInstructions, ) -> Result, PayableTransactionError> { let (consuming_wallet, gas_price) = match self.consuming_wallet_opt.as_ref() { Some(consuming_wallet) => match self.persistent_config.gas_price() { @@ -480,7 +486,7 @@ impl BlockchainBridge { let new_fingerprints_recipient = self.get_new_fingerprints_recipient(); - self.blockchain_interface.send_payables_within_batch( + self.blockchain_interface.send_batch_of_payables( consuming_wallet, gas_price, pending_nonce, @@ -630,7 +636,7 @@ mod tests { None, ); subject.sent_payable_subs_opt = Some(recipient); - let request = OutcomingPayamentsInstructions { + let request = OutcomingPaymentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, @@ -679,18 +685,31 @@ mod tests { .get_gas_balance_params(&get_gas_balance_params_arc) .get_gas_balance_result(Ok(gas_balance)) .get_token_balance_params(&get_token_balance_params_arc) - .get_token_balance_result(Ok(token_balance)); + .get_token_balance_result(Ok(token_balance)) + .estimated_gas_limit_per_transaction_result(51_546); let consuming_wallet = make_paying_wallet(b"somewallet"); let persistent_configuration = PersistentConfigurationMock::default().gas_price_result(Ok(146)); - let qualified_accounts = vec![PayableAccount { - wallet: make_wallet("booga"), - balance_wei: 78_654_321, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(1000)) - .unwrap(), - pending_payable_opt: None, - }]; + let wallet_1 = make_wallet("booga"); + let wallet_2 = make_wallet("gulp"); + let qualified_accounts = vec![ + PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 78_654_321_124, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(1000)) + .unwrap(), + pending_payable_opt: None, + }, + PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 60_457_111_003, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(500)) + .unwrap(), + pending_payable_opt: None, + }, + ]; let subject = BlockchainBridge::new( Box::new(blockchain_interface), Box::new(persistent_configuration), @@ -720,9 +739,9 @@ mod tests { let accountant_received_payment = accountant_recording_arc.lock().unwrap(); assert_eq!(accountant_received_payment.len(), 1); let reported_balances_and_qualified_accounts: &PayablePaymentSetup< - ConsumingWalletBalancesAndGasPrice, + ConsumingWalletBalancesAndGasParams, > = accountant_received_payment.get_record(0); - let expected_msg: PayablePaymentSetup = ( + let expected_msg: PayablePaymentSetup = ( RequestBalancesToPayPayables { accounts: qualified_accounts, response_skeleton_opt: Some(ResponseSkeleton { @@ -730,9 +749,10 @@ mod tests { context_id: 444, }), }, - ConsumingWalletBalancesAndGasPrice { + ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: wallet_balances_found, - desired_gas_price: 146, + estimated_gas_limit_per_transaction: 51_546, + desired_gas_price_gwei: 146, }, ) .into(); @@ -897,7 +917,7 @@ mod tests { let system = System::new("handle_report_accounts_payable_transacts_and_sends_finished_payments_back_to_accountant"); let get_transaction_count_params_arc = Arc::new(Mutex::new(vec![])); - let send_payables_within_batch_params_arc = Arc::new(Mutex::new(vec![])); + let send_batch_of_payables_params_arc = Arc::new(Mutex::new(vec![])); let (accountant, _, accountant_recording_arc) = make_recorder(); let accountant = accountant.system_stop_conditions(match_every_type_id!(PendingPayableFingerprintSeeds)); @@ -906,8 +926,8 @@ mod tests { let blockchain_interface_mock = BlockchainInterfaceMock::default() .get_transaction_count_params(&get_transaction_count_params_arc) .get_transaction_count_result(Ok(U256::from(1u64))) - .send_payables_within_batch_params(&send_payables_within_batch_params_arc) - .send_payables_within_batch_result(Ok(vec![ + .send_batch_of_payables_params(&send_batch_of_payables_params_arc) + .send_batch_of_payables_result(Ok(vec![ Correct(PendingPayable { recipient_wallet: wallet_account_1.clone(), hash: H256::from("sometransactionhash".keccak256()), @@ -947,7 +967,7 @@ mod tests { send_bind_message!(subject_subs, peer_actors); let _ = addr - .try_send(OutcomingPayamentsInstructions { + .try_send(OutcomingPaymentsInstructions { accounts: accounts.clone(), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -958,8 +978,7 @@ mod tests { System::current().stop(); system.run(); - let mut send_payables_within_batch_params = - send_payables_within_batch_params_arc.lock().unwrap(); + let mut send_batch_of_payables_params = send_batch_of_payables_params_arc.lock().unwrap(); //cannot assert on the captured recipient as its actor is gone after the System stops spinning let ( consuming_wallet_actual, @@ -967,8 +986,8 @@ mod tests { nonce_actual, _recipient_actual, accounts_actual, - ) = send_payables_within_batch_params.remove(0); - assert!(send_payables_within_batch_params.is_empty()); + ) = send_batch_of_payables_params.remove(0); + assert!(send_batch_of_payables_params.is_empty()); assert_eq!(consuming_wallet_actual, consuming_wallet.clone()); assert_eq!(gas_price_actual, expected_gas_price); assert_eq!(nonce_actual, U256::from(1u64)); @@ -1015,7 +1034,7 @@ mod tests { }); let blockchain_interface_mock = BlockchainInterfaceMock::default() .get_transaction_count_result(Ok(U256::from(1u64))) - .send_payables_within_batch_result(expected_error.clone()); + .send_batch_of_payables_result(expected_error.clone()); let persistent_configuration_mock = PersistentConfigurationMock::default().gas_price_result(Ok(123)); let consuming_wallet = make_paying_wallet(b"somewallet"); @@ -1037,7 +1056,7 @@ mod tests { send_bind_message!(subject_subs, peer_actors); let _ = addr - .try_send(OutcomingPayamentsInstructions { + .try_send(OutcomingPaymentsInstructions { accounts, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1094,7 +1113,7 @@ mod tests { false, Some(consuming_wallet), ); - let request = OutcomingPayamentsInstructions { + let request = OutcomingPaymentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 123_456, @@ -1119,7 +1138,7 @@ mod tests { let transaction_hash = make_tx_hash(789); let blockchain_interface_mock = BlockchainInterfaceMock::default() .get_transaction_count_result(Ok(web3::types::U256::from(1))) - .send_payables_within_batch_result(Err(PayableTransactionError::Sending { + .send_batch_of_payables_result(Err(PayableTransactionError::Sending { msg: "failure from exhaustion".to_string(), hashes: vec![transaction_hash], })); @@ -1132,7 +1151,7 @@ mod tests { false, Some(consuming_wallet.clone()), ); - let request = OutcomingPayamentsInstructions { + let request = OutcomingPaymentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 424_454, @@ -1180,7 +1199,7 @@ mod tests { ); subject.sent_payable_subs_opt = Some(sent_payables_recipient); subject.scan_error_subs_opt = Some(scan_error_recipient); - let request = OutcomingPayamentsInstructions { + let request = OutcomingPaymentsInstructions { accounts: vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface.rs index 19a6bab8a..dedf357f9 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::comma_joined_stringifiable; use crate::accountant::payable_dao::{PayableAccount, PendingPayable}; +use crate::accountant::{comma_joined_stringifiable, gwei_to_wei}; use crate::blockchain::batch_payable_tools::{BatchPayableTools, BatchPayableToolsReal}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::blockchain::blockchain_interface::BlockchainError::{ @@ -22,12 +22,12 @@ use std::fmt::{Debug, Display, Formatter}; use std::iter::once; use thousands::Separable; use web3::contract::{Contract, Options}; -use web3::transports::{Batch, EventLoopHandle, Http}; +use web3::transports::{Batch, EventLoopHandle}; use web3::types::{ Address, BlockNumber, Bytes, FilterBuilder, Log, SignedTransaction, TransactionParameters, TransactionReceipt, H160, H256, U256, }; -use web3::{BatchTransport, Error, Transport, Web3}; +use web3::{BatchTransport, Error, Web3}; pub const REQUESTS_IN_PARALLEL: usize = 1; @@ -40,6 +40,8 @@ const TRANSACTION_LITERAL: H256 = H256([ const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; +const TRANSACTION_DATA_MARGIN_TO_GAS_LIMIT: u64 = transaction_data_margin(); + #[derive(Clone, Debug, Eq, Message, PartialEq)] pub struct BlockchainTransaction { pub block_number: u64, @@ -129,7 +131,7 @@ pub struct RetrievedBlockchainTransactions { pub transactions: Vec, } -pub trait BlockchainInterface { +pub trait BlockchainInterface { fn contract_address(&self) -> Address; fn retrieve_transactions( @@ -138,7 +140,9 @@ pub trait BlockchainInterface { recipient: &Wallet, ) -> Result; - fn send_payables_within_batch( + fn estimated_gas_limit_per_transaction(&self) -> u64; + + fn send_batch_of_payables( &self, consuming_wallet: &Wallet, gas_price: u64, @@ -192,7 +196,11 @@ impl BlockchainInterface for BlockchainInterfaceClandestine { Err(BlockchainError::QueryFailed(msg)) } - fn send_payables_within_batch( + fn estimated_gas_limit_per_transaction(&self) -> u64 { + todo!() + } + + fn send_batch_of_payables( &self, _consuming_wallet: &Wallet, _gas_price: u64, @@ -341,7 +349,11 @@ where .wait() } - fn send_payables_within_batch( + fn estimated_gas_limit_per_transaction(&self) -> u64 { + todo!() + } + + fn send_batch_of_payables( &self, consuming_wallet: &Wallet, gas_price: u64, @@ -614,29 +626,11 @@ where nonce: U256, gas_price: u64, ) -> Result { - let mut data = [0u8; 4 + 32 + 32]; - data[0..4].copy_from_slice(&TRANSFER_METHOD_ID); - data[16..36].copy_from_slice(&recipient.address().0[..]); - U256::try_from(amount) - .expect("shouldn't overflow") - .to_big_endian(&mut data[36..68]); - let base_gas_limit = Self::base_gas_limit(self.chain); - let gas_limit = - ethereum_types::U256::try_from(data.iter().fold(base_gas_limit, |acc, v| { - acc + if v == &0u8 { 4 } else { 68 } - })) - .expect("Internal error"); - let converted_nonce = serde_json::from_value::( - serde_json::to_value(nonce).expect("Internal error"), - ) - .expect("Internal error"); - let gas_price = serde_json::from_value::( - serde_json::to_value(to_wei(gas_price)).expect("Internal error"), - ) - .expect("Internal error"); - + let data = Self::transaction_data(recipient, amount); + let gas_limit = Self::compute_gas_limit(data.as_slice(), self.chain); //TODO this should by a const for each chain perhaps (excessive gas isn't consumed) + let gas_price = gwei_to_wei::(gas_price); let transaction_parameters = TransactionParameters { - nonce: Some(converted_nonce), + nonce: Some(nonce), to: Some(H160(self.contract_address().0)), gas: gas_limit, gas_price: Some(gas_price), @@ -685,6 +679,24 @@ where introduction.chain(body).collect() } + fn transaction_data(recipient: &Wallet, amount: u128) -> [u8; 68] { + let mut data = [0u8; 4 + 32 + 32]; + data[0..4].copy_from_slice(&TRANSFER_METHOD_ID); + data[16..36].copy_from_slice(&recipient.address().0[..]); + U256::try_from(amount) + .expect("shouldn't overflow") + .to_big_endian(&mut data[36..68]); + data + } + + fn compute_gas_limit(data: &[u8], chain: Chain) -> U256 { + let base_gas_limit = Self::base_gas_limit(chain); + ethereum_types::U256::try_from(data.iter().fold(base_gas_limit, |acc, v| { + acc + if v == &0u8 { 4 } else { 68 } + })) + .expect("Internal error") + } + fn base_gas_limit(chain: Chain) -> u64 { match chain.rec().chain_family { ChainFamily::Polygon => 70_000, @@ -693,12 +705,22 @@ where } } + fn gas_limit_safe_estimation(chain: Chain) -> u64 { + todo!("use transaction_data_margin here") + } + #[cfg(test)] fn web3(&self) -> &Web3 { &self.web3 } } +const fn transaction_data_margin() -> u64 { + // 68 bytes * 68 per non zero byte according to the Ethereum docs, + // deliberately maximized + 68 * 68 +} + #[cfg(test)] mod tests { use super::*; @@ -1321,7 +1343,7 @@ mod tests { let test_timestamp_before = SystemTime::now(); let result = subject - .send_payables_within_batch( + .send_batch_of_payables( &consuming_wallet, gas_price, pending_nonce, @@ -1471,8 +1493,7 @@ mod tests { } #[test] - fn non_clandestine_interface_send_payables_within_batch_components_are_used_together_properly() - { + fn non_clandestine_interface_send_batch_of_payables_components_are_used_together_properly() { let sign_transaction_params_arc = Arc::new(Mutex::new(vec![])); let append_transaction_to_batch_params_arc = Arc::new(Mutex::new(vec![])); let new_payable_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); @@ -1562,7 +1583,7 @@ mod tests { None, ); - let result = subject.send_payables_within_batch( + let result = subject.send_batch_of_payables( &consuming_wallet, gas_price, pending_nonce, @@ -1772,13 +1793,8 @@ mod tests { let nonce = U256::from(123); let accounts = vec![make_payable_account(5555), make_payable_account(6666)]; - let result = subject.send_payables_within_batch( - &consuming_wallet, - 111, - nonce, - &recipient, - &accounts, - ); + let result = + subject.send_batch_of_payables(&consuming_wallet, 111, nonce, &recipient, &accounts); assert_eq!( result, @@ -1791,7 +1807,7 @@ mod tests { } #[test] - fn send_payables_within_batch_fails_on_badly_prepared_consuming_wallet_without_secret() { + fn send_batch_of_payables_fails_on_badly_prepared_consuming_wallet_without_secret() { let transport = TestTransport::default(); let incomplete_consuming_wallet = Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(); @@ -1812,7 +1828,7 @@ mod tests { let gas_price = 123; let nonce = U256::from(1); - let result = subject.send_payables_within_batch( + let result = subject.send_batch_of_payables( &incomplete_consuming_wallet, gas_price, nonce, @@ -1830,7 +1846,7 @@ mod tests { } #[test] - fn send_payables_within_batch_fails_on_sending() { + fn send_batch_of_payables_fails_on_sending() { let transport = TestTransport::default(); let hash = make_tx_hash(123); let mut signed_transaction = make_default_signed_transaction(); @@ -1856,7 +1872,7 @@ mod tests { let gas_price = 123; let nonce = U256::from(1); - let result = subject.send_payables_within_batch( + let result = subject.send_batch_of_payables( &consuming_wallet, gas_price, nonce, diff --git a/node/src/blockchain/test_utils.rs b/node/src/blockchain/test_utils.rs index cc5c0b253..0acbe30cb 100644 --- a/node/src/blockchain/test_utils.rs +++ b/node/src/blockchain/test_utils.rs @@ -58,7 +58,8 @@ pub struct BlockchainInterfaceMock { retrieve_transactions_parameters: Arc>>, retrieve_transactions_results: RefCell>>, - send_payables_within_batch_params: Arc< + estimated_gas_limit_per_transaction_results: RefCell>, + send_batch_of_payables_params: Arc< Mutex< Vec<( Wallet, @@ -69,7 +70,7 @@ pub struct BlockchainInterfaceMock { )>, >, >, - send_payables_within_batch_results: + send_batch_of_payables_results: RefCell, PayableTransactionError>>>, get_gas_balance_params: Arc>>, get_gas_balance_results: RefCell>, @@ -99,7 +100,15 @@ impl BlockchainInterface for BlockchainInterfaceMock { self.retrieve_transactions_results.borrow_mut().remove(0) } - fn send_payables_within_batch( + fn estimated_gas_limit_per_transaction(&self) -> u64 { + *self + .estimated_gas_limit_per_transaction_results + .borrow_mut() + .as_ref() + .unwrap() + } + + fn send_batch_of_payables( &self, consuming_wallet: &Wallet, gas_price: u64, @@ -107,19 +116,14 @@ impl BlockchainInterface for BlockchainInterfaceMock { new_fingerprints_recipient: &Recipient, accounts: &[PayableAccount], ) -> Result, PayableTransactionError> { - self.send_payables_within_batch_params - .lock() - .unwrap() - .push(( - consuming_wallet.clone(), - gas_price, - last_nonce, - new_fingerprints_recipient.clone(), - accounts.to_vec(), - )); - self.send_payables_within_batch_results - .borrow_mut() - .remove(0) + self.send_batch_of_payables_params.lock().unwrap().push(( + consuming_wallet.clone(), + gas_price, + last_nonce, + new_fingerprints_recipient.clone(), + accounts.to_vec(), + )); + self.send_batch_of_payables_results.borrow_mut().remove(0) } fn get_gas_balance(&self, address: &Wallet) -> ResultForBalance { @@ -169,7 +173,14 @@ impl BlockchainInterfaceMock { self } - pub fn send_payables_within_batch_params( + pub fn estimated_gas_limit_per_transaction_result(self, result: u64) -> Self { + self.estimated_gas_limit_per_transaction_results + .borrow_mut() + .replace(result); + self + } + + pub fn send_batch_of_payables_params( mut self, params: &Arc< Mutex< @@ -183,15 +194,15 @@ impl BlockchainInterfaceMock { >, >, ) -> Self { - self.send_payables_within_batch_params = params.clone(); + self.send_batch_of_payables_params = params.clone(); self } - pub fn send_payables_within_batch_result( + pub fn send_batch_of_payables_result( self, result: Result, PayableTransactionError>, ) -> Self { - self.send_payables_within_batch_results + self.send_batch_of_payables_results .borrow_mut() .push(result); self diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index d48d3f702..d4911d740 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payable_dao::PayableDaoFactory; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasParams; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; use crate::accountant::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::receivable_dao::ReceivableDaoFactory; @@ -97,7 +97,7 @@ pub struct AccountantSubs { pub report_exit_service_provided: Recipient, pub report_services_consumed: Recipient, pub report_consuming_wallet_balances_and_qualified_payables: - Recipient>, + Recipient>, pub report_inbound_payments: Recipient, pub init_pending_payable_fingerprints: Recipient, pub report_transaction_receipts: Recipient, diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 9aa08f5b2..f69e37654 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -22,7 +22,7 @@ pub struct BlockchainBridgeConfig { #[derive(Clone, PartialEq, Eq)] pub struct BlockchainBridgeSubs { pub bind: Recipient, - pub report_accounts_payable: Recipient, + pub report_accounts_payable: Recipient, pub request_balances_to_pay_payables: Recipient, pub retrieve_transactions: Recipient, pub ui_sub: Recipient, @@ -48,12 +48,12 @@ impl SkeletonOptHolder for RequestBalancesToPayPayables { } #[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct OutcomingPayamentsInstructions { +pub struct OutcomingPaymentsInstructions { pub accounts: Vec, pub response_skeleton_opt: Option, } -impl SkeletonOptHolder for OutcomingPayamentsInstructions { +impl SkeletonOptHolder for OutcomingPaymentsInstructions { fn skeleton_opt(&self) -> Option { self.response_skeleton_opt } diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 8dbfc8c6c..9727b7b9b 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasPrice; +use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasParams; use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ @@ -17,7 +17,7 @@ use crate::sub_lib::accountant::AccountantSubs; use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; -use crate::sub_lib::blockchain_bridge::OutcomingPayamentsInstructions; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, RequestBalancesToPayPayables}; use crate::sub_lib::configurator::{ConfiguratorSubs, NewPasswordMessage}; use crate::sub_lib::dispatcher::InboundClientData; @@ -128,7 +128,7 @@ recorder_message_handler!(ReportServicesConsumedMessage); recorder_message_handler!(ReportExitServiceProvidedMessage); recorder_message_handler!(ReportRoutingServiceProvidedMessage); recorder_message_handler!(ScanError); -recorder_message_handler!(PayablePaymentSetup); +recorder_message_handler!(PayablePaymentSetup); recorder_message_handler!(SentPayables); recorder_message_handler!(SetConsumingWalletMessage); recorder_message_handler!(RequestBalancesToPayPayables); @@ -139,7 +139,7 @@ recorder_message_handler!(PendingPayableFingerprintSeeds); recorder_message_handler!(RetrieveTransactions); recorder_message_handler!(RequestTransactionReceipts); recorder_message_handler!(ReportTransactionReceipts); -recorder_message_handler!(OutcomingPayamentsInstructions); +recorder_message_handler!(OutcomingPaymentsInstructions); recorder_message_handler!(ScanForReceivables); recorder_message_handler!(ScanForPayables); recorder_message_handler!(ConnectionProgressMessage); @@ -427,7 +427,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayablePaymentSetup + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -449,7 +449,7 @@ pub fn make_ui_gateway_subs_from_recorder(addr: &Addr) -> UiGatewaySub pub fn make_blockchain_bridge_subs_from(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, OutcomingPayamentsInstructions), + report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), From 5547fdaaf995dfc0d88e57e873e0c1cd3abe9324 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 31 May 2023 12:39:14 +0200 Subject: [PATCH 018/250] GH-672: some renaming and moving files --- .../tests/blockchain_interaction_test.rs | 2 +- .../tests/bookkeeping_test.rs | 2 +- .../database_access_objects/banned_dao.rs | 2 +- .../accountant/database_access_objects/mod.rs | 2 +- .../database_access_objects/payable_dao.rs | 16 +++++++------- .../pending_payable_dao.rs | 4 ++-- .../database_access_objects/receivable_dao.rs | 10 ++++----- .../{dao_utils.rs => utils.rs} | 0 node/src/accountant/financials.rs | 4 ++-- node/src/accountant/mod.rs | 12 +++++----- node/src/accountant/payment_adjuster.rs | 22 +++++++++---------- node/src/accountant/scanners/mod.rs | 8 ++++--- .../{ => scanners}/payable_scan_setup_msgs.rs | 4 +--- .../{ => scanners}/scan_mid_procedures.rs | 2 +- .../src/accountant/scanners/scanners_utils.rs | 4 ++-- node/src/accountant/test_utils.rs | 6 ++--- node/src/blockchain/blockchain_bridge.rs | 4 ++-- node/src/blockchain/blockchain_interface.rs | 2 +- .../migrations/migration_4_to_5.rs | 4 ++-- .../migrations/migration_6_to_7.rs | 2 +- node/src/db_config/config_dao.rs | 2 +- .../unprivileged_parse_args_configuration.rs | 2 +- node/src/sub_lib/accountant.rs | 4 ++-- node/src/test_utils/database_utils.rs | 2 +- node/src/test_utils/recorder.rs | 4 ++-- node/tests/financials_test.rs | 2 +- 26 files changed, 63 insertions(+), 65 deletions(-) rename node/src/accountant/database_access_objects/{dao_utils.rs => utils.rs} (100%) rename node/src/accountant/{ => scanners}/payable_scan_setup_msgs.rs (94%) rename node/src/accountant/{ => scanners}/scan_mid_procedures.rs (93%) diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index bcd7de13b..346c6a684 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -21,7 +21,7 @@ use multinode_integration_tests_lib::mock_blockchain_client_server::MBCSBuilder; use multinode_integration_tests_lib::utils::{ config_dao, open_all_file_permissions, receivable_dao, UrlHolder, }; -use node_lib::accountant::database_access_objects::dao_utils::CustomQuery; +use node_lib::accountant::database_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; #[test] diff --git a/multinode_integration_tests/tests/bookkeeping_test.rs b/multinode_integration_tests/tests/bookkeeping_test.rs index 1de7745a1..65b1eb1ac 100644 --- a/multinode_integration_tests/tests/bookkeeping_test.rs +++ b/multinode_integration_tests/tests/bookkeeping_test.rs @@ -6,7 +6,7 @@ use multinode_integration_tests_lib::masq_real_node::{ STANDARD_CLIENT_TIMEOUT_MILLIS, }; use multinode_integration_tests_lib::utils::{payable_dao, receivable_dao}; -use node_lib::accountant::database_access_objects::dao_utils::CustomQuery; +use node_lib::accountant::database_access_objects::utils::CustomQuery; use node_lib::accountant::database_access_objects::payable_dao::PayableAccount; use node_lib::accountant::database_access_objects::receivable_dao::ReceivableAccount; use node_lib::sub_lib::wallet::Wallet; diff --git a/node/src/accountant/database_access_objects/banned_dao.rs b/node/src/accountant/database_access_objects/banned_dao.rs index 44088e973..889cae028 100644 --- a/node/src/accountant/database_access_objects/banned_dao.rs +++ b/node/src/accountant/database_access_objects/banned_dao.rs @@ -1,5 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::dao_utils::{ +use crate::accountant::database_access_objects::utils::{ DaoFactoryReal, VigilantRusqliteFlatten, }; use crate::database::connection_wrapper::ConnectionWrapper; diff --git a/node/src/accountant/database_access_objects/mod.rs b/node/src/accountant/database_access_objects/mod.rs index 7c4fc5ffb..aacc31b67 100644 --- a/node/src/accountant/database_access_objects/mod.rs +++ b/node/src/accountant/database_access_objects/mod.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod banned_dao; -pub mod dao_utils; +pub mod utils; pub mod payable_dao; pub mod pending_payable_dao; pub mod receivable_dao; diff --git a/node/src/accountant/database_access_objects/payable_dao.rs b/node/src/accountant/database_access_objects/payable_dao.rs index fe89ed1e0..cb4afd3bb 100644 --- a/node/src/accountant/database_access_objects/payable_dao.rs +++ b/node/src/accountant/database_access_objects/payable_dao.rs @@ -10,8 +10,8 @@ use crate::accountant::big_int_processing::big_int_db_processor::{ BigIntDbProcessor, BigIntSqlConfig, Param, SQLParamsBuilder, TableNameDAO, }; use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::dao_utils; -use crate::accountant::database_access_objects::dao_utils::{ +use crate::accountant::database_access_objects::utils; +use crate::accountant::database_access_objects::utils::{ sum_i128_values_from_table, to_time_t, AssemblerFeeder, CustomQuery, DaoFactoryReal, RangeStmConfig, TopStmConfig, VigilantRusqliteFlatten, }; @@ -207,7 +207,7 @@ impl PayableDao for PayableDaoReal { balance_wei: checked_conversion::(BigIntDivider::reconstitute( high_b, low_b, )), - last_paid_timestamp: dao_utils::from_time_t(last_paid_timestamp), + last_paid_timestamp: utils::from_time_t(last_paid_timestamp), pending_payable_opt: None, }) } @@ -293,7 +293,7 @@ impl PayableDao for PayableDaoReal { balance_wei: checked_conversion::(BigIntDivider::reconstitute( high_bytes, low_bytes, )), - last_paid_timestamp: dao_utils::from_time_t(last_paid_timestamp), + last_paid_timestamp: utils::from_time_t(last_paid_timestamp), pending_payable_opt: match rowid { Some(rowid) => Some(PendingPayableId::new( u64::try_from(rowid).unwrap(), @@ -349,7 +349,7 @@ impl PayableDaoReal { balance_wei: checked_conversion::(BigIntDivider::reconstitute( high_bytes, low_bytes, )), - last_paid_timestamp: dao_utils::from_time_t(last_paid_timestamp), + last_paid_timestamp: utils::from_time_t(last_paid_timestamp), pending_payable_opt: rowid_opt.map(|rowid| { let hash_str = hash_opt.expect("database corrupt; missing hash but existing rowid"); @@ -402,7 +402,7 @@ impl TableNameDAO for PayableDaoReal { mod mark_pending_payable_associated_functions { use crate::accountant::comma_joined_stringifiable; - use crate::accountant::database_access_objects::dao_utils::{ + use crate::accountant::database_access_objects::utils::{ update_rows_and_return_valid_count, VigilantRusqliteFlatten, }; use crate::accountant::database_access_objects::payable_dao::PayableDaoError; @@ -552,7 +552,7 @@ mod mark_pending_payable_associated_functions { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::{from_time_t, now_time_t, to_time_t}; + use crate::accountant::database_access_objects::utils::{from_time_t, now_time_t, to_time_t}; use crate::accountant::gwei_to_wei; use crate::accountant::database_access_objects::payable_dao::mark_pending_payable_associated_functions::explanatory_extension; use crate::accountant::test_utils::{ @@ -1585,7 +1585,7 @@ mod tests { let conn = DbInitializerReal::default() .initialize(&home_dir, DbInitializationConfig::test_default()) .unwrap(); - let timestamp = dao_utils::now_time_t(); + let timestamp = utils::now_time_t(); insert_payable_record_fn( &*conn, "0x1111111111111111111111111111111111111111", diff --git a/node/src/accountant/database_access_objects/pending_payable_dao.rs b/node/src/accountant/database_access_objects/pending_payable_dao.rs index 582c12a36..ca5827400 100644 --- a/node/src/accountant/database_access_objects/pending_payable_dao.rs +++ b/node/src/accountant/database_access_objects/pending_payable_dao.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::dao_utils::{ +use crate::accountant::database_access_objects::utils::{ from_time_t, to_time_t, DaoFactoryReal, VigilantRusqliteFlatten, }; use crate::accountant::{checked_conversion, comma_joined_stringifiable}; @@ -243,7 +243,7 @@ impl<'a> PendingPayableDaoReal<'a> { mod tests { use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; - use crate::accountant::database_access_objects::dao_utils::from_time_t; + use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoReal, }; diff --git a/node/src/accountant/database_access_objects/receivable_dao.rs b/node/src/accountant/database_access_objects/receivable_dao.rs index be626d1bb..425d14aad 100644 --- a/node/src/accountant/database_access_objects/receivable_dao.rs +++ b/node/src/accountant/database_access_objects/receivable_dao.rs @@ -9,8 +9,8 @@ use crate::accountant::big_int_processing::big_int_db_processor::{ }; use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; -use crate::accountant::database_access_objects::dao_utils; -use crate::accountant::database_access_objects::dao_utils::{ +use crate::accountant::database_access_objects::utils; +use crate::accountant::database_access_objects::utils::{ sum_i128_values_from_table, to_time_t, AssemblerFeeder, CustomQuery, DaoFactoryReal, RangeStmConfig, ThresholdUtils, TopStmConfig, VigilantRusqliteFlatten, }; @@ -335,7 +335,7 @@ impl ReceivableDaoReal { Ok(ReceivableAccount { wallet, balance_wei: BigIntDivider::reconstitute(high_bytes, low_bytes), - last_received_timestamp: dao_utils::from_time_t(last_received_timestamp), + last_received_timestamp: utils::from_time_t(last_received_timestamp), }) } e => panic!( @@ -413,7 +413,7 @@ impl TableNameDAO for ReceivableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::{ + use crate::accountant::database_access_objects::utils::{ from_time_t, now_time_t, to_time_t, }; use crate::accountant::gwei_to_wei; @@ -1438,7 +1438,7 @@ mod tests { .unwrap(); let insert = insert_account_by_separate_values; - let timestamp = dao_utils::now_time_t(); + let timestamp = utils::now_time_t(); insert( &*conn, "0x1111111111111111111111111111111111111111", diff --git a/node/src/accountant/database_access_objects/dao_utils.rs b/node/src/accountant/database_access_objects/utils.rs similarity index 100% rename from node/src/accountant/database_access_objects/dao_utils.rs rename to node/src/accountant/database_access_objects/utils.rs diff --git a/node/src/accountant/financials.rs b/node/src/accountant/financials.rs index 465d247d6..23415e19e 100644 --- a/node/src/accountant/financials.rs +++ b/node/src/accountant/financials.rs @@ -24,7 +24,7 @@ where } pub(in crate::accountant) mod visibility_restricted_module { - use crate::accountant::database_access_objects::dao_utils::CustomQuery; + use crate::accountant::database_access_objects::utils::CustomQuery; use crate::accountant::financials::{fits_in_0_to_i64max_for_u64, OPCODE_FINANCIALS}; use masq_lib::constants::{ REQUEST_WITH_MUTUALLY_EXCLUSIVE_PARAMS, REQUEST_WITH_NO_VALUES, VALUE_EXCEEDS_ALLOWED_LIMIT, @@ -111,7 +111,7 @@ pub(in crate::accountant) mod visibility_restricted_module { #[cfg(test)] mod tests { use super::visibility_restricted_module::check_query_is_within_tech_limits; - use crate::accountant::database_access_objects::dao_utils::CustomQuery; + use crate::accountant::database_access_objects::utils::CustomQuery; use crate::accountant::financials::fits_in_0_to_i64max_for_u64; use masq_lib::constants::VALUE_EXCEEDS_ALLOWED_LIMIT; use masq_lib::messages::TopRecordsOrdering::Age; diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c57b675b2..235b15d6e 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -3,9 +3,7 @@ pub mod big_int_processing; pub mod database_access_objects; pub mod financials; -pub mod payable_scan_setup_msgs; pub mod payment_adjuster; -pub mod scan_mid_procedures; pub mod scanners; #[cfg(test)] @@ -21,7 +19,7 @@ use masq_lib::messages::{ }; use masq_lib::ui_gateway::{MessageBody, MessagePath}; -use crate::accountant::database_access_objects::dao_utils::{ +use crate::accountant::database_access_objects::utils::{ remap_payable_accounts, remap_receivable_accounts, CustomQuery, DaoFactoryReal, }; use crate::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoError}; @@ -70,7 +68,7 @@ use masq_lib::messages::{FromMessageBody, ToMessageBody, UiFinancialsRequest}; use masq_lib::ui_gateway::MessageTarget::ClientId; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::ExpectValue; -use payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use std::any::type_name; @@ -1020,15 +1018,15 @@ pub mod check_sqlite_fns { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::from_time_t; - use crate::accountant::database_access_objects::dao_utils::{to_time_t, CustomQuery}; + use crate::accountant::database_access_objects::utils::from_time_t; + use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, }; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::payment_adjuster::Adjustment; - use crate::accountant::scan_mid_procedures::AwaitingAdjustment; + use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::NullScanner; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index ea4306205..21b06d945 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -2,10 +2,10 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::gwei_to_wei; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; -use crate::accountant::scan_mid_procedures::AwaitingAdjustment; +use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use itertools::Itertools; @@ -147,8 +147,8 @@ impl PaymentAdjusterReal { } fn sum_as_u256(collection: &[T], arranger: F) -> U256 - where - F: Fn(&T) -> u128, + where + F: Fn(&T) -> u128, { collection.iter().map(arranger).sum::().into() } @@ -181,7 +181,7 @@ impl PaymentAdjusterReal { u128::try_from(tech_info.estimated_gas_limit_per_transaction) .expectv("small number for gas limit") * u128::try_from(tech_info.desired_gas_price_gwei) - .expectv("small number for gas price"); + .expectv("small number for gas price"); let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; eprintln!("available wei: {:?}", available_wei); @@ -259,7 +259,7 @@ impl PaymentAdjusterReal { now: SystemTime, ) -> Vec<(u128, PayableAccount)> { type CriteriaClosure<'a> = - Box (u128, PayableAccount) + 'a>; + Box (u128, PayableAccount) + 'a>; //define individual criteria as closures to be used in a map() let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { @@ -316,7 +316,7 @@ impl PaymentAdjusterReal { Self::prefabricated_formatted_accounts(original, true).collect::>(); move |adjusted_accounts: &[PayableAccount]| { let prefabricated_adjusted = - //TODO extend the collection of adjusted up to the initial length using Option + //TODO extend the collection of adjusted up to the initial length using Option Self::prefabricated_formatted_accounts(adjusted_accounts, false); format!( "\nAdjusted payables:\n\ @@ -383,8 +383,8 @@ mod tests { use std::time::{Duration, SystemTime}; use std::vec; use web3::types::U256; - use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; - use crate::accountant::scan_mid_procedures::AwaitingAdjustment; + use crate::accountant::scanners::payable_scan_setup_msgs::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; + use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; fn type_definite_conversion(gwei: u64) -> u128 { gwei_to_wei(gwei) @@ -589,8 +589,8 @@ mod tests { (123, 3), (111_111_111_111_111_111, 18), ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) + .into_iter() + .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) } #[test] diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 2fa9af360..63a5ffc2e 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1,15 +1,17 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod scanners_utils; +pub mod payable_scan_setup_msgs; +pub mod scan_mid_procedures; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; use crate::accountant::database_access_objects::receivable_dao::ReceivableDao; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; -use crate::accountant::scan_mid_procedures::{ +use crate::accountant::scanners::scan_mid_procedures::{ AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ @@ -1142,7 +1144,7 @@ mod tests { use std::ops::Sub; use std::panic::{catch_unwind, AssertUnwindSafe}; - use crate::accountant::database_access_objects::dao_utils::{from_time_t, to_time_t}; + use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDaoError, PendingPayable, }; diff --git a/node/src/accountant/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs similarity index 94% rename from node/src/accountant/payable_scan_setup_msgs.rs rename to node/src/accountant/scanners/payable_scan_setup_msgs.rs index 3a22db066..ebdf57a03 100644 --- a/node/src/accountant/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -//TODO remove this mod frame around -pub mod inter_actor_communication_for_payable_scanner { + use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::ResponseSkeleton; use crate::sub_lib::blockchain_bridge::{ @@ -43,4 +42,3 @@ pub mod inter_actor_communication_for_payable_scanner { } } } -} diff --git a/node/src/accountant/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs similarity index 93% rename from node/src/accountant/scan_mid_procedures.rs rename to node/src/accountant/scanners/scan_mid_procedures.rs index 0c4576a77..7727c73ef 100644 --- a/node/src/accountant/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::payment_adjuster::Adjustment; diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index ec9e3c0b1..428ca7eb3 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod payable_scanner_utils { - use crate::accountant::database_access_objects::dao_utils::ThresholdUtils; + use crate::accountant::database_access_objects::utils::ThresholdUtils; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDaoError, PendingPayable}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, @@ -415,7 +415,7 @@ pub mod receivable_scanner_utils { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::dao_utils::{from_time_t, to_time_t}; + use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 881f53aae..fe9379735 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -3,7 +3,7 @@ #![cfg(test)] use crate::accountant::database_access_objects::banned_dao::{BannedDao, BannedDaoFactory}; -use crate::accountant::database_access_objects::dao_utils::{from_time_t, to_time_t, CustomQuery}; +use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; @@ -13,11 +13,11 @@ use crate::accountant::database_access_objects::pending_payable_dao::{ use crate::accountant::database_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; -use crate::accountant::scan_mid_procedures::AwaitingAdjustment; +use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; use crate::accountant::{gwei_to_wei, Accountant, DEFAULT_PENDING_TOO_LONG_SEC}; diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 7a37e2e66..0f90bcbf1 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::{ +use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::{ @@ -512,7 +512,7 @@ struct PendingTxInfo { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::from_time_t; + use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; use crate::accountant::test_utils::make_pending_payable_fingerprint; use crate::blockchain::bip32::Bip32ECKeyProvider; diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface.rs index ef57c061e..af8bd83c6 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface.rs @@ -724,7 +724,7 @@ const fn transaction_data_margin() -> u64 { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::from_time_t; + use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ make_payable_account, make_payable_account_with_wallet_and_balance_and_timestamp_opt, diff --git a/node/src/database/db_migrations/migrations/migration_4_to_5.rs b/node/src/database/db_migrations/migrations/migration_4_to_5.rs index 208a1018f..d46c6ae43 100644 --- a/node/src/database/db_migrations/migrations/migration_4_to_5.rs +++ b/node/src/database/db_migrations/migrations/migration_4_to_5.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::dao_utils::VigilantRusqliteFlatten; +use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; use crate::database::db_migrations::db_migrator::DatabaseMigration; use crate::database::db_migrations::migrator_utils::DBMigDeclarator; @@ -76,7 +76,7 @@ impl DatabaseMigration for Migrate_4_to_5 { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::dao_utils::{from_time_t, to_time_t}; + use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::database::connection_wrapper::{ConnectionWrapper, ConnectionWrapperReal}; use crate::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, DATABASE_FILE, diff --git a/node/src/database/db_migrations/migrations/migration_6_to_7.rs b/node/src/database/db_migrations/migrations/migration_6_to_7.rs index 3a1606281..0fa8ab6c9 100644 --- a/node/src/database/db_migrations/migrations/migration_6_to_7.rs +++ b/node/src/database/db_migrations/migrations/migration_6_to_7.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::dao_utils::VigilantRusqliteFlatten; +use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; use crate::accountant::gwei_to_wei; use crate::database::db_migrations::db_migrator::DatabaseMigration; use crate::database::db_migrations::migrator_utils::{ diff --git a/node/src/db_config/config_dao.rs b/node/src/db_config/config_dao.rs index e19aacda6..6c2b9ebab 100644 --- a/node/src/db_config/config_dao.rs +++ b/node/src/db_config/config_dao.rs @@ -1,5 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::dao_utils::DaoFactoryReal; +use crate::accountant::database_access_objects::utils::DaoFactoryReal; use crate::database::connection_wrapper::ConnectionWrapper; use rusqlite::types::ToSql; use rusqlite::{Row, Rows, Statement}; diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 5d543a80a..db948b67a 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -619,7 +619,7 @@ fn is_user_specified(multi_config: &MultiConfig, parameter: &str) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::dao_utils::ThresholdUtils; + use crate::accountant::database_access_objects::utils::ThresholdUtils; use crate::apps::app_node; use crate::blockchain::bip32::Bip32ECKeyProvider; use crate::database::db_initializer::DbInitializationConfig; diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index aede62f24..32a4bfdd4 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -3,8 +3,8 @@ use crate::accountant::database_access_objects::banned_dao::BannedDaoFactory; use crate::accountant::database_access_objects::payable_dao::PayableDaoFactory; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoFactory; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasParams; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; +use crate::accountant::scanners::payable_scan_setup_msgs::ConsumingWalletBalancesAndGasParams; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::{ checked_conversion, Accountant, ReceivedPayments, ReportTransactionReceipts, ScanError, SentPayables, diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index 9672eefd4..f63d7682a 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -2,7 +2,7 @@ #![cfg(test)] -use crate::accountant::database_access_objects::dao_utils::VigilantRusqliteFlatten; +use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; use crate::database::connection_wrapper::ConnectionWrapper; use crate::database::db_initializer::ExternalData; diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 4dcb44b96..24cf900dc 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::ConsumingWalletBalancesAndGasParams; -use crate::accountant::payable_scan_setup_msgs::inter_actor_communication_for_payable_scanner::PayablePaymentSetup; +use crate::accountant::scanners::payable_scan_setup_msgs::ConsumingWalletBalancesAndGasParams; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, diff --git a/node/tests/financials_test.rs b/node/tests/financials_test.rs index 981c81ebb..948ed0ed6 100644 --- a/node/tests/financials_test.rs +++ b/node/tests/financials_test.rs @@ -10,7 +10,7 @@ use masq_lib::messages::{ use masq_lib::test_utils::ui_connection::UiConnection; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::find_free_port; -use node_lib::accountant::database_access_objects::dao_utils::{from_time_t, to_time_t}; +use node_lib::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use node_lib::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::database_access_objects::receivable_dao::{ ReceivableDao, ReceivableDaoReal, From 4c322ae119bcf5de5f368ae26510fd042b6c9a61 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 1 Jun 2023 00:01:45 +0200 Subject: [PATCH 019/250] GH-672: almost completed but there is a cenceptual error; maybe extra tests for BlockchainInterfaceClandestine should be made first --- masq_lib/src/logger.rs | 2 +- .../tests/bookkeeping_test.rs | 2 +- .../database_access_objects/banned_dao.rs | 4 +- .../accountant/database_access_objects/mod.rs | 2 +- .../database_access_objects/payable_dao.rs | 2 +- .../pending_payable_dao.rs | 2 +- .../database_access_objects/receivable_dao.rs | 6 +- node/src/accountant/mod.rs | 73 +- node/src/accountant/payment_adjust/mod.rs | 0 node/src/accountant/payment_adjuster.rs | 759 +++--------------- node/src/accountant/scanners/mod.rs | 12 +- .../scanners/payable_scan_setup_msgs.rs | 67 +- .../scanners/scan_mid_procedures.rs | 11 +- node/src/accountant/test_utils.rs | 31 +- node/src/blockchain/blockchain_bridge.rs | 9 +- node/src/blockchain/blockchain_interface.rs | 42 +- node/src/blockchain/test_utils.rs | 10 +- node/tests/financials_test.rs | 2 +- 18 files changed, 260 insertions(+), 776 deletions(-) create mode 100644 node/src/accountant/payment_adjust/mod.rs diff --git a/masq_lib/src/logger.rs b/masq_lib/src/logger.rs index 455d0f574..7fa801e18 100644 --- a/masq_lib/src/logger.rs +++ b/masq_lib/src/logger.rs @@ -53,7 +53,7 @@ pub fn prepare_log_recipient(recipient: Recipient) { } } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Logger { name: String, #[cfg(not(feature = "no_test_share"))] diff --git a/multinode_integration_tests/tests/bookkeeping_test.rs b/multinode_integration_tests/tests/bookkeeping_test.rs index 65b1eb1ac..cb570a8ae 100644 --- a/multinode_integration_tests/tests/bookkeeping_test.rs +++ b/multinode_integration_tests/tests/bookkeeping_test.rs @@ -6,9 +6,9 @@ use multinode_integration_tests_lib::masq_real_node::{ STANDARD_CLIENT_TIMEOUT_MILLIS, }; use multinode_integration_tests_lib::utils::{payable_dao, receivable_dao}; -use node_lib::accountant::database_access_objects::utils::CustomQuery; use node_lib::accountant::database_access_objects::payable_dao::PayableAccount; use node_lib::accountant::database_access_objects::receivable_dao::ReceivableAccount; +use node_lib::accountant::database_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; use std::collections::HashMap; use std::thread; diff --git a/node/src/accountant/database_access_objects/banned_dao.rs b/node/src/accountant/database_access_objects/banned_dao.rs index 889cae028..0644699c6 100644 --- a/node/src/accountant/database_access_objects/banned_dao.rs +++ b/node/src/accountant/database_access_objects/banned_dao.rs @@ -1,7 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::utils::{ - DaoFactoryReal, VigilantRusqliteFlatten, -}; +use crate::accountant::database_access_objects::utils::{DaoFactoryReal, VigilantRusqliteFlatten}; use crate::database::connection_wrapper::ConnectionWrapper; use crate::sub_lib::wallet::Wallet; use lazy_static::lazy_static; diff --git a/node/src/accountant/database_access_objects/mod.rs b/node/src/accountant/database_access_objects/mod.rs index aacc31b67..a350148ab 100644 --- a/node/src/accountant/database_access_objects/mod.rs +++ b/node/src/accountant/database_access_objects/mod.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod banned_dao; -pub mod utils; pub mod payable_dao; pub mod pending_payable_dao; pub mod receivable_dao; +pub mod utils; diff --git a/node/src/accountant/database_access_objects/payable_dao.rs b/node/src/accountant/database_access_objects/payable_dao.rs index cb4afd3bb..0036134e6 100644 --- a/node/src/accountant/database_access_objects/payable_dao.rs +++ b/node/src/accountant/database_access_objects/payable_dao.rs @@ -402,10 +402,10 @@ impl TableNameDAO for PayableDaoReal { mod mark_pending_payable_associated_functions { use crate::accountant::comma_joined_stringifiable; + use crate::accountant::database_access_objects::payable_dao::PayableDaoError; use crate::accountant::database_access_objects::utils::{ update_rows_and_return_valid_count, VigilantRusqliteFlatten, }; - use crate::accountant::database_access_objects::payable_dao::PayableDaoError; use crate::database::connection_wrapper::ConnectionWrapper; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; diff --git a/node/src/accountant/database_access_objects/pending_payable_dao.rs b/node/src/accountant/database_access_objects/pending_payable_dao.rs index ca5827400..462030704 100644 --- a/node/src/accountant/database_access_objects/pending_payable_dao.rs +++ b/node/src/accountant/database_access_objects/pending_payable_dao.rs @@ -243,10 +243,10 @@ impl<'a> PendingPayableDaoReal<'a> { mod tests { use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; - use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoReal, }; + use crate::accountant::database_access_objects::utils::from_time_t; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::test_utils::make_tx_hash; use crate::database::connection_wrapper::ConnectionWrapperReal; diff --git a/node/src/accountant/database_access_objects/receivable_dao.rs b/node/src/accountant/database_access_objects/receivable_dao.rs index 425d14aad..db91cbe93 100644 --- a/node/src/accountant/database_access_objects/receivable_dao.rs +++ b/node/src/accountant/database_access_objects/receivable_dao.rs @@ -9,12 +9,12 @@ use crate::accountant::big_int_processing::big_int_db_processor::{ }; use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; +use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoError::RusqliteError; use crate::accountant::database_access_objects::utils; use crate::accountant::database_access_objects::utils::{ sum_i128_values_from_table, to_time_t, AssemblerFeeder, CustomQuery, DaoFactoryReal, RangeStmConfig, ThresholdUtils, TopStmConfig, VigilantRusqliteFlatten, }; -use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoError::RusqliteError; use crate::accountant::gwei_to_wei; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::database::connection_wrapper::ConnectionWrapper; @@ -413,9 +413,7 @@ impl TableNameDAO for ReceivableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::{ - from_time_t, now_time_t, to_time_t, - }; + use crate::accountant::database_access_objects::utils::{from_time_t, now_time_t, to_time_t}; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 235b15d6e..56d809a04 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -19,14 +19,14 @@ use masq_lib::messages::{ }; use masq_lib::ui_gateway::{MessageBody, MessagePath}; -use crate::accountant::database_access_objects::utils::{ - remap_payable_accounts, remap_receivable_accounts, CustomQuery, DaoFactoryReal, -}; use crate::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoError}; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; use crate::accountant::database_access_objects::receivable_dao::{ ReceivableDao, ReceivableDaoError, }; +use crate::accountant::database_access_objects::utils::{ + remap_payable_accounts, remap_receivable_accounts, CustomQuery, DaoFactoryReal, +}; use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; @@ -68,9 +68,7 @@ use masq_lib::messages::{FromMessageBody, ToMessageBody, UiFinancialsRequest}; use masq_lib::ui_gateway::MessageTarget::ClientId; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::ExpectValue; -use scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use scanners::payable_scan_setup_msgs::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; use std::any::type_name; #[cfg(test)] use std::default::Default; @@ -216,8 +214,7 @@ impl Handler> for Accou msg: PayablePaymentSetup, _ctx: &mut Self::Context, ) -> Self::Result { - self.handle_payable_payment_setup(msg); - todo!("send msg to UIGateway...") + self.handle_payable_payment_setup(msg) } } @@ -240,10 +237,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_payable(msg.response_skeleton_opt); - //TODO handling error with msg to the UI is missing! - self.scan_timings - .payable - .schedule_another_periodic_scan(ctx); + self.scan_timings.payable.next_scan_period(ctx); } } @@ -252,10 +246,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPendingPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_pending_payable(msg.response_skeleton_opt); - //TODO handling error with msg to the UI is missing! - self.scan_timings - .pending_payable - .schedule_another_periodic_scan(ctx); + self.scan_timings.pending_payable.next_scan_period(ctx); } } @@ -264,10 +255,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); - //TODO handling error with msg to the UI is missing! - self.scan_timings - .receivable - .schedule_another_periodic_scan(ctx); + self.scan_timings.receivable.next_scan_period(ctx); } } @@ -662,24 +650,23 @@ impl Accountant { fn handle_payable_payment_setup( &mut self, msg: PayablePaymentSetup, - ) -> Option { - let bb_instructions = match self.scanners.payable.process_softly(msg, &self.logger) { + ) { + let bb_instructions = match self.scanners.payable.try_soft_process(msg, &self.logger) { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { - //TODO we will eventually query info from Neighborhood here + //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 self.scanners .payable - .process_with_adjustment(unaccepted_msg, &self.logger) + .process_adjustment(unaccepted_msg, &self.logger) } - Err(e) => todo!(), + Err(_e) => todo!("be completed by GH-711"), }; self.outcoming_payments_instructions_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(bb_instructions) - .expect("BlockchainBridge is dead"); - - todo!() + .expect("BlockchainBridge is dead") + //TODO implement send point for ScanError; be completed by GH-711 } fn handle_financials(&self, msg: &UiFinancialsRequest, client_id: u64, context_id: u64) { @@ -1018,13 +1005,13 @@ pub mod check_sqlite_fns { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::from_time_t; - use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, }; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; + use crate::accountant::database_access_objects::utils::from_time_t; + use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::NullScanner; @@ -1401,6 +1388,8 @@ mod tests { // the numbers for balances don't do real math, they need not to match either the condition for // the payment adjustment or the actual values that come from the payable size reducing algorithm; // all that is mocked in this test + init_test_logging(); + let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; let is_adjustment_required_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge @@ -1416,6 +1405,7 @@ mod tests { .build(); subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); + subject.logger = Logger::new(test_name); let subject_addr = subject.start(); let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); @@ -1441,10 +1431,12 @@ mod tests { .unwrap(); system.run(); - let is_adjustment_required_params = is_adjustment_required_params_arc.lock().unwrap(); + let mut is_adjustment_required_params = is_adjustment_required_params_arc.lock().unwrap(); + let (payable_payment_setup_msg, logger_clone) = is_adjustment_required_params.remove(0); + assert!(is_adjustment_required_params.is_empty()); assert_eq!( - *is_adjustment_required_params, - vec![consuming_balances_and_qualified_payments] + payable_payment_setup_msg, + consuming_balances_and_qualified_payments ); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( @@ -1457,16 +1449,27 @@ mod tests { }) } ); + test_use_of_the_same_logger(&logger_clone, test_name) // adjust_payments() did not need a prepared result which means it wasn't reached // because otherwise this test would've panicked } + fn test_use_of_the_same_logger(logger_clone: &Logger, test_name: &str) { + let experiment_msg = format!("DEBUG: {test_name}: hello world"); + let log_handler = TestLogHandler::default(); + log_handler.exists_no_log_containing(&experiment_msg); + debug!(logger_clone, "hello world"); + log_handler.exists_log_containing(&experiment_msg); + } + #[test] fn received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge( ) { // the numbers for balances don't do real math, they need not to match either the condition for // the payment adjustment or the actual values that come from the payable size reducing algorithm; // all that is mocked in this test + init_test_logging(); + let test_name = "received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge"; let adjust_payments_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge @@ -1526,7 +1529,7 @@ mod tests { assert_eq!(system.run(), 0); let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); - let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); + let (cwbqp_msg, captured_now, logger_clone) = adjust_payments_params.remove(0); assert_eq!( cwbqp_msg, AwaitingAdjustment { @@ -1544,6 +1547,7 @@ mod tests { response_skeleton_opt: Some(response_skeleton) } ); + test_use_of_the_same_logger(&logger_clone, test_name) } #[test] @@ -3124,6 +3128,7 @@ mod tests { .get_gas_balance_result(Ok(U256::from(u128::MAX))) .get_token_balance_result(Ok(U256::from(u128::MAX))) .get_transaction_count_result(Ok(web3::types::U256::from(1))) + .estimated_gas_limit_per_payable_result(55_000) .get_transaction_count_result(Ok(web3::types::U256::from(2))) //because we cannot have both, resolution on the high level and also of what's inside blockchain interface, //there is one component missing in this wholesome test - the part where we send a request for diff --git a/node/src/accountant/payment_adjust/mod.rs b/node/src/accountant/payment_adjust/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 21b06d945..89a985f65 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,20 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::gwei_to_wei; use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; -use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use itertools::Itertools; use lazy_static::lazy_static; use masq_lib::logger::Logger; use std::any::Any; -use std::iter::{once, successors}; use std::time::SystemTime; -use thousands::Separable; use web3::types::U256; lazy_static! { @@ -43,98 +37,21 @@ pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, - logger: &Logger, + _msg: &PayablePaymentSetup, + _logger: &Logger, ) -> Result, AnalysisError> { - let qualified_payables = msg.qualified_payables.as_slice(); - - // let total_gas_required_gwei = - // U256::from(msg.this_stage_data.estimated_gas_limit_per_transaction) - // * U256::from(qualified_payables.len()) - // * U256::from(msg.this_stage_data.desired_gas_price_gwei); - // eprintln!("total gwei required: {}", total_gas_required_gwei); - // let total_gas_required_wei = gwei_to_wei::(total_gas_required_gwei); - // eprintln!("available wei: {}", msg.this_stage_data.consuming_wallet_balances.gas_currency_wei); - // let limit_by_gas_opt = if total_gas_required_wei - // <= msg - // .this_stage_data - // .consuming_wallet_balances - // .gas_currency_wei - // { - // //TODO drive in both < and = - // false - // } else { - // true - // }; - - //TODO use question mark later - let limit_by_gas_opt = match Self::determine_feasible_count_to_pay_regarding_gas( - &msg.this_stage_data, - qualified_payables.len(), - ) { - Ok(None) => None, - Ok(Some(limiting_count)) => Some(limiting_count), - Err(e) => todo!(), - }; - - let required_masq_sum = - Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); - let cw_masq_balance = msg - .this_stage_data - .consuming_wallet_balances - .masq_tokens_wei; - - let required_by_masq_token = if U256::from(required_masq_sum) <= cw_masq_balance { - false - } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { - todo!() - } else { - Self::log_adjustment_required(logger, required_masq_sum, cw_masq_balance); - - true - }; - - match (limit_by_gas_opt, required_by_masq_token) { - (None, false) => Ok(None), - (None, true) => Ok(Some(Adjustment::MasqToken)), - (Some(limiting_count), false) => Ok(Some(Adjustment::Gas { limiting_count })), - (Some(limiting_count), true) => todo!(), - } + Ok(None) } fn adjust_payments( &self, setup: AwaitingAdjustment, - now: SystemTime, - logger: &Logger, + _now: SystemTime, + _logger: &Logger, ) -> OutcomingPaymentsInstructions { - let msg = setup.original_msg; - let current_stage_data = msg.this_stage_data; - let qualified_payables: Vec = msg.qualified_payables; - let debug_log_printer_opt = - logger - .debug_enabled() - .then_some(Self::before_and_after_debug_msg_formatter( - &qualified_payables, - )); - - let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); - let accounts_with_individual_criteria = - Self::apply_criteria(accounts_with_zero_criteria, now); - let balance_adjusted_accounts = Self::handle_adjustment( - current_stage_data.consuming_wallet_balances.masq_tokens_wei, - accounts_with_individual_criteria, - ); - - debug!( - logger, - "{}", - debug_log_printer_opt.expect("debug message missing")(&balance_adjusted_accounts) - ); - OutcomingPaymentsInstructions { - accounts: balance_adjusted_accounts, - response_skeleton_opt: msg.response_skeleton_opt, + accounts: setup.original_msg.qualified_payables, + response_skeleton_opt: setup.original_msg.response_skeleton_opt, } } @@ -145,217 +62,6 @@ impl PaymentAdjusterReal { pub fn new() -> Self { Self {} } - - fn sum_as_u256(collection: &[T], arranger: F) -> U256 - where - F: Fn(&T) -> u128, - { - collection.iter().map(arranger).sum::().into() - } - - fn sum_payable_balances(qualified_accounts: &[PayableAccount]) -> U256 { - qualified_accounts - .iter() - .map(|account| account.balance_wei) - .sum::() - .into() - } - - fn find_smallest_debt(qualified_accounts: &[PayableAccount]) -> U256 { - qualified_accounts - .iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - }) - .last() - .expect("at least one qualified payable must have been sent here") - .balance_wei - .into() - } - - fn determine_feasible_count_to_pay_regarding_gas( - tech_info: &ConsumingWalletBalancesAndGasParams, - required_max_count: usize, - ) -> Result, AnalysisError> { - let gas_required_per_transaction_gwei = - u128::try_from(tech_info.estimated_gas_limit_per_transaction) - .expectv("small number for gas limit") - * u128::try_from(tech_info.desired_gas_price_gwei) - .expectv("small number for gas price"); - let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); - let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; - eprintln!("available wei: {:?}", available_wei); - eprintln!("wei per tx: {:?}", grpt_in_wei); - let possible_payment_count = (available_wei / grpt_in_wei).as_u128(); - if possible_payment_count == 0 { - todo!() - } else if possible_payment_count >= required_max_count as u128 { - Ok(None) - } else { - let type_limited_possible_count = - u16::try_from(possible_payment_count).expectv("small number for possible tx count"); - Ok(Some(type_limited_possible_count)) - } - } - - fn find_multiplication_coeff(cw_masq_balance: U256, criteria_sum: U256) -> u128 { - ((criteria_sum / cw_masq_balance) * *MULTI_COEFF_BY_100).as_u128() - } - - fn initialize_zero_criteria( - qualified_payables: Vec, - ) -> impl Iterator { - fn just_zero_criteria_iterator(accounts_count: usize) -> impl Iterator { - let one_element = once(0_u128); - let endlessly_repeated = one_element.into_iter().cycle(); - endlessly_repeated.take(accounts_count) - } - - let accounts_count = qualified_payables.len(); - let criteria_iterator = just_zero_criteria_iterator(accounts_count); - criteria_iterator.zip(qualified_payables.into_iter()) - } - - fn recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - proportional_fragment_of_cw_balance: u128, - multiplication_coeff: u128, - ) -> Vec { - let rebuild_account = |(criteria_sum, mut account): (u128, PayableAccount)| { - let proportional_amount_to_pay = - criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - account.balance_wei = proportional_amount_to_pay; - account - }; - - accounts_with_individual_criteria - .into_iter() - .map(rebuild_account) - .collect() - } - - fn handle_adjustment( - cw_masq_balance: U256, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> Vec { - let criteria_sum = - Self::sum_as_u256(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }); - let multiplication_coeff = - PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_sum); - let proportional_fragment_of_cw_balance = - cw_masq_balance.as_u128() * multiplication_coeff / criteria_sum.as_u128(); - - Self::recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria, - proportional_fragment_of_cw_balance, - multiplication_coeff, - ) - } - - fn apply_criteria( - accounts_with_zero_criteria: impl Iterator, - now: SystemTime, - ) -> Vec<(u128, PayableAccount)> { - type CriteriaClosure<'a> = - Box (u128, PayableAccount) + 'a>; - //define individual criteria as closures to be used in a map() - - let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let criteria = now - .duration_since(account.last_paid_timestamp) - .expect("time traveller") - .as_secs() as u128; - (criteria_sum + criteria, account) - }); - let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let digits_weight = log_10(account.balance_wei); - let additional_criteria = account.balance_wei * digits_weight as u128; - (criteria_sum + additional_criteria, account) - }); - - accounts_with_zero_criteria - .map(time_criteria_closure) - .map(balance_criteria_closure) - .collect() - } - - fn format_brief_accounts_summary( - original_accounts: impl Iterator, - adjusted_accounts: impl Iterator, - ) -> String { - original_accounts - .zip(adjusted_accounts) - .map(|(original, adjusted)| format!("{}\n{}", original, adjusted)) - .join("\n") - } - - fn prefabricated_formatted_accounts<'a>( - accounts: &'a [PayableAccount], - display_wallet: bool, - ) -> impl Iterator + 'a { - accounts.iter().map(move |account| { - let wallet_opt = if display_wallet { - Some(account.wallet.to_string()) - } else { - None - }; - format!( - "{:<42} {}", - wallet_opt.as_ref().map(|w| w.as_str()).unwrap_or(""), - account.balance_wei - ) - }) - } - - fn before_and_after_debug_msg_formatter( - original: &[PayableAccount], - ) -> impl FnOnce(&[PayableAccount]) -> String { - let original_prefabricated = - Self::prefabricated_formatted_accounts(original, true).collect::>(); - move |adjusted_accounts: &[PayableAccount]| { - let prefabricated_adjusted = - //TODO extend the collection of adjusted up to the initial length using Option - Self::prefabricated_formatted_accounts(adjusted_accounts, false); - format!( - "\nAdjusted payables:\n\ - {:<42} {}\n\ - {: <42} {}\n\ - {: <42} {}\n\ - \n\ - {}", - "Account wallet", - "Balance wei", - "", - "Original", - "", - "Adjusted", - Self::format_brief_accounts_summary( - original_prefabricated.into_iter(), - prefabricated_adjusted - ) - ) - //TODO mention accounts that will be excluded completely - } - } - - fn log_adjustment_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { - warning!( - logger, - "Total of {} wei in MASQ was ordered while the consuming wallet held \ - only {} wei of the MASQ token. Adjustment in their count or the amounts \ - is required.", - payables_sum.separate_with_commas(), - cw_masq_balance.separate_with_commas() - ) - } -} - -// replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` -// which will be introduced by Rust 1.67.0; this was written with 1.63.0 -fn log_10(num: u128) -> usize { - successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } #[derive(Debug, PartialEq, Eq)] @@ -370,389 +76,130 @@ pub enum AnalysisError {} #[cfg(test)] mod tests { - use crate::accountant::gwei_to_wei; - use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::{log_10, PaymentAdjuster, PaymentAdjusterReal, MULTI_COEFF_BY_100, Adjustment}; + use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; + use crate::accountant::scanners::payable_scan_setup_msgs::{ + ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, + }; + use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::test_utils::make_payable_account; + use crate::accountant::ResponseSkeleton; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; - use crate::test_utils::make_wallet; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use std::time::{Duration, SystemTime}; - use std::vec; + use std::time::SystemTime; use web3::types::U256; - use crate::accountant::scanners::payable_scan_setup_msgs::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; - use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; - - fn type_definite_conversion(gwei: u64) -> u128 { - gwei_to_wei(gwei) - } #[test] - fn sum_payable_balances_works() { - let qualified_payables = vec![ - make_payable_account(456), - make_payable_account(1111), - make_payable_account(7800), - ]; - - let result = PaymentAdjusterReal::sum_payable_balances(&qualified_payables); - - let expected_result = type_definite_conversion(456) - + type_definite_conversion(1111) - + type_definite_conversion(7800); - assert_eq!(result, U256::from(expected_result)) - } - - fn make_payable_setup_msg_coming_from_blockchain_bridge( - q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, - gas_price_opt: Option, - ) -> PayablePaymentSetup { - let (qualified_payables_gwei, consuming_wallet_masq_gwei) = - q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); - - let ( - desired_gas_price, - number_of_payments, - estimated_gas_limit_per_tx, - cw_balance_gas_gwei, - ) = match gas_price_opt { - Some(conditions) => ( - conditions.desired_gas_price_gwei, - conditions.number_of_payments, - conditions.estimated_gas_limit_per_transaction, - conditions.consuming_wallet_masq_gwei, - ), - None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), - }; - - let qualified_payables: Vec<_> = match number_of_payments != qualified_payables_gwei.len() { - true => (0..number_of_payments) - .map(|idx| make_payable_account(idx as u64)) - .collect(), - false => qualified_payables_gwei - .into_iter() - .map(|balance| make_payable_account(balance)) - .collect(), - }; - - PayablePaymentSetup { - qualified_payables, + fn is_adjustment_required_always_returns_none() { + init_test_logging(); + let test_name = "is_adjustment_required_always_returns_none"; + let mut payable_1 = make_payable_account(111); + payable_1.balance_wei = 100_000_000; + let mut payable_2 = make_payable_account(222); + payable_2.balance_wei = 200_000_000; + let non_required = PayablePaymentSetup { + qualified_payables: vec![payable_1.clone(), payable_2.clone()], this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), - masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), + gas_currency_wei: U256::from(1_001_000_000_000_u64), + masq_tokens_wei: U256::from(301_000_000), }, - estimated_gas_limit_per_transaction: estimated_gas_limit_per_tx, - desired_gas_price_gwei: desired_gas_price, + estimated_gas_limit_per_transaction: 50_000, + desired_gas_price_gwei: 10, + //gas amount to spent = 2 * 50_000 * 10 [gwei] = 1_000_000_000_000 wei }, response_skeleton_opt: None, - } - } - - struct GasTestConditions { - desired_gas_price_gwei: u64, - number_of_payments: usize, - estimated_gas_limit_per_transaction: u64, - consuming_wallet_masq_gwei: u64, - } - - #[test] - fn is_adjustment_required_negative_answer() { - init_test_logging(); - let test_name = "is_adjustment_required_negative_answer"; - let subject = PaymentAdjusterReal::new(); - let logger = Logger::new(test_name); - //masq balance > payments - let msg_1 = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 14], 100)), None); - //masq balance = payments - let msg_2 = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 15], 100)), None); - //gas balance > payments - let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( - None, - Some(GasTestConditions { - desired_gas_price_gwei: 111, - number_of_payments: 5, - estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_masq_gwei: (111 * 5 * 53_000) + 1, - }), - ); - //gas balance = payments - let msg_4 = make_payable_setup_msg_coming_from_blockchain_bridge( - None, - Some(GasTestConditions { - desired_gas_price_gwei: 100, - number_of_payments: 6, - estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_masq_gwei: 100 * 6 * 53_000, - }), - ); - - [msg_1, msg_2, msg_3, msg_4].into_iter().for_each(|msg| { - assert_eq!( - subject.is_adjustment_required(&msg, &logger), - Ok(None), - "failed for msg {:?}", - msg - ) - }); - - TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); - } - - #[test] - fn is_adjustment_required_positive_for_masq_token() { - init_test_logging(); - let test_name = "is_adjustment_required_positive_for_masq_token"; - let logger = Logger::new(test_name); - let subject = PaymentAdjusterReal::new(); - let msg = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 16], 100)), None); - - let result = subject.is_adjustment_required(&msg, &logger); - - assert_eq!(result, Ok(Some(Adjustment::MasqToken))); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Total of 101,000,000,000 \ - wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ - Adjustment in their count or the amounts is required.")); - } - - #[test] - fn is_adjustment_required_positive_for_gas() { - init_test_logging(); - let test_name = "is_adjustment_required_positive_for_gas"; + }; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); - let number_of_payments = 3; - let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - None, - Some(GasTestConditions { - desired_gas_price_gwei: 100, - number_of_payments, - estimated_gas_limit_per_transaction: 55_000, - consuming_wallet_masq_gwei: 100 * 3 * 55_000 - 1, - }), - ); - let result = subject.is_adjustment_required(&msg, &logger); - - let expected_limiting_count = number_of_payments as u16 - 1; - assert_eq!( - result, - Ok(Some(Adjustment::Gas { - limiting_count: expected_limiting_count - })) - ); - // TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ - // 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 would \ - // require 100 gwei wei while the consuming wallet holds only 100,000,000,000 wei. \ - // Going to adjust them to fit in the limit, by cutting back the number of payments or their \ - // size.")); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: blaaaah msg")); - } - - #[test] - fn find_smallest_debt_works() { - let mut payable_1 = make_payable_account(111); - payable_1.balance_wei = 111_111; - let mut payable_3 = make_payable_account(333); - payable_3.balance_wei = 111_110; - let mut payable_2 = make_payable_account(222); - payable_2.balance_wei = 3_000_000; - let qualified_payables = vec![payable_1, payable_2, payable_3]; - - let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); - - assert_eq!(min, U256::from(111_110)) - } - - #[test] - fn find_smallest_debt_handles_just_one_account() { - let payable = make_payable_account(111); - let qualified_payables = vec![payable]; - - let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); - - assert_eq!(min, U256::from(111_000_000_000_u128)) - } + let non_required_result = subject.is_adjustment_required(&non_required, &logger); - #[test] - fn log_10_works() { - [ - (4_565_u128, 4), - (1_666_777, 7), - (3, 1), - (123, 3), - (111_111_111_111_111_111, 18), - ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) - } - - #[test] - fn multiplication_coeff_to_get_integers_above_one_instead_of_fractional_numbers_works() { - let final_criteria_sum = U256::from(5_000_000_000_000_u64); - let consuming_wallet_balances = vec![ - U256::from(222_222_222_222_u64), - U256::from(100_000), - U256::from(123_456_789), - ]; - - let result = consuming_wallet_balances - .clone() - .into_iter() - .map(|cw_balance| { - PaymentAdjusterReal::find_multiplication_coeff(cw_balance, final_criteria_sum) - }) - .collect::>(); - - let expected_coefficients = { - let co_1 = ((final_criteria_sum / consuming_wallet_balances[0]) * *MULTI_COEFF_BY_100) - .as_u128(); - assert_eq!(co_1, 22_000); - let co_2 = ((final_criteria_sum / consuming_wallet_balances[1]) * *MULTI_COEFF_BY_100) - .as_u128(); - assert_eq!(co_2, 50_000_000_000); - let co_3 = ((final_criteria_sum / consuming_wallet_balances[2]) * *MULTI_COEFF_BY_100) - .as_u128(); - assert_eq!(co_3, 40_500_000); - vec![co_1, co_2, co_3] - }; - assert_eq!(result, expected_coefficients) - } - - #[test] - fn adjust_payments_works() { - init_test_logging(); - let test_name = "adjust_payments_works"; - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 444_444_444_444_444_444, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1234)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 666_666_666_666_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 22_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(78910)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); - let accounts_sum: u128 = - 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 - let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); - let setup_msg = PayablePaymentSetup { - qualified_payables, + let should_require = PayablePaymentSetup { + qualified_payables: vec![payable_1, payable_2], this_stage_data: ConsumingWalletBalancesAndGasParams { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(150), - masq_tokens_wei: consuming_wallet_masq_balance, + gas_currency_wei: U256::from(999_000_000_000_u64), + masq_tokens_wei: U256::from(299_000_000), }, - estimated_gas_limit_per_transaction: 165_000, - desired_gas_price_gwei: 222222222222222222, + estimated_gas_limit_per_transaction: 50_000, + desired_gas_price_gwei: 10, }, response_skeleton_opt: None, }; - let adjustment_setup = AwaitingAdjustment { - original_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; //TODO what to do with the required adjustment? - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - - let expected_criteria_computation_output = { - let time_criteria = vec![ - secs_elapsed(account_1.last_paid_timestamp, now), - secs_elapsed(account_2.last_paid_timestamp, now), - secs_elapsed(account_3.last_paid_timestamp, now), - ]; - let amount_criteria = vec![ - account_1.balance_wei * log_10(account_1.balance_wei) as u128, - account_2.balance_wei * log_10(account_2.balance_wei) as u128, - account_3.balance_wei * log_10(account_3.balance_wei) as u128, - ]; - let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( - vec![0, 0, 0], - |acc: Vec, current| { - vec![ - acc[0] + current[0], - acc[1] + current[1], - acc[2] + current[2], - ] - }, - ); - let final_criteria_sum = U256::from(final_criteria.iter().sum::()); - let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( - consuming_wallet_masq_balance, - final_criteria_sum, - ); - let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance - * U256::from(multiplication_coeff) - / final_criteria_sum) - .as_u128(); - let balanced_portions = vec![ - in_ratio_fragment_of_available_balance * final_criteria[0] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[1] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[2] / multiplication_coeff, - ]; - let new_total_amount_to_pay = balanced_portions.iter().sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance.as_u128()); - assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance.as_u128() * 100) / 102, - "new total amount to pay: {}, consuming wallet masq balance: {}", - new_total_amount_to_pay, - consuming_wallet_masq_balance - ); - let mut account_1_adjusted = account_1; - account_1_adjusted.balance_wei = balanced_portions[0]; - let mut account_2_adjusted = account_2; - account_2_adjusted.balance_wei = balanced_portions[1]; - let mut account_3_adjusted = account_3; - account_3_adjusted.balance_wei = balanced_portions[2]; - vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] - }; - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: expected_criteria_computation_output, - response_skeleton_opt: None - } - ); - let log_msg = format!( - "DEBUG: {test_name}: \n\ -|Adjusted payables: -|Account wallet Balance wei -| Original -| Adjusted -| -|0x0000000000000000000000000000000000616263 444444444444444444 -| 333000000000000051 -|0x0000000000000000000000000000000000646566 666666666666000000000000 -| 665999999999334000000004 -|0x000000000000000000000000000000000067686b 22000000000000 -| 12820500003284" - ); - TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); - } + let should_require_result = subject.is_adjustment_required(&should_require, &logger); - fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { - now.duration_since(timestamp).unwrap().as_secs() as u128 + assert_eq!(non_required_result, Ok(None)); + assert_eq!(should_require_result, Ok(None)); + TestLogHandler::default().exists_no_log_containing(test_name); } #[test] - fn output_with_response_skeleton_opt_some() { - todo!("rather include into some other special test??") + fn adjust_payments_returns_accounts_unadjusted() { + init_test_logging(); + let test_name = "is_adjustment_required_always_returns_none"; + let mut payable_1 = make_payable_account(111); + payable_1.balance_wei = 123_000_000; + let mut payable_2 = make_payable_account(222); + payable_2.balance_wei = 234_000_000; + let subject = PaymentAdjusterReal::new(); + let setup_msg = { + let payable_1 = payable_1.clone(); + let payable_2 = payable_2.clone(); + move |adjustment: Adjustment, response_skeleton_opt: Option| { + AwaitingAdjustment { + original_msg: PayablePaymentSetup { + qualified_payables: vec![payable_1, payable_2], + this_stage_data: ConsumingWalletBalancesAndGasParams { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(123_456_789), + masq_tokens_wei: U256::from(111_222_333_444_u64), + }, + estimated_gas_limit_per_transaction: 111_111, + desired_gas_price_gwei: 123, + }, + response_skeleton_opt, + }, + adjustment, + } + } + }; + let expected_msg = + move |response_skeleton_opt: Option| OutcomingPaymentsInstructions { + accounts: vec![payable_1, payable_2], + response_skeleton_opt, + }; + let response_skeleton_opt = Some(ResponseSkeleton { + client_id: 123, + context_id: 111, + }); + let logger = Logger::new(test_name); + + [ + (Adjustment::Gas { limiting_count: 1 }, None), + (Adjustment::Gas { limiting_count: 1 }, response_skeleton_opt), + (Adjustment::MasqToken, None), + (Adjustment::MasqToken, response_skeleton_opt), + (Adjustment::Both, None), + (Adjustment::Both, response_skeleton_opt), + ] + .into_iter() + .for_each(|(adjustment, response_skeleton_opt)| { + let setup_msg = setup_msg.clone(); + let expected_msg = expected_msg.clone(); + assert_eq!( + subject.adjust_payments( + setup_msg(adjustment, response_skeleton_opt), + SystemTime::now(), + &logger + ), + expected_msg(response_skeleton_opt) + ) + }); + + TestLogHandler::default().exists_no_log_containing(test_name); } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 63a5ffc2e..b28822270 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1,8 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod scanners_utils; pub mod payable_scan_setup_msgs; pub mod scan_mid_procedures; +pub mod scanners_utils; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; @@ -262,7 +262,7 @@ impl PayableScannerWithMidProcedures } impl PayableScannerMidProcedures for PayableScanner { - fn process_softly( + fn try_soft_process( &self, msg: PayablePaymentSetup, logger: &Logger, @@ -273,11 +273,11 @@ impl PayableScannerMidProcedures for PayableScanner { response_skeleton_opt: msg.response_skeleton_opt, })), Ok(Some(adjustment)) => Ok(Either::Right(AwaitingAdjustment::new(msg, adjustment))), - Err(e) => todo!(), + Err(_e) => todo!("be implemented with GH-711"), } } - fn process_with_adjustment( + fn process_adjustment( &self, setup: AwaitingAdjustment, logger: &Logger, @@ -1114,7 +1114,7 @@ pub struct PeriodicalScanConfig { } impl PeriodicalScanConfig { - pub fn schedule_another_periodic_scan(&self, ctx: &mut Context) { + pub fn next_scan_period(&self, ctx: &mut Context) { // the default of the message implies response_skeleton_opt to be None // because scheduled scans don't respond let _ = self.handle.notify_later(T::default(), self.interval, ctx); @@ -1144,11 +1144,11 @@ mod tests { use std::ops::Sub; use std::panic::{catch_unwind, AssertUnwindSafe}; - use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDaoError, PendingPayable, }; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; + use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs index ebdf57a03..08107715d 100644 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -1,44 +1,41 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::ResponseSkeleton; +use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, RequestBalancesToPayPayables}; +use actix::Message; - use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::ResponseSkeleton; - use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, RequestBalancesToPayPayables, - }; - use actix::Message; +#[derive(Debug, Message, PartialEq, Eq, Clone)] +pub struct PayablePaymentSetup { + //this field should stay private for anybody outside Accountant + pub(in crate::accountant) qualified_payables: Vec, + pub this_stage_data: T, + pub response_skeleton_opt: Option, +} - #[derive(Debug, Message, PartialEq, Eq, Clone)] - pub struct PayablePaymentSetup { - //this field should stay private for anybody outside Accountant - pub(in crate::accountant) qualified_payables: Vec, - pub this_stage_data: T, - pub response_skeleton_opt: Option, - } - - #[derive(Debug, PartialEq, Eq, Clone)] - pub struct ConsumingWalletBalancesAndGasParams { - pub consuming_wallet_balances: ConsumingWalletBalances, - pub estimated_gas_limit_per_transaction: u64, - pub desired_gas_price_gwei: u64, - } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ConsumingWalletBalancesAndGasParams { + pub consuming_wallet_balances: ConsumingWalletBalances, + pub estimated_gas_limit_per_transaction: u64, + pub desired_gas_price_gwei: u64, +} - impl - From<( +impl + From<( + RequestBalancesToPayPayables, + ConsumingWalletBalancesAndGasParams, + )> for PayablePaymentSetup +{ + fn from( + (previous_msg, current_stage_data): ( RequestBalancesToPayPayables, ConsumingWalletBalancesAndGasParams, - )> for PayablePaymentSetup - { - fn from( - (previous_msg, current_stage_data): ( - RequestBalancesToPayPayables, - ConsumingWalletBalancesAndGasParams, - ), - ) -> Self { - PayablePaymentSetup { - qualified_payables: previous_msg.accounts, - this_stage_data: current_stage_data, - response_skeleton_opt: previous_msg.response_skeleton_opt, - } + ), + ) -> Self { + PayablePaymentSetup { + qualified_payables: previous_msg.accounts, + this_stage_data: current_stage_data, + response_skeleton_opt: previous_msg.response_skeleton_opt, } } +} diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 7727c73ef..72cc7c7ac 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -1,9 +1,9 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; -use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use actix::Message; @@ -19,12 +19,12 @@ where } pub trait PayableScannerMidProcedures { - fn process_softly( + fn try_soft_process( &self, msg: PayablePaymentSetup, logger: &Logger, ) -> Result, String>; - fn process_with_adjustment( + fn process_adjustment( &self, setup: AwaitingAdjustment, logger: &Logger, @@ -42,6 +42,9 @@ impl AwaitingAdjustment { original_msg: PayablePaymentSetup, adjustment: Adjustment, ) -> Self { - todo!() + Self { + original_msg, + adjustment, + } } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index fe9379735..86c635124 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -3,7 +3,6 @@ #![cfg(test)] use crate::accountant::database_access_objects::banned_dao::{BannedDao, BannedDaoFactory}; -use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::database_access_objects::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; @@ -13,10 +12,11 @@ use crate::accountant::database_access_objects::pending_payable_dao::{ use crate::accountant::database_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; +use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; +use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::scanners::payable_scan_setup_msgs::{ ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, }; -use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; @@ -1381,10 +1381,16 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - is_adjustment_required_params: - Arc>>>, + is_adjustment_required_params: Arc< + Mutex< + Vec<( + PayablePaymentSetup, + Logger, + )>, + >, + >, is_adjustment_required_results: RefCell, AnalysisError>>>, - adjust_payments_params: Arc>>, + adjust_payments_params: Arc>>, adjust_payments_results: RefCell>, } @@ -1397,7 +1403,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { self.is_adjustment_required_params .lock() .unwrap() - .push(msg.clone()); + .push((msg.clone(), logger.clone())); self.is_adjustment_required_results.borrow_mut().remove(0) } @@ -1410,7 +1416,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { self.adjust_payments_params .lock() .unwrap() - .push((setup, now)); + .push((setup, now, logger.clone())); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1418,7 +1424,14 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn is_adjustment_required_params( mut self, - params: &Arc>>>, + params: &Arc< + Mutex< + Vec<( + PayablePaymentSetup, + Logger, + )>, + >, + >, ) -> Self { self.is_adjustment_required_params = params.clone(); self @@ -1436,7 +1449,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 0f90bcbf1..a7eaff350 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -308,9 +308,8 @@ impl BlockchainBridge { .gas_price() .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; - let estimated_gas_limit_per_transaction = self - .blockchain_interface - .estimated_gas_limit_per_transaction(); + let estimated_gas_limit_per_transaction = + self.blockchain_interface.estimated_gas_limit_per_payable(); let this_stage_data = ConsumingWalletBalancesAndGasParams { consuming_wallet_balances, @@ -512,8 +511,8 @@ struct PendingTxInfo { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; + use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::test_utils::make_pending_payable_fingerprint; use crate::blockchain::bip32::Bip32ECKeyProvider; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; @@ -686,7 +685,7 @@ mod tests { .get_gas_balance_result(Ok(gas_balance)) .get_token_balance_params(&get_token_balance_params_arc) .get_token_balance_result(Ok(token_balance)) - .estimated_gas_limit_per_transaction_result(51_546); + .estimated_gas_limit_per_payable_result(51_546); let consuming_wallet = make_paying_wallet(b"somewallet"); let persistent_configuration = PersistentConfigurationMock::default().gas_price_result(Ok(146)); diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface.rs index af8bd83c6..b10b3c328 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface.rs @@ -140,7 +140,7 @@ pub trait BlockchainInterface { recipient: &Wallet, ) -> Result; - fn estimated_gas_limit_per_transaction(&self) -> u64; + fn estimated_gas_limit_per_payable(&self) -> u64; fn send_batch_of_payables( &self, @@ -182,6 +182,7 @@ impl Default for BlockchainInterfaceClandestine { } impl BlockchainInterface for BlockchainInterfaceClandestine { + //TODO are the guts of these function really tested? I have doubts fn contract_address(&self) -> Address { self.chain.rec().contract } @@ -196,7 +197,7 @@ impl BlockchainInterface for BlockchainInterfaceClandestine { Err(BlockchainError::QueryFailed(msg)) } - fn estimated_gas_limit_per_transaction(&self) -> u64 { + fn estimated_gas_limit_per_payable(&self) -> u64 { todo!() } @@ -349,8 +350,8 @@ where .wait() } - fn estimated_gas_limit_per_transaction(&self) -> u64 { - todo!() + fn estimated_gas_limit_per_payable(&self) -> u64 { + Self::base_gas_limit(self.chain) + TRANSACTION_DATA_MARGIN_TO_GAS_LIMIT } fn send_batch_of_payables( @@ -627,7 +628,7 @@ where gas_price: u64, ) -> Result { let data = Self::transaction_data(recipient, amount); - let gas_limit = Self::compute_gas_limit(data.as_slice(), self.chain); //TODO this should by a const for each chain perhaps (excessive gas isn't consumed) + let gas_limit = Self::compute_gas_limit(data.as_slice(), self.chain); let gas_price = gwei_to_wei::(gas_price); let transaction_parameters = TransactionParameters { nonce: Some(nonce), @@ -705,10 +706,6 @@ where } } - fn gas_limit_safe_estimation(chain: Chain) -> u64 { - todo!("use transaction_data_margin here") - } - #[cfg(test)] fn web3(&self) -> &Web3 { &self.web3 @@ -1288,6 +1285,33 @@ mod tests { ) } + #[test] + fn blockchain_interface_non_clandestine_gives_estimates_for_gas_limits() { + let subject = |chain: Chain| { + BlockchainInterfaceNonClandestine::new( + TestTransport::default(), + make_fake_event_loop_handle(), + chain, + ) + }; + + [ + Chain::EthMainnet, + Chain::PolyMainnet, + Chain::PolyMumbai, + Chain::Dev, + ] + .into_iter() + .for_each(|chain| { + let subject = subject.clone(); + assert_eq!( + subject(chain).estimated_gas_limit_per_payable(), + BlockchainInterfaceNonClandestine::::base_gas_limit(chain) + + (68 * 68) //number of bytes and gas required per non-zero bytes = maximal margin + ) + }); + } + #[test] fn blockchain_interface_non_clandestine_can_transfer_tokens_in_batch() { //exercising also the layer of web3 functions, but the transport layer is mocked diff --git a/node/src/blockchain/test_utils.rs b/node/src/blockchain/test_utils.rs index a409e70e7..8300a6821 100644 --- a/node/src/blockchain/test_utils.rs +++ b/node/src/blockchain/test_utils.rs @@ -58,7 +58,7 @@ pub struct BlockchainInterfaceMock { retrieve_transactions_parameters: Arc>>, retrieve_transactions_results: RefCell>>, - estimated_gas_limit_per_transaction_results: RefCell>, + estimated_gas_limit_per_payable_results: RefCell>, send_batch_of_payables_params: Arc< Mutex< Vec<( @@ -100,9 +100,9 @@ impl BlockchainInterface for BlockchainInterfaceMock { self.retrieve_transactions_results.borrow_mut().remove(0) } - fn estimated_gas_limit_per_transaction(&self) -> u64 { + fn estimated_gas_limit_per_payable(&self) -> u64 { *self - .estimated_gas_limit_per_transaction_results + .estimated_gas_limit_per_payable_results .borrow_mut() .as_ref() .unwrap() @@ -173,8 +173,8 @@ impl BlockchainInterfaceMock { self } - pub fn estimated_gas_limit_per_transaction_result(self, result: u64) -> Self { - self.estimated_gas_limit_per_transaction_results + pub fn estimated_gas_limit_per_payable_result(self, result: u64) -> Self { + self.estimated_gas_limit_per_payable_results .borrow_mut() .replace(result); self diff --git a/node/tests/financials_test.rs b/node/tests/financials_test.rs index 948ed0ed6..f71d19bce 100644 --- a/node/tests/financials_test.rs +++ b/node/tests/financials_test.rs @@ -10,11 +10,11 @@ use masq_lib::messages::{ use masq_lib::test_utils::ui_connection::UiConnection; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::find_free_port; -use node_lib::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use node_lib::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::database_access_objects::receivable_dao::{ ReceivableDao, ReceivableDaoReal, }; +use node_lib::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use node_lib::accountant::gwei_to_wei; use node_lib::test_utils::make_wallet; use std::time::SystemTime; From 05eb924d2e598583904e0b642738435e367deff2 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 1 Jun 2023 11:59:42 +0200 Subject: [PATCH 020/250] GH-672: last details; adding tests for untested code from BlockchainInterfaceClandestine --- masq_lib/src/logger.rs | 2 +- node/src/accountant/mod.rs | 5 +- node/src/accountant/scanners/mod.rs | 20 +- .../scanners/scan_mid_procedures.rs | 10 +- node/src/blockchain/blockchain_interface.rs | 227 ++++++++++++++++-- 5 files changed, 221 insertions(+), 43 deletions(-) diff --git a/masq_lib/src/logger.rs b/masq_lib/src/logger.rs index 7fa801e18..455d0f574 100644 --- a/masq_lib/src/logger.rs +++ b/masq_lib/src/logger.rs @@ -53,7 +53,7 @@ pub fn prepare_log_recipient(recipient: Recipient) { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] pub struct Logger { name: String, #[cfg(not(feature = "no_test_share"))] diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 56d809a04..c1c2ec861 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -651,13 +651,13 @@ impl Accountant { &mut self, msg: PayablePaymentSetup, ) { - let bb_instructions = match self.scanners.payable.try_soft_process(msg, &self.logger) { + let bb_instructions = match self.scanners.payable.try_softly(msg, &self.logger) { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 self.scanners .payable - .process_adjustment(unaccepted_msg, &self.logger) + .get_special_payments_instructions(unaccepted_msg, &self.logger) } Err(_e) => todo!("be completed by GH-711"), }; @@ -1504,6 +1504,7 @@ mod tests { .build(); subject.scanners.payable = Box::new(payable_scanner); subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); + subject.logger = Logger::new(test_name); let subject_addr = subject.start(); let account_1 = make_payable_account(111_111); let account_2 = make_payable_account(222_222); diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index b28822270..ad71aa5db 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -12,7 +12,7 @@ use crate::accountant::scanners::payable_scan_setup_msgs::{ }; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scan_mid_procedures::{ - AwaitingAdjustment, PayableScannerMidProcedures, PayableScannerWithMidProcedures, + AwaitingAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, @@ -65,7 +65,7 @@ use web3::types::{TransactionReceipt, H256}; pub struct Scanners { pub payable: - Box>, + Box>, pub pending_payable: Box>, pub receivable: Box>, } @@ -256,13 +256,8 @@ impl Scanner for PayableScanner { implement_as_any!(); } -impl PayableScannerWithMidProcedures - for PayableScanner -{ -} - -impl PayableScannerMidProcedures for PayableScanner { - fn try_soft_process( +impl PayableScannerMiddleProcedures for PayableScanner { + fn try_softly( &self, msg: PayablePaymentSetup, logger: &Logger, @@ -277,7 +272,7 @@ impl PayableScannerMidProcedures for PayableScanner { } } - fn process_adjustment( + fn get_special_payments_instructions( &self, setup: AwaitingAdjustment, logger: &Logger, @@ -287,6 +282,11 @@ impl PayableScannerMidProcedures for PayableScanner { } } +impl PayableScannerWithMiddleProcedures + for PayableScanner +{ +} + impl PayableScanner { pub fn new( payable_dao: Box, diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 72cc7c7ac..bfebe63f1 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -10,21 +10,21 @@ use actix::Message; use itertools::Either; use masq_lib::logger::Logger; -pub trait PayableScannerWithMidProcedures: - Scanner + PayableScannerMidProcedures +pub trait PayableScannerWithMiddleProcedures: + Scanner + PayableScannerMiddleProcedures where BeginMessage: Message, EndMessage: Message, { } -pub trait PayableScannerMidProcedures { - fn try_soft_process( +pub trait PayableScannerMiddleProcedures { + fn try_softly( &self, msg: PayablePaymentSetup, logger: &Logger, ) -> Result, String>; - fn process_adjustment( + fn get_special_payments_instructions( &self, setup: AwaitingAdjustment, logger: &Logger, diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface.rs index b10b3c328..d2a836f43 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface.rs @@ -4,6 +4,7 @@ use crate::accountant::database_access_objects::payable_dao::{PayableAccount, Pe use crate::accountant::{comma_joined_stringifiable, gwei_to_wei}; use crate::blockchain::batch_payable_tools::{BatchPayableTools, BatchPayableToolsReal}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; +use crate::blockchain::blockchain_interface::blockchain_interface_common::TRANSACTION_DATA_GAS_MARGIN_MAX; use crate::blockchain::blockchain_interface::BlockchainError::{ InvalidAddress, InvalidResponse, InvalidUrl, QueryFailed, }; @@ -40,8 +41,6 @@ const TRANSACTION_LITERAL: H256 = H256([ const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; -const TRANSACTION_DATA_MARGIN_TO_GAS_LIMIT: u64 = transaction_data_margin(); - #[derive(Clone, Debug, Eq, Message, PartialEq)] pub struct BlockchainTransaction { pub block_number: u64, @@ -182,7 +181,6 @@ impl Default for BlockchainInterfaceClandestine { } impl BlockchainInterface for BlockchainInterfaceClandestine { - //TODO are the guts of these function really tested? I have doubts fn contract_address(&self) -> Address { self.chain.rec().contract } @@ -198,7 +196,7 @@ impl BlockchainInterface for BlockchainInterfaceClandestine { } fn estimated_gas_limit_per_payable(&self) -> u64 { - todo!() + blockchain_interface_common::base_gas_limit(self.chain) + TRANSACTION_DATA_GAS_MARGIN_MAX } fn send_batch_of_payables( @@ -217,12 +215,15 @@ impl BlockchainInterface for BlockchainInterfaceClandestine { } fn get_gas_balance(&self, _address: &Wallet) -> ResultForBalance { - error!(self.logger, "Can't get eth balance clandestinely yet",); + error!(self.logger, "Can't get gas balance clandestinely yet",); Ok(0.into()) } fn get_token_balance(&self, _address: &Wallet) -> ResultForBalance { - error!(self.logger, "Can't get token balance clandestinely yet",); + error!( + self.logger, + "Can't get masq token balance clandestinely yet", + ); Ok(0.into()) } @@ -351,7 +352,7 @@ where } fn estimated_gas_limit_per_payable(&self) -> u64 { - Self::base_gas_limit(self.chain) + TRANSACTION_DATA_MARGIN_TO_GAS_LIMIT + blockchain_interface_common::base_gas_limit(self.chain) + TRANSACTION_DATA_GAS_MARGIN_MAX } fn send_batch_of_payables( @@ -691,14 +692,25 @@ where } fn compute_gas_limit(data: &[u8], chain: Chain) -> U256 { - let base_gas_limit = Self::base_gas_limit(chain); + let base_gas_limit = blockchain_interface_common::base_gas_limit(chain); ethereum_types::U256::try_from(data.iter().fold(base_gas_limit, |acc, v| { acc + if v == &0u8 { 4 } else { 68 } })) .expect("Internal error") } - fn base_gas_limit(chain: Chain) -> u64 { + #[cfg(test)] + fn web3(&self) -> &Web3 { + &self.web3 + } +} + +mod blockchain_interface_common { + use masq_lib::blockchains::chains::{Chain, ChainFamily}; + + pub const TRANSACTION_DATA_GAS_MARGIN_MAX: u64 = transaction_data_margin(); + + pub fn base_gas_limit(chain: Chain) -> u64 { match chain.rec().chain_family { ChainFamily::Polygon => 70_000, ChainFamily::Eth => 55_000, @@ -706,18 +718,13 @@ where } } - #[cfg(test)] - fn web3(&self) -> &Web3 { - &self.web3 + pub const fn transaction_data_margin() -> u64 { + // 68 bytes * 68 per non zero byte according to the Ethereum docs, + // deliberately maximized + 68 * 68 } } -const fn transaction_data_margin() -> u64 { - // 68 bytes * 68 per non zero byte according to the Ethereum docs, - // deliberately maximized - 68 * 68 -} - #[cfg(test)] mod tests { use super::*; @@ -1306,8 +1313,7 @@ mod tests { let subject = subject.clone(); assert_eq!( subject(chain).estimated_gas_limit_per_payable(), - BlockchainInterfaceNonClandestine::::base_gas_limit(chain) - + (68 * 68) //number of bytes and gas required per non-zero bytes = maximal margin + blockchain_interface_common::base_gas_limit(chain) + (68 * 68) //number of bytes and gas required per non-zero bytes = maximal margin ) }); } @@ -1703,23 +1709,23 @@ mod tests { #[test] fn non_clandestine_base_gas_limit_is_properly_set() { assert_eq!( - BlockchainInterfaceNonClandestine::::base_gas_limit(Chain::PolyMainnet), + blockchain_interface_common::base_gas_limit(Chain::PolyMainnet), 70_000 ); assert_eq!( - BlockchainInterfaceNonClandestine::::base_gas_limit(Chain::PolyMumbai), + blockchain_interface_common::base_gas_limit(Chain::PolyMumbai), 70_000 ); assert_eq!( - BlockchainInterfaceNonClandestine::::base_gas_limit(Chain::EthMainnet), + blockchain_interface_common::base_gas_limit(Chain::EthMainnet), 55_000 ); assert_eq!( - BlockchainInterfaceNonClandestine::::base_gas_limit(Chain::EthRopsten), + blockchain_interface_common::base_gas_limit(Chain::EthRopsten), 55_000 ); assert_eq!( - BlockchainInterfaceNonClandestine::::base_gas_limit(Chain::Dev), + blockchain_interface_common::base_gas_limit(Chain::Dev), 55_000 ); } @@ -2546,4 +2552,175 @@ mod tests { ] ) } + + fn make_clandestine_subject(test_name: &str, chain: Chain) -> BlockchainInterfaceClandestine { + BlockchainInterfaceClandestine { + logger: Logger::new(test_name), + chain, + } + } + + fn all_chains() -> [Chain; 4] { + [ + Chain::EthMainnet, + Chain::PolyMainnet, + Chain::PolyMumbai, + Chain::Dev, + ] + } + + #[test] + fn blockchain_interface_clandestine_returns_contract_address() { + all_chains().into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject("irrelevant", chain).contract_address(), + chain.rec().contract + ) + }) + } + + #[test] + fn blockchain_interface_clandestine_retrieves_no_transactions() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_retrieves_no_transactions"; + let expected_msg = "Can't retrieve transactions clandestinely yet"; + let wallet = make_wallet("blah"); + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).retrieve_transactions(0, &wallet), + Err(BlockchainError::QueryFailed(expected_msg.to_string())) + ) + }); + + let expected_log_msg = format!("ERROR: {test_name}: {}", expected_msg); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } + + #[test] + fn blockchain_interface_clandestine_estimated_gas_limit_per_payable() { + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject("irrelevant", chain).estimated_gas_limit_per_payable(), + blockchain_interface_common::base_gas_limit(chain) + + TRANSACTION_DATA_GAS_MARGIN_MAX + ) + }); + } + + #[test] + fn blockchain_interface_clandestine_send_batch_of_payables() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_send_batch_of_payables"; + let wallet = make_wallet("blah"); + let chains = all_chains(); + let (recorder, _, _) = make_recorder(); + let recipient = recorder.start().recipient(); + let accounts = vec![make_payable_account(111)]; + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).send_batch_of_payables( + &wallet, + 123, + U256::one(), + &recipient, + &accounts + ), + Err(PayableTransactionError::Sending { + msg: "invalid attempt to send txs clandestinely".to_string(), + hashes: vec![], + }) + ) + }); + + let expected_log_msg = + format!("ERROR: {test_name}: Can't send transactions out clandestinely yet"); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } + + #[test] + fn blockchain_interface_clandestine_gets_no_gas_balance() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_gets_no_gas_balance"; + let wallet = make_wallet("blah"); + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).get_gas_balance(&wallet), + Ok(U256::zero()) + ) + }); + + let expected_log_msg = + format!("ERROR: {test_name}: Can't get gas balance clandestinely yet"); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } + + #[test] + fn blockchain_interface_clandestine_gets_no_token_balance() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_gets_no_token_balance"; + let wallet = make_wallet("blah"); + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).get_token_balance(&wallet), + Ok(U256::zero()) + ) + }); + + let expected_log_msg = + format!("ERROR: {test_name}: Can't get masq token balance clandestinely yet"); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } + + #[test] + fn blockchain_interface_clandestine_gets_no_transaction_count() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_gets_no_transaction_count"; + let wallet = make_wallet("blah"); + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).get_transaction_count(&wallet), + Ok(U256::zero()) + ) + }); + + let expected_log_msg = + format!("ERROR: {test_name}: Can't get transaction count clandestinely yet"); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } + + #[test] + fn blockchain_interface_clandestine_gets_no_transaction_receipt() { + init_test_logging(); + let test_name = "blockchain_interface_clandestine_gets_no_transaction_receipt"; + let tx_hash = make_tx_hash(123); + let chains = all_chains(); + + chains.into_iter().for_each(|chain| { + assert_eq!( + make_clandestine_subject(test_name, chain).get_transaction_receipt(tx_hash), + Ok(None) + ) + }); + + let expected_log_msg = + format!("ERROR: {test_name}: Can't get transaction receipt clandestinely yet"); + TestLogHandler::new() + .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + } } From 87b7ac9f178edc39ed680e63c9c598100dbb7ebb Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 1 Jun 2023 23:20:51 +0200 Subject: [PATCH 021/250] GH-672: almost done; some refactoring ahead --- node/src/accountant/mod.rs | 548 +++++++++--------- node/src/accountant/payment_adjuster.rs | 25 +- node/src/accountant/scanners/mod.rs | 72 ++- .../scanners/payable_scan_setup_msgs.rs | 40 +- .../scanners/scan_mid_procedures.rs | 13 +- node/src/accountant/test_utils.rs | 37 +- node/src/blockchain/blockchain_bridge.rs | 110 ++-- node/src/sub_lib/accountant.rs | 4 +- node/src/sub_lib/blockchain_bridge.rs | 11 +- node/src/test_utils/recorder.rs | 11 +- 10 files changed, 426 insertions(+), 445 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c1c2ec861..c79c81510 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -46,9 +46,7 @@ use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::accountant::{MessageIdGenerator, MessageIdGeneratorReal}; -use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, OutcomingPaymentsInstructions, RequestBalancesToPayPayables, -}; +use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::wallet::Wallet; @@ -68,7 +66,7 @@ use masq_lib::messages::{FromMessageBody, ToMessageBody, UiFinancialsRequest}; use masq_lib::ui_gateway::MessageTarget::ClientId; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::ExpectValue; -use scanners::payable_scan_setup_msgs::{ConsumingWalletBalancesAndGasParams, PayablePaymentSetup}; +use scanners::payable_scan_setup_msgs::PayablePaymentSetup; use std::any::type_name; #[cfg(test)] use std::default::Default; @@ -78,7 +76,6 @@ use std::path::Path; use std::rc::Rc; use std::time::SystemTime; use web3::types::{TransactionReceipt, H256}; - pub const CRASH_KEY: &str = "ACCOUNTANT"; pub const DEFAULT_PENDING_TOO_LONG_SEC: u64 = 21_600; //6 hours @@ -94,7 +91,7 @@ pub struct Accountant { scan_timings: ScanTimings, financial_statistics: Rc>, outcoming_payments_instructions_sub_opt: Option>, - request_balances_to_pay_payables_sub_opt: Option>, + pps_for_blockchain_bridge_sub_opt: Option>, retrieve_transactions_sub_opt: Option>, request_transaction_receipts_subs_opt: Option>, report_inbound_payments_sub_opt: Option>, @@ -206,14 +203,10 @@ impl Handler for Accountant { } } -impl Handler> for Accountant { +impl Handler for Accountant { type Result = (); - fn handle( - &mut self, - msg: PayablePaymentSetup, - _ctx: &mut Self::Context, - ) -> Self::Result { + fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) -> Self::Result { self.handle_payable_payment_setup(msg) } } @@ -434,7 +427,7 @@ impl Accountant { scan_timings: ScanTimings::new(scan_intervals), financial_statistics: Rc::clone(&financial_statistics), outcoming_payments_instructions_sub_opt: None, - request_balances_to_pay_payables_sub_opt: None, + pps_for_blockchain_bridge_sub_opt: None, report_sent_payables_sub_opt: None, retrieve_transactions_sub_opt: None, report_inbound_payments_sub_opt: None, @@ -454,7 +447,7 @@ impl Accountant { report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayablePaymentSetup + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -552,10 +545,10 @@ impl Accountant { Some(msg.peer_actors.blockchain_bridge.retrieve_transactions); self.report_inbound_payments_sub_opt = Some(msg.peer_actors.accountant.report_inbound_payments); - self.request_balances_to_pay_payables_sub_opt = Some( + self.pps_for_blockchain_bridge_sub_opt = Some( msg.peer_actors .blockchain_bridge - .request_balances_to_pay_payables, + .pps_for_blockchain_bridge, ); self.report_sent_payables_sub_opt = Some(msg.peer_actors.accountant.report_sent_payments); self.ui_message_sub_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); @@ -647,10 +640,7 @@ impl Accountant { }) } - fn handle_payable_payment_setup( - &mut self, - msg: PayablePaymentSetup, - ) { + fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) { let bb_instructions = match self.scanners.payable.try_softly(msg, &self.logger) { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { @@ -807,7 +797,7 @@ impl Accountant { &self.logger, ) { Ok(scan_message) => { - self.request_balances_to_pay_payables_sub_opt + self.pps_for_blockchain_bridge_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(scan_message) @@ -1013,8 +1003,9 @@ mod tests { use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::Adjustment; + use crate::accountant::scanners::payable_scan_setup_msgs::StageData; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; - use crate::accountant::scanners::NullScanner; + use crate::accountant::scanners::{BeginScanError, NullScanner, ScannerMock}; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; @@ -1074,6 +1065,7 @@ mod tests { use std::time::Duration; use std::vec; use web3::types::{TransactionReceipt, U256}; + use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; impl Handler> for Accountant { type Result = (); @@ -1330,13 +1322,14 @@ mod tests { system.run(); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &RequestBalancesToPayPayables { - accounts: vec![payable_account], + blockchain_bridge_recording.get_record::(0), + &PayablePaymentSetup { + qualified_payables: vec![payable_account], + this_stage_data_opt: None, response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, - }), + }) } ); } @@ -1412,14 +1405,14 @@ mod tests { let system = System::new("test"); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data: ConsumingWalletBalancesAndGasParams { + this_stage_data_opt: Some(StageData::FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(u32::MAX), }, estimated_gas_limit_per_transaction: 112_000, desired_gas_price_gwei: 123, - }, + }), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -1511,14 +1504,14 @@ mod tests { let system = System::new("test"); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data: ConsumingWalletBalancesAndGasParams { + this_stage_data_opt: Some(StageData::FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: U256::from(150_000_000_000_u64), }, estimated_gas_limit_per_transaction: 110_000, desired_gas_price_gwei: 0, - }, + }), response_skeleton_opt: Some(response_skeleton), }; @@ -1742,6 +1735,7 @@ mod tests { } #[test] + //TODO change this name!!!! fn accountant_sends_asks_blockchain_bridge_about_consuming_wallet_balances_when_qualified_payable_found( ) { let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); @@ -1771,11 +1765,12 @@ mod tests { system.run(); let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!(blockchain_bridge_recorder.len(), 1); - let message = blockchain_bridge_recorder.get_record::(0); + let message = blockchain_bridge_recorder.get_record::(0); assert_eq!( message, - &RequestBalancesToPayPayables { - accounts: qualified_payables, + &PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: None, response_skeleton_opt: None, } ); @@ -1783,43 +1778,43 @@ mod tests { #[test] fn accountant_sends_request_to_blockchain_bridge_to_scan_for_received_payments() { - // init_test_logging(); - // let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - // let earning_wallet = make_wallet("someearningwallet"); - // let system = System::new( - // "accountant_sends_request_to_blockchain_bridge_to_scan_for_received_payments", - // ); - // let receivable_dao = ReceivableDaoMock::new() - // .new_delinquencies_result(vec![]) - // .paid_delinquencies_result(vec![]); - // let mut subject = AccountantBuilder::default() - // .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) - // .receivable_daos(vec![ForReceivableScanner(receivable_dao)]) - // .build(); - // subject.scanners.pending_payable = Box::new(NullScanner::new()); - // subject.scanners.payable = Box::new(NullScanner::new()); - // let accountant_addr = subject.start(); - // let accountant_subs = Accountant::make_subs_from(&accountant_addr); - // let peer_actors = peer_actors_builder() - // .blockchain_bridge(blockchain_bridge) - // .build(); - // send_bind_message!(accountant_subs, peer_actors); - // - // send_start_message!(accountant_subs); - // - // System::current().stop(); - // system.run(); - // let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); - // assert_eq!(blockchain_bridge_recorder.len(), 1); - // let retrieve_transactions_msg = - // blockchain_bridge_recorder.get_record::(0); - // assert_eq!( - // retrieve_transactions_msg, - // &RetrieveTransactions { - // recipient: earning_wallet.clone(), - // response_skeleton_opt: None, - // } - // ); + init_test_logging(); + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let earning_wallet = make_wallet("someearningwallet"); + let system = System::new( + "accountant_sends_request_to_blockchain_bridge_to_scan_for_received_payments", + ); + let receivable_dao = ReceivableDaoMock::new() + .new_delinquencies_result(vec![]) + .paid_delinquencies_result(vec![]); + let mut subject = AccountantBuilder::default() + .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) + .receivable_daos(vec![ForReceivableScanner(receivable_dao)]) + .build(); + subject.scanners.pending_payable = Box::new(NullScanner::new()); + subject.scanners.payable = Box::new(NullScanner::new()); + let accountant_addr = subject.start(); + let accountant_subs = Accountant::make_subs_from(&accountant_addr); + let peer_actors = peer_actors_builder() + .blockchain_bridge(blockchain_bridge) + .build(); + send_bind_message!(accountant_subs, peer_actors); + + send_start_message!(accountant_subs); + + System::current().stop(); + system.run(); + let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); + assert_eq!(blockchain_bridge_recorder.len(), 1); + let retrieve_transactions_msg = + blockchain_bridge_recorder.get_record::(0); + assert_eq!( + retrieve_transactions_msg, + &RetrieveTransactions { + recipient: earning_wallet.clone(), + response_skeleton_opt: None, + } + ); } #[test] @@ -1932,205 +1927,206 @@ mod tests { #[test] fn periodical_scanning_for_receivables_and_delinquencies_works() { - // init_test_logging(); - // let test_name = "periodical_scanning_for_receivables_and_delinquencies_works"; - // let begin_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); - // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - // let receivable_scanner = ScannerMock::new() - // .begin_scan_params(&begin_scan_params_arc) - // .begin_scan_result(Err(BeginScanError::NothingToProcess)) - // .begin_scan_result(Ok(RetrieveTransactions { - // recipient: make_wallet("some_recipient"), - // response_skeleton_opt: None, - // })) - // .stop_the_system(); - // let mut config = make_bc_with_defaults(); - // config.scan_intervals_opt = Some(ScanIntervals { - // payable_scan_interval: Duration::from_secs(100), - // receivable_scan_interval: Duration::from_millis(99), - // pending_payable_scan_interval: Duration::from_secs(100), - // }); - // let mut subject = AccountantBuilder::default() - // .bootstrapper_config(config) - // .logger(Logger::new(test_name)) - // .build(); - // subject.scanners.payable = Box::new(NullScanner::new()); // Skipping - // subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping - // subject.scanners.receivable = Box::new(receivable_scanner); - // subject.scan_timings.receivable.handle = Box::new( - // NotifyLaterHandleMock::default() - // .notify_later_params(¬ify_later_receivable_params_arc) - // .permit_to_send_out(), - // ); - // let subject_addr = subject.start(); - // let subject_subs = Accountant::make_subs_from(&subject_addr); - // let peer_actors = peer_actors_builder().build(); - // send_bind_message!(subject_subs, peer_actors); - // - // send_start_message!(subject_subs); - // - // system.run(); - // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - // let notify_later_receivable_params = notify_later_receivable_params_arc.lock().unwrap(); - // TestLogHandler::new().exists_log_containing(&format!( - // "DEBUG: {test_name}: There was nothing to process during Receivables scan." - // )); - // assert_eq!(begin_scan_params.len(), 2); - // 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) - // ), - // ] - // ) + init_test_logging(); + let test_name = "periodical_scanning_for_receivables_and_delinquencies_works"; + let begin_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); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let receivable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(RetrieveTransactions { + recipient: make_wallet("some_recipient"), + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = make_bc_with_defaults(); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_secs(100), + receivable_scan_interval: Duration::from_millis(99), + pending_payable_scan_interval: Duration::from_secs(100), + }); + let mut subject = AccountantBuilder::default() + .bootstrapper_config(config) + .logger(Logger::new(test_name)) + .build(); + subject.scanners.payable = Box::new(NullScanner::new()); // Skipping + subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping + subject.scanners.receivable = Box::new(receivable_scanner); + subject.scan_timings.receivable.handle = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_receivable_params_arc) + .permit_to_send_out(), + ); + let subject_addr = subject.start(); + let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); + send_bind_message!(subject_subs, peer_actors); + + send_start_message!(subject_subs); + + system.run(); + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + let notify_later_receivable_params = notify_later_receivable_params_arc.lock().unwrap(); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: There was nothing to process during Receivables scan." + )); + assert_eq!(begin_scan_params.len(), 2); + 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) + ), + ] + ) } #[test] fn periodical_scanning_for_pending_payable_works() { - // init_test_logging(); - // let test_name = "periodical_scanning_for_pending_payable_works"; - // let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); - // let notify_later_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - // let system = System::new(test_name); - // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - // let pending_payable_scanner = ScannerMock::new() - // .begin_scan_params(&begin_scan_params_arc) - // .begin_scan_result(Err(BeginScanError::NothingToProcess)) - // .begin_scan_result(Ok(RequestTransactionReceipts { - // pending_payable: vec![], - // response_skeleton_opt: None, - // })) - // .stop_the_system(); - // let mut config = make_bc_with_defaults(); - // config.scan_intervals_opt = Some(ScanIntervals { - // payable_scan_interval: Duration::from_secs(100), - // receivable_scan_interval: Duration::from_secs(100), - // pending_payable_scan_interval: Duration::from_millis(98), - // }); - // let mut subject = AccountantBuilder::default() - // .bootstrapper_config(config) - // .logger(Logger::new(test_name)) - // .build(); - // subject.scanners.payable = Box::new(NullScanner::new()); //skipping - // subject.scanners.pending_payable = Box::new(pending_payable_scanner); - // subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - // subject.scan_timings.pending_payable.handle = Box::new( - // NotifyLaterHandleMock::default() - // .notify_later_params(¬ify_later_pending_payable_params_arc) - // .permit_to_send_out(), - // ); - // let subject_addr: Addr = subject.start(); - // let subject_subs = Accountant::make_subs_from(&subject_addr); - // let peer_actors = peer_actors_builder().build(); - // send_bind_message!(subject_subs, peer_actors); - // - // send_start_message!(subject_subs); - // - // system.run(); - // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - // let notify_later_pending_payable_params = - // notify_later_pending_payable_params_arc.lock().unwrap(); - // TestLogHandler::new().exists_log_containing(&format!( - // "DEBUG: {test_name}: There was nothing to process during PendingPayables scan." - // )); - // assert_eq!(begin_scan_params.len(), 2); - // assert_eq!( - // *notify_later_pending_payable_params, - // vec![ - // ( - // ScanForPendingPayables { - // response_skeleton_opt: None - // }, - // Duration::from_millis(98) - // ), - // ( - // ScanForPendingPayables { - // response_skeleton_opt: None - // }, - // Duration::from_millis(98) - // ), - // ] - // ) + init_test_logging(); + let test_name = "periodical_scanning_for_pending_payable_works"; + let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); + let notify_later_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); + let system = System::new(test_name); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let pending_payable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(RequestTransactionReceipts { + pending_payable: vec![], + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = make_bc_with_defaults(); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_secs(100), + receivable_scan_interval: Duration::from_secs(100), + pending_payable_scan_interval: Duration::from_millis(98), + }); + let mut subject = AccountantBuilder::default() + .bootstrapper_config(config) + .logger(Logger::new(test_name)) + .build(); + subject.scanners.payable = Box::new(NullScanner::new()); //skipping + subject.scanners.pending_payable = Box::new(pending_payable_scanner); + subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + subject.scan_timings.pending_payable.handle = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_pending_payable_params_arc) + .permit_to_send_out(), + ); + let subject_addr: Addr = subject.start(); + let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); + send_bind_message!(subject_subs, peer_actors); + + send_start_message!(subject_subs); + + system.run(); + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + let notify_later_pending_payable_params = + notify_later_pending_payable_params_arc.lock().unwrap(); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: There was nothing to process during PendingPayables scan." + )); + assert_eq!(begin_scan_params.len(), 2); + assert_eq!( + *notify_later_pending_payable_params, + vec![ + ( + ScanForPendingPayables { + response_skeleton_opt: None + }, + Duration::from_millis(98) + ), + ( + ScanForPendingPayables { + response_skeleton_opt: None + }, + Duration::from_millis(98) + ), + ] + ) } #[test] fn periodical_scanning_for_payable_works() { - // init_test_logging(); - // let test_name = "periodical_scanning_for_payable_works"; - // let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); - // let notify_later_payables_params_arc = Arc::new(Mutex::new(vec![])); - // let system = System::new(test_name); - // SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions - // let payable_scanner = ScannerMock::new() - // .begin_scan_params(&begin_scan_params_arc) - // .begin_scan_result(Err(BeginScanError::NothingToProcess)) - // .begin_scan_result(Ok(RequestBalancesToPayPayables { - // accounts: vec![], - // response_skeleton_opt: None, - // })) - // .stop_the_system(); - // let mut config = bc_from_earning_wallet(make_wallet("hi")); - // config.scan_intervals_opt = Some(ScanIntervals { - // payable_scan_interval: Duration::from_millis(97), - // receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner - // pending_payable_scan_interval: Duration::from_secs(100), // We'll never run this scanner - // }); - // let mut subject = AccountantBuilder::default() - // .bootstrapper_config(config) - // .logger(Logger::new(test_name)) - // .build(); - // subject.scanners.payable = Box::new(payable_scanner); - // subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping - // subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - // subject.scan_timings.payable.handle = Box::new( - // NotifyLaterHandleMock::default() - // .notify_later_params(¬ify_later_payables_params_arc) - // .permit_to_send_out(), - // ); - // let subject_addr = subject.start(); - // let subject_subs = Accountant::make_subs_from(&subject_addr); - // let peer_actors = peer_actors_builder().build(); - // send_bind_message!(subject_subs, peer_actors); - // - // send_start_message!(subject_subs); - // - // system.run(); - // //the second attempt is the one where the queue is empty and System::current.stop() ends the cycle - // let begin_scan_params = begin_scan_params_arc.lock().unwrap(); - // let notify_later_payables_params = notify_later_payables_params_arc.lock().unwrap(); - // TestLogHandler::new().exists_log_containing(&format!( - // "DEBUG: {test_name}: There was nothing to process during Payables scan." - // )); - // assert_eq!(begin_scan_params.len(), 2); - // assert_eq!( - // *notify_later_payables_params, - // vec![ - // ( - // ScanForPayables { - // response_skeleton_opt: None - // }, - // Duration::from_millis(97) - // ), - // ( - // ScanForPayables { - // response_skeleton_opt: None - // }, - // Duration::from_millis(97) - // ), - // ] - // ) + init_test_logging(); + let test_name = "periodical_scanning_for_payable_works"; + let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); + let notify_later_payables_params_arc = Arc::new(Mutex::new(vec![])); + let system = System::new(test_name); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let payable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(PayablePaymentSetup { + qualified_payables: vec![], + this_stage_data_opt: None, + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = bc_from_earning_wallet(make_wallet("hi")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(97), + receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + pending_payable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + }); + let mut subject = AccountantBuilder::default() + .bootstrapper_config(config) + .logger(Logger::new(test_name)) + .build(); + subject.scanners.payable = Box::new(payable_scanner); + subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping + subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + subject.scan_timings.payable.handle = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_payables_params_arc) + .permit_to_send_out(), + ); + let subject_addr = subject.start(); + let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); + send_bind_message!(subject_subs, peer_actors); + + send_start_message!(subject_subs); + + system.run(); + //the second attempt is the one where the queue is empty and System::current.stop() ends the cycle + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + let notify_later_payables_params = notify_later_payables_params_arc.lock().unwrap(); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: There was nothing to process during Payables scan." + )); + assert_eq!(begin_scan_params.len(), 2); + assert_eq!( + *notify_later_payables_params, + vec![ + ( + ScanForPayables { + response_skeleton_opt: None + }, + Duration::from_millis(97) + ), + ( + ScanForPayables { + response_skeleton_opt: None + }, + Duration::from_millis(97) + ), + ] + ) } #[test] @@ -2249,7 +2245,7 @@ mod tests { receivable_scan_interval: Duration::from_secs(50_000), }); let now = to_time_t(SystemTime::now()); - let accounts = vec![ + let qualified_payables = vec![ // slightly above minimum balance, to the right of the curve (time intersection) PayableAccount { wallet: make_wallet("wallet0"), @@ -2277,10 +2273,11 @@ mod tests { pending_payable_opt: None, }, ]; - let payable_dao = PayableDaoMock::default().non_pending_payables_result(accounts.clone()); + let payable_dao = + PayableDaoMock::default().non_pending_payables_result(qualified_payables.clone()); let (blockchain_bridge, _, blockchain_bridge_recordings_arc) = make_recorder(); - let blockchain_bridge = blockchain_bridge - .system_stop_conditions(match_every_type_id!(RequestBalancesToPayPayables)); + let blockchain_bridge = + blockchain_bridge.system_stop_conditions(match_every_type_id!(PayablePaymentSetup)); let system = System::new("scan_for_payable_message_triggers_payment_for_balances_over_the_curve"); let peer_actors = peer_actors_builder() @@ -2300,11 +2297,12 @@ mod tests { system.run(); let blockchain_bridge_recordings = blockchain_bridge_recordings_arc.lock().unwrap(); - let message = blockchain_bridge_recordings.get_record::(0); + let message = blockchain_bridge_recordings.get_record::(0); assert_eq!( message, - &RequestBalancesToPayPayables { - accounts, + &PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: None, response_skeleton_opt: None, } ); @@ -2318,11 +2316,11 @@ mod tests { let (blockchain_bridge, _, blockchain_bridge_recording) = make_recorder(); let blockchain_bridge_addr = blockchain_bridge .system_stop_conditions(match_every_type_id!( - RequestBalancesToPayPayables, - RequestBalancesToPayPayables + PayablePaymentSetup, + PayablePaymentSetup )) .start(); - let request_balances_to_pay_payables_sub = blockchain_bridge_addr.clone().recipient(); + let pps_for_blockchain_bridge_sub = blockchain_bridge_addr.clone().recipient(); let last_paid_timestamp = to_time_t(SystemTime::now()) - DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec as i64 - 1; @@ -2354,8 +2352,8 @@ mod tests { context_id: 444, }), }; - subject.request_balances_to_pay_payables_sub_opt = - Some(request_balances_to_pay_payables_sub); + subject.pps_for_blockchain_bridge_sub_opt = + Some(pps_for_blockchain_bridge_sub); let addr = subject.start(); addr.try_send(message_before.clone()).unwrap(); @@ -2381,12 +2379,12 @@ mod tests { let recording = blockchain_bridge_recording.lock().unwrap(); let messages_received = recording.len(); assert_eq!(messages_received, 2); - let first_message: &RequestBalancesToPayPayables = recording.get_record(0); + let first_message: &PayablePaymentSetup = recording.get_record(0); assert_eq!( first_message.response_skeleton_opt, message_before.response_skeleton_opt ); - let second_message: &RequestBalancesToPayPayables = recording.get_record(1); + let second_message: &PayablePaymentSetup = recording.get_record(1); assert_eq!( second_message.response_skeleton_opt, message_after.response_skeleton_opt diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 89a985f65..4ec832e40 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,8 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use lazy_static::lazy_static; @@ -18,7 +16,7 @@ lazy_static! { pub trait PaymentAdjuster { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, + msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError>; @@ -37,7 +35,7 @@ pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { fn is_adjustment_required( &self, - _msg: &PayablePaymentSetup, + _msg: &PayablePaymentSetup, _logger: &Logger, ) -> Result, AnalysisError> { Ok(None) @@ -77,9 +75,7 @@ pub enum AnalysisError {} #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; - use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, - }; + use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup, StageData}; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::test_utils::make_payable_account; use crate::accountant::ResponseSkeleton; @@ -90,6 +86,7 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::SystemTime; use web3::types::U256; + use StageData::FinancialDetails; #[test] fn is_adjustment_required_always_returns_none() { @@ -101,7 +98,7 @@ mod tests { payable_2.balance_wei = 200_000_000; let non_required = PayablePaymentSetup { qualified_payables: vec![payable_1.clone(), payable_2.clone()], - this_stage_data: ConsumingWalletBalancesAndGasParams { + this_stage_data_opt: Some(FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(1_001_000_000_000_u64), masq_tokens_wei: U256::from(301_000_000), @@ -109,7 +106,7 @@ mod tests { estimated_gas_limit_per_transaction: 50_000, desired_gas_price_gwei: 10, //gas amount to spent = 2 * 50_000 * 10 [gwei] = 1_000_000_000_000 wei - }, + }), response_skeleton_opt: None, }; let logger = Logger::new(test_name); @@ -119,14 +116,14 @@ mod tests { let should_require = PayablePaymentSetup { qualified_payables: vec![payable_1, payable_2], - this_stage_data: ConsumingWalletBalancesAndGasParams { + this_stage_data_opt: Some(FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(999_000_000_000_u64), masq_tokens_wei: U256::from(299_000_000), }, estimated_gas_limit_per_transaction: 50_000, desired_gas_price_gwei: 10, - }, + }), response_skeleton_opt: None, }; @@ -153,14 +150,14 @@ mod tests { AwaitingAdjustment { original_msg: PayablePaymentSetup { qualified_payables: vec![payable_1, payable_2], - this_stage_data: ConsumingWalletBalancesAndGasParams { + this_stage_data_opt: Some(FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(123_456_789), masq_tokens_wei: U256::from(111_222_333_444_u64), }, estimated_gas_limit_per_transaction: 111_111, desired_gas_price_gwei: 123, - }, + }), response_skeleton_opt, }, adjustment, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index ad71aa5db..798631598 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -7,9 +7,7 @@ pub mod scanners_utils; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDao, PendingPayable}; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; use crate::accountant::database_access_objects::receivable_dao::ReceivableDao; -use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scan_mid_procedures::{ AwaitingAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, @@ -42,7 +40,7 @@ use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; use crate::sub_lib::blockchain_bridge::{ - OutcomingPaymentsInstructions, RequestBalancesToPayPayables, + OutcomingPaymentsInstructions, }; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; @@ -64,8 +62,7 @@ use time::OffsetDateTime; use web3::types::{TransactionReceipt, H256}; pub struct Scanners { - pub payable: - Box>, + pub payable: Box>, pub pending_payable: Box>, pub receivable: Box>, } @@ -186,13 +183,13 @@ pub struct PayableScanner { pub payment_adjuster: Box, } -impl Scanner for PayableScanner { +impl Scanner for PayableScanner { fn begin_scan( &mut self, timestamp: SystemTime, response_skeleton_opt: Option, logger: &Logger, - ) -> Result { + ) -> Result { if let Some(timestamp) = self.scan_started_at() { return Err(BeginScanError::ScanAlreadyRunning(timestamp)); } @@ -206,10 +203,10 @@ impl Scanner for PayableScanner { investigate_debt_extremes(timestamp, &all_non_pending_payables) ); - let qualified_payable = + let qualified_payables = self.sniff_out_alarming_payables_and_maybe_log_them(all_non_pending_payables, logger); - match qualified_payable.is_empty() { + match qualified_payables.is_empty() { true => { self.mark_as_ended(logger); Err(BeginScanError::NothingToProcess) @@ -218,10 +215,11 @@ impl Scanner for PayableScanner { info!( logger, "Chose {} qualified debts to pay", - qualified_payable.len() + qualified_payables.len() ); - Ok(RequestBalancesToPayPayables { - accounts: qualified_payable, + Ok(PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: None, response_skeleton_opt, }) } @@ -259,7 +257,7 @@ impl Scanner for PayableScanner { impl PayableScannerMiddleProcedures for PayableScanner { fn try_softly( &self, - msg: PayablePaymentSetup, + msg: PayablePaymentSetup, logger: &Logger, ) -> Result, String> { match self.payment_adjuster.is_adjustment_required(&msg, logger) { @@ -282,10 +280,7 @@ impl PayableScannerMiddleProcedures for PayableScanner { } } -impl PayableScannerWithMiddleProcedures - for PayableScanner -{ -} +impl PayableScannerWithMiddleProcedures for PayableScanner {} impl PayableScanner { pub fn new( @@ -974,6 +969,26 @@ where implement_as_any!(); } +impl PayableScannerWithMiddleProcedures for NullScanner {} + +impl PayableScannerMiddleProcedures for NullScanner { + fn try_softly( + &self, + msg: PayablePaymentSetup, + logger: &Logger, + ) -> Result, String> { + todo!() + } + + fn get_special_payments_instructions( + &self, + setup: AwaitingAdjustment, + logger: &Logger, + ) -> OutcomingPaymentsInstructions { + todo!() + } +} + impl Default for NullScanner { fn default() -> Self { Self::new() @@ -1083,6 +1098,18 @@ impl ScannerMock { } } +impl PayableScannerWithMiddleProcedures for ScannerMock {} + +impl PayableScannerMiddleProcedures for ScannerMock{ + fn try_softly(&self, msg: PayablePaymentSetup, logger: &Logger) -> Result, String> { + todo!() + } + + fn get_special_payments_instructions(&self, setup: AwaitingAdjustment, logger: &Logger) -> OutcomingPaymentsInstructions { + todo!() + } +} + pub struct ScanTimings { pub pending_payable: PeriodicalScanConfig, pub payable: PeriodicalScanConfig, @@ -1150,6 +1177,7 @@ mod tests { use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::{Correct, Failed}; @@ -1160,7 +1188,6 @@ mod tests { use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, DEFAULT_PAYMENT_THRESHOLDS, }; - use crate::sub_lib::blockchain_bridge::RequestBalancesToPayPayables; use crate::test_utils::make_wallet; use actix::{Message, System}; use ethereum_types::U64; @@ -1291,8 +1318,9 @@ mod tests { assert_eq!(timestamp, Some(now)); assert_eq!( result, - Ok(RequestBalancesToPayPayables { - accounts: qualified_payable_accounts.clone(), + Ok(PayablePaymentSetup { + qualified_payables: qualified_payable_accounts.clone(), + this_stage_data_opt: None, response_skeleton_opt: None, }) ); @@ -2976,7 +3004,7 @@ mod tests { let logger = Logger::new(test_name); let log_handler = TestLogHandler::new(); - assert_elapsed_time_in_mark_as_ended::( + assert_elapsed_time_in_mark_as_ended::( &mut PayableScannerBuilder::new().build(), "Payables", test_name, diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs index 08107715d..6fe2883f9 100644 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -2,40 +2,32 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::ResponseSkeleton; -use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, RequestBalancesToPayPayables}; +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use actix::Message; #[derive(Debug, Message, PartialEq, Eq, Clone)] -pub struct PayablePaymentSetup { +pub struct PayablePaymentSetup { //this field should stay private for anybody outside Accountant pub(in crate::accountant) qualified_payables: Vec, - pub this_stage_data: T, + pub this_stage_data_opt: Option, pub response_skeleton_opt: Option, } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct ConsumingWalletBalancesAndGasParams { - pub consuming_wallet_balances: ConsumingWalletBalances, - pub estimated_gas_limit_per_transaction: u64, - pub desired_gas_price_gwei: u64, -} - -impl - From<( - RequestBalancesToPayPayables, - ConsumingWalletBalancesAndGasParams, - )> for PayablePaymentSetup -{ - fn from( - (previous_msg, current_stage_data): ( - RequestBalancesToPayPayables, - ConsumingWalletBalancesAndGasParams, - ), - ) -> Self { +impl From<(PayablePaymentSetup, StageData)> for PayablePaymentSetup { + fn from((previous_msg, this_stage_data): (PayablePaymentSetup, StageData)) -> Self { PayablePaymentSetup { - qualified_payables: previous_msg.accounts, - this_stage_data: current_stage_data, + qualified_payables: previous_msg.qualified_payables, + this_stage_data_opt: Some(this_stage_data), response_skeleton_opt: previous_msg.response_skeleton_opt, } } } + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum StageData { + FinancialDetails { + consuming_wallet_balances: ConsumingWalletBalances, + estimated_gas_limit_per_transaction: u64, + desired_gas_price_gwei: u64, + }, +} diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index bfebe63f1..6ae17ed12 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -1,9 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::Adjustment; -use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use actix::Message; @@ -21,7 +19,7 @@ where pub trait PayableScannerMiddleProcedures { fn try_softly( &self, - msg: PayablePaymentSetup, + msg: PayablePaymentSetup, logger: &Logger, ) -> Result, String>; fn get_special_payments_instructions( @@ -33,15 +31,12 @@ pub trait PayableScannerMiddleProcedures { #[derive(Debug, PartialEq, Eq)] pub struct AwaitingAdjustment { - pub original_msg: PayablePaymentSetup, + pub original_msg: PayablePaymentSetup, pub adjustment: Adjustment, } impl AwaitingAdjustment { - pub fn new( - original_msg: PayablePaymentSetup, - adjustment: Adjustment, - ) -> Self { + pub fn new(original_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { Self { original_msg, adjustment, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 86c635124..bc2929261 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -14,13 +14,11 @@ use crate::accountant::database_access_objects::receivable_dao::{ }; use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; -use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; -use crate::accountant::{gwei_to_wei, Accountant, DEFAULT_PENDING_TOO_LONG_SEC}; +use crate::accountant::{gwei_to_wei, Accountant, ResponseSkeleton, DEFAULT_PENDING_TOO_LONG_SEC}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::blockchain::test_utils::make_tx_hash; @@ -1381,14 +1379,7 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - is_adjustment_required_params: Arc< - Mutex< - Vec<( - PayablePaymentSetup, - Logger, - )>, - >, - >, + is_adjustment_required_params: Arc>>, is_adjustment_required_results: RefCell, AnalysisError>>>, adjust_payments_params: Arc>>, adjust_payments_results: RefCell>, @@ -1397,7 +1388,7 @@ pub struct PaymentAdjusterMock { impl PaymentAdjuster for PaymentAdjusterMock { fn is_adjustment_required( &self, - msg: &PayablePaymentSetup, + msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError> { self.is_adjustment_required_params @@ -1424,14 +1415,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn is_adjustment_required_params( mut self, - params: &Arc< - Mutex< - Vec<( - PayablePaymentSetup, - Logger, - )>, - >, - >, + params: &Arc>>, ) -> Self { self.is_adjustment_required_params = params.clone(); self @@ -1460,3 +1444,14 @@ impl PaymentAdjusterMock { self } } + +pub fn make_initial_payable_payment_setup_message( + qualified_payables: Vec, + response_skeleton_opt: Option, +) -> PayablePaymentSetup { + PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: None, + response_skeleton_opt, + } +} diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index a7eaff350..45671f303 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,8 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::scanners::payable_scan_setup_msgs::{ - ConsumingWalletBalancesAndGasParams, PayablePaymentSetup, -}; +use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup, StageData}; use crate::accountant::{ ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, }; @@ -18,7 +16,6 @@ use crate::db_config::persistent_configuration::{ }; use crate::sub_lib::blockchain_bridge::{ BlockchainBridgeSubs, ConsumingWalletBalances, OutcomingPaymentsInstructions, - RequestBalancesToPayPayables, }; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; @@ -48,8 +45,7 @@ pub struct BlockchainBridge { persistent_config: Box, set_consuming_wallet_subs_opt: Option>>, sent_payable_subs_opt: Option>, - balances_and_payables_sub_opt: - Option>>, + balances_and_payables_sub_opt: Option>, received_payments_subs_opt: Option>, scan_error_subs_opt: Option>, crashable: bool, @@ -140,12 +136,12 @@ impl Handler for BlockchainBridge { } } -impl Handler for BlockchainBridge { +impl Handler for BlockchainBridge { type Result = (); - fn handle(&mut self, msg: RequestBalancesToPayPayables, _ctx: &mut Self::Context) { + fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) { self.handle_scan( - Self::handle_request_balances_to_pay_payables, + Self::handle_payable_payment_setup, ScanType::Payables, msg, ); @@ -252,17 +248,16 @@ impl BlockchainBridge { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), - request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), + pps_for_blockchain_bridge: recipient!(addr, PayablePaymentSetup), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), request_transaction_receipts: recipient!(addr, RequestTransactionReceipts), } } - //TODO rename to something more summarizing - fn handle_request_balances_to_pay_payables( + fn handle_payable_payment_setup( &mut self, - msg: RequestBalancesToPayPayables, + msg: PayablePaymentSetup, ) -> Result<(), String> { let consuming_wallet = match self.consuming_wallet_opt.as_ref() { Some(wallet) => wallet, @@ -307,18 +302,14 @@ impl BlockchainBridge { .persistent_config .gas_price() .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; - let estimated_gas_limit_per_transaction = self.blockchain_interface.estimated_gas_limit_per_payable(); - - let this_stage_data = ConsumingWalletBalancesAndGasParams { + let this_stage_data = StageData::FinancialDetails { consuming_wallet_balances, estimated_gas_limit_per_transaction, desired_gas_price_gwei, }; - - let msg: PayablePaymentSetup = - (msg, this_stage_data).into(); + let msg = PayablePaymentSetup::from((msg, this_stage_data)); self.balances_and_payables_sub_opt .as_ref() @@ -513,7 +504,9 @@ mod tests { use super::*; use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; use crate::accountant::database_access_objects::utils::from_time_t; - use crate::accountant::test_utils::make_pending_payable_fingerprint; + use crate::accountant::test_utils::{ + make_initial_payable_payment_setup_message, make_pending_payable_fingerprint, + }; use crate::blockchain::bip32::Bip32ECKeyProvider; use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; use crate::blockchain::blockchain_interface::{ @@ -667,9 +660,9 @@ mod tests { } #[test] - fn handle_request_balances_to_pay_payables_reports_balances_and_payables_back_to_accountant() { + fn handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant() { let system = System::new( - "handle_request_balances_to_pay_payables_reports_balances_and_payables_back_to_accountant", + "handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant", ); let get_gas_balance_params_arc = Arc::new(Mutex::new(vec![])); let get_token_balance_params_arc = Arc::new(Mutex::new(vec![])); @@ -691,7 +684,7 @@ mod tests { PersistentConfigurationMock::default().gas_price_result(Ok(146)); let wallet_1 = make_wallet("booga"); let wallet_2 = make_wallet("gulp"); - let qualified_accounts = vec![ + let qualified_payables = vec![ PayableAccount { wallet: wallet_1.clone(), balance_wei: 78_654_321_124, @@ -718,16 +711,16 @@ mod tests { let addr = subject.start(); let subject_subs = BlockchainBridge::make_subs_from(&addr); let peer_actors = peer_actors_builder().accountant(accountant).build(); - send_bind_message!(subject_subs, peer_actors); - - addr.try_send(RequestBalancesToPayPayables { - accounts: qualified_accounts.clone(), - response_skeleton_opt: Some(ResponseSkeleton { + let msg = make_initial_payable_payment_setup_message( + qualified_payables.clone(), + Some(ResponseSkeleton { client_id: 11122, context_id: 444, }), - }) - .unwrap(); + ); + send_bind_message!(subject_subs, peer_actors); + + addr.try_send(msg.clone()).unwrap(); System::current().stop(); system.run(); @@ -737,18 +730,11 @@ mod tests { assert_eq!(*get_token_balance_params, vec![consuming_wallet]); let accountant_received_payment = accountant_recording_arc.lock().unwrap(); assert_eq!(accountant_received_payment.len(), 1); - let reported_balances_and_qualified_accounts: &PayablePaymentSetup< - ConsumingWalletBalancesAndGasParams, - > = accountant_received_payment.get_record(0); - let expected_msg: PayablePaymentSetup = ( - RequestBalancesToPayPayables { - accounts: qualified_accounts, - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 11122, - context_id: 444, - }), - }, - ConsumingWalletBalancesAndGasParams { + let reported_balances_and_qualified_accounts: &PayablePaymentSetup = + accountant_received_payment.get_record(0); + let expected_msg: PayablePaymentSetup = ( + msg, + StageData::FinancialDetails { consuming_wallet_balances: wallet_balances_found, estimated_gas_limit_per_transaction: 51_546, desired_gas_price_gwei: 146, @@ -779,18 +765,18 @@ mod tests { ); subject.logger = Logger::new(test_name); subject.scan_error_subs_opt = Some(scan_error_recipient); - let request = RequestBalancesToPayPayables { - accounts: vec![PayableAccount { + let request = make_initial_payable_payment_setup_message( + vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, }], - response_skeleton_opt: Some(ResponseSkeleton { + Some(ResponseSkeleton { client_id: 11, context_id: 2323, }), - }; + ); let subject_addr = subject.start(); let system = System::new(test_name); @@ -817,9 +803,9 @@ mod tests { } #[test] - fn handle_request_balances_to_pay_payables_fails_on_inspection_of_gas_balance() { + fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_gas_balance() { let test_name = - "handle_request_balances_to_pay_payables_fails_on_inspection_of_gas_balance"; + "handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_gas_balance"; let blockchain_interface = BlockchainInterfaceMock::default().get_gas_balance_result(Err( BlockchainError::QueryFailed("Lazy and yet you're asking for balances?".to_string()), )); @@ -830,9 +816,9 @@ mod tests { } #[test] - fn handle_request_balances_to_pay_payables_fails_on_inspection_of_token_balance() { + fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_token_balance() { let test_name = - "handle_request_balances_to_pay_payables_fails_on_inspection_of_token_balance"; + "handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_token_balance"; let blockchain_interface = BlockchainInterfaceMock::default() .get_gas_balance_result(Ok(U256::from(45678))) .get_token_balance_result(Err(BlockchainError::QueryFailed( @@ -845,7 +831,7 @@ mod tests { } #[test] - fn handle_request_balances_to_pay_payables_fails_at_missing_consuming_wallet() { + fn handle_payable_payment_setup_for_blockchain_bridge_fails_at_missing_consuming_wallet() { let blockchain_interface = BlockchainInterfaceMock::default(); let persistent_configuration = PersistentConfigurationMock::default(); let mut subject = BlockchainBridge::new( @@ -854,17 +840,17 @@ mod tests { false, None, ); - let request = RequestBalancesToPayPayables { - accounts: vec![PayableAccount { + let request = make_initial_payable_payment_setup_message( + vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 4254, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, }], - response_skeleton_opt: None, - }; + None, + ); - let result = subject.handle_request_balances_to_pay_payables(request); + let result = subject.handle_payable_payment_setup(request); assert_eq!( result, @@ -876,7 +862,7 @@ mod tests { } #[test] - fn handle_request_balances_to_pay_payables_fails_on_gas_price_query() { + fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_gas_price_query() { let blockchain_interface = BlockchainInterfaceMock::default() .get_gas_balance_result(Ok(U256::from(456789))) .get_token_balance_result(Ok(U256::from(7890123456_u64))); @@ -890,20 +876,20 @@ mod tests { false, Some(consuming_wallet), ); - let request = RequestBalancesToPayPayables { - accounts: vec![PayableAccount { + let request = make_initial_payable_payment_setup_message( + vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 123456, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, }], - response_skeleton_opt: Some(ResponseSkeleton { + Some(ResponseSkeleton { client_id: 123, context_id: 222, }), - }; + ); - let result = subject.handle_request_balances_to_pay_payables(request); + let result = subject.handle_payable_payment_setup(request); assert_eq!( result, diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 32a4bfdd4..01ddd33c0 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -3,7 +3,6 @@ use crate::accountant::database_access_objects::banned_dao::BannedDaoFactory; use crate::accountant::database_access_objects::payable_dao::PayableDaoFactory; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoFactory; use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoFactory; -use crate::accountant::scanners::payable_scan_setup_msgs::ConsumingWalletBalancesAndGasParams; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::{ checked_conversion, Accountant, ReceivedPayments, ReportTransactionReceipts, ScanError, @@ -96,8 +95,7 @@ pub struct AccountantSubs { pub report_routing_service_provided: Recipient, pub report_exit_service_provided: Recipient, pub report_services_consumed: Recipient, - pub report_consuming_wallet_balances_and_qualified_payables: - Recipient>, + pub report_consuming_wallet_balances_and_qualified_payables: Recipient, pub report_inbound_payments: Recipient, pub init_pending_payable_fingerprints: Recipient, pub report_transaction_receipts: Recipient, diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 8c4ade3bb..413d5e3cc 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::{RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder}; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::sub_lib::peer_actors::BindMessage; @@ -23,7 +24,7 @@ pub struct BlockchainBridgeConfig { pub struct BlockchainBridgeSubs { pub bind: Recipient, pub report_accounts_payable: Recipient, - pub request_balances_to_pay_payables: Recipient, + pub pps_for_blockchain_bridge: Recipient, pub retrieve_transactions: Recipient, pub ui_sub: Recipient, pub request_transaction_receipts: Recipient, @@ -35,13 +36,7 @@ impl Debug for BlockchainBridgeSubs { } } -#[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct RequestBalancesToPayPayables { - pub accounts: Vec, - pub response_skeleton_opt: Option, -} - -impl SkeletonOptHolder for RequestBalancesToPayPayables { +impl SkeletonOptHolder for PayablePaymentSetup { fn skeleton_opt(&self) -> Option { self.response_skeleton_opt } diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 24cf900dc..92a29fb2d 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::accountant::scanners::payable_scan_setup_msgs::ConsumingWalletBalancesAndGasParams; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ @@ -17,8 +16,8 @@ use crate::sub_lib::accountant::AccountantSubs; use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; +use crate::sub_lib::blockchain_bridge::BlockchainBridgeSubs; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, RequestBalancesToPayPayables}; use crate::sub_lib::configurator::{ConfiguratorSubs, NewPasswordMessage}; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{DispatcherSubs, StreamShutdownMsg}; @@ -27,7 +26,6 @@ use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage} use crate::sub_lib::hopper::{HopperSubs, MessageType}; use crate::sub_lib::neighborhood::ConnectionProgressMessage; use crate::sub_lib::neighborhood::NeighborhoodSubs; - use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; @@ -128,10 +126,9 @@ recorder_message_handler!(ReportServicesConsumedMessage); recorder_message_handler!(ReportExitServiceProvidedMessage); recorder_message_handler!(ReportRoutingServiceProvidedMessage); recorder_message_handler!(ScanError); -recorder_message_handler!(PayablePaymentSetup); +recorder_message_handler!(PayablePaymentSetup); recorder_message_handler!(SentPayables); recorder_message_handler!(SetConsumingWalletMessage); -recorder_message_handler!(RequestBalancesToPayPayables); recorder_message_handler!(StartMessage); recorder_message_handler!(StreamShutdownMsg); recorder_message_handler!(TransmitDataMsg); @@ -410,7 +407,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_consuming_wallet_balances_and_qualified_payables: recipient!( addr, - PayablePaymentSetup + PayablePaymentSetup ), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), @@ -433,7 +430,7 @@ pub fn make_blockchain_bridge_subs_from(addr: &Addr) -> BlockchainBrid BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), - request_balances_to_pay_payables: recipient!(addr, RequestBalancesToPayPayables), + pps_for_blockchain_bridge: recipient!(addr, PayablePaymentSetup), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), request_transaction_receipts: recipient!(addr, RequestTransactionReceipts), From ab0b1d8409b3d3f464cb44b6b860ae968d533ab3 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 1 Jun 2023 23:51:43 +0200 Subject: [PATCH 022/250] GH-672: heading to auto-review --- node/src/accountant/mod.rs | 26 ++- node/src/accountant/payment_adjust/mod.rs | 0 node/src/accountant/payment_adjuster.rs | 13 +- node/src/accountant/scanners/mod.rs | 179 +----------------- .../scanners/scan_mid_procedures.rs | 16 +- node/src/accountant/test_utils.rs | 171 ++++++++++++++++- node/src/blockchain/blockchain_bridge.rs | 14 +- node/src/blockchain/blockchain_interface.rs | 3 +- 8 files changed, 203 insertions(+), 219 deletions(-) delete mode 100644 node/src/accountant/payment_adjust/mod.rs diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c79c81510..01b2886d1 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -46,7 +46,7 @@ use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::accountant::{MessageIdGenerator, MessageIdGeneratorReal}; -use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::wallet::Wallet; @@ -545,11 +545,8 @@ impl Accountant { Some(msg.peer_actors.blockchain_bridge.retrieve_transactions); self.report_inbound_payments_sub_opt = Some(msg.peer_actors.accountant.report_inbound_payments); - self.pps_for_blockchain_bridge_sub_opt = Some( - msg.peer_actors - .blockchain_bridge - .pps_for_blockchain_bridge, - ); + self.pps_for_blockchain_bridge_sub_opt = + Some(msg.peer_actors.blockchain_bridge.pps_for_blockchain_bridge); self.report_sent_payables_sub_opt = Some(msg.peer_actors.accountant.report_sent_payments); self.ui_message_sub_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); self.request_transaction_receipts_subs_opt = Some( @@ -1005,15 +1002,15 @@ mod tests { use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::payable_scan_setup_msgs::StageData; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; - use crate::accountant::scanners::{BeginScanError, NullScanner, ScannerMock}; + use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, - BannedDaoFactoryMock, MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, - PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, - PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, + BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, + PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, + PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; @@ -1026,7 +1023,9 @@ mod tests { ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, }; - use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; + use crate::sub_lib::blockchain_bridge::{ + ConsumingWalletBalances, OutcomingPaymentsInstructions, + }; use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; @@ -1034,6 +1033,7 @@ mod tests { use crate::test_utils::recorder::Recorder; use crate::test_utils::recorder_stop_conditions::{StopCondition, StopConditions}; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; + use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; use crate::test_utils::unshared_test_utils::{ assert_on_initialization_with_panic_on_migration, make_bc_with_defaults, prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, @@ -1065,7 +1065,6 @@ mod tests { use std::time::Duration; use std::vec; use web3::types::{TransactionReceipt, U256}; - use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; impl Handler> for Accountant { type Result = (); @@ -2352,8 +2351,7 @@ mod tests { context_id: 444, }), }; - subject.pps_for_blockchain_bridge_sub_opt = - Some(pps_for_blockchain_bridge_sub); + subject.pps_for_blockchain_bridge_sub_opt = Some(pps_for_blockchain_bridge_sub); let addr = subject.start(); addr.try_send(message_before.clone()).unwrap(); diff --git a/node/src/accountant/payment_adjust/mod.rs b/node/src/accountant/payment_adjust/mod.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 4ec832e40..35a5183c2 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -3,15 +3,10 @@ use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use lazy_static::lazy_static; use masq_lib::logger::Logger; +#[cfg(test)] use std::any::Any; use std::time::SystemTime; -use web3::types::U256; - -lazy_static! { - static ref MULTI_COEFF_BY_100: U256 = U256::from(1000); -} pub trait PaymentAdjuster { fn is_adjustment_required( @@ -62,6 +57,12 @@ impl PaymentAdjusterReal { } } +impl Default for PaymentAdjusterReal { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 798631598..45ed86bc9 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -44,7 +44,7 @@ use crate::sub_lib::blockchain_bridge::{ }; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; -use actix::{Context, Message, System}; +use actix::{Context, Message}; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use masq_lib::logger::TIME_FORMATTING_STRING; @@ -55,7 +55,6 @@ use masq_lib::utils::ExpectValue; use std::any::Any; use std::cell::RefCell; use std::rc::Rc; -use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use time::format_description::parse; use time::OffsetDateTime; @@ -934,182 +933,6 @@ impl BeginScanError { } } -pub struct NullScanner {} - -impl Scanner for NullScanner -where - BeginMessage: Message, - EndMessage: Message, -{ - fn begin_scan( - &mut self, - _timestamp: SystemTime, - _response_skeleton_opt: Option, - _logger: &Logger, - ) -> Result { - Err(BeginScanError::CalledFromNullScanner) - } - - fn finish_scan(&mut self, _message: EndMessage, _logger: &Logger) -> Option { - panic!("Called finish_scan() from NullScanner"); - } - - fn scan_started_at(&self) -> Option { - panic!("Called scan_started_at() from NullScanner"); - } - - fn mark_as_started(&mut self, _timestamp: SystemTime) { - panic!("Called mark_as_started() from NullScanner"); - } - - fn mark_as_ended(&mut self, _logger: &Logger) { - panic!("Called mark_as_ended() from NullScanner"); - } - - implement_as_any!(); -} - -impl PayableScannerWithMiddleProcedures for NullScanner {} - -impl PayableScannerMiddleProcedures for NullScanner { - fn try_softly( - &self, - msg: PayablePaymentSetup, - logger: &Logger, - ) -> Result, String> { - todo!() - } - - fn get_special_payments_instructions( - &self, - setup: AwaitingAdjustment, - logger: &Logger, - ) -> OutcomingPaymentsInstructions { - todo!() - } -} - -impl Default for NullScanner { - fn default() -> Self { - Self::new() - } -} - -impl NullScanner { - pub fn new() -> Self { - Self {} - } -} - -pub struct ScannerMock { - begin_scan_params: Arc>>, - begin_scan_results: RefCell>>, - end_scan_params: Arc>>, - end_scan_results: RefCell>>, - stop_system_after_last_message: RefCell, -} - -impl Scanner - for ScannerMock -where - BeginMessage: Message, - EndMessage: Message, -{ - fn begin_scan( - &mut self, - _timestamp: SystemTime, - _response_skeleton_opt: Option, - _logger: &Logger, - ) -> Result { - self.begin_scan_params.lock().unwrap().push(()); - if self.is_allowed_to_stop_the_system() && self.is_last_message() { - System::current().stop(); - } - self.begin_scan_results.borrow_mut().remove(0) - } - - fn finish_scan(&mut self, message: EndMessage, _logger: &Logger) -> Option { - self.end_scan_params.lock().unwrap().push(message); - if self.is_allowed_to_stop_the_system() && self.is_last_message() { - System::current().stop(); - } - self.end_scan_results.borrow_mut().remove(0) - } - - fn scan_started_at(&self) -> Option { - intentionally_blank!() - } - - fn mark_as_started(&mut self, _timestamp: SystemTime) { - intentionally_blank!() - } - - fn mark_as_ended(&mut self, _logger: &Logger) { - intentionally_blank!() - } -} - -impl Default for ScannerMock { - fn default() -> Self { - Self::new() - } -} - -impl ScannerMock { - pub fn new() -> Self { - Self { - begin_scan_params: Arc::new(Mutex::new(vec![])), - begin_scan_results: RefCell::new(vec![]), - end_scan_params: Arc::new(Mutex::new(vec![])), - end_scan_results: RefCell::new(vec![]), - stop_system_after_last_message: RefCell::new(false), - } - } - - pub fn begin_scan_params(mut self, params: &Arc>>) -> Self { - self.begin_scan_params = params.clone(); - self - } - - pub fn begin_scan_result(self, result: Result) -> Self { - self.begin_scan_results.borrow_mut().push(result); - self - } - - pub fn stop_the_system(self) -> Self { - self.stop_system_after_last_message.replace(true); - self - } - - pub fn is_allowed_to_stop_the_system(&self) -> bool { - *self.stop_system_after_last_message.borrow() - } - - pub fn is_last_message(&self) -> bool { - self.is_last_message_from_begin_scan() || self.is_last_message_from_end_scan() - } - - pub fn is_last_message_from_begin_scan(&self) -> bool { - self.begin_scan_results.borrow().len() == 1 && self.end_scan_results.borrow().is_empty() - } - - pub fn is_last_message_from_end_scan(&self) -> bool { - self.end_scan_results.borrow().len() == 1 && self.begin_scan_results.borrow().is_empty() - } -} - -impl PayableScannerWithMiddleProcedures for ScannerMock {} - -impl PayableScannerMiddleProcedures for ScannerMock{ - fn try_softly(&self, msg: PayablePaymentSetup, logger: &Logger) -> Result, String> { - todo!() - } - - fn get_special_payments_instructions(&self, setup: AwaitingAdjustment, logger: &Logger) -> OutcomingPaymentsInstructions { - todo!() - } -} - pub struct ScanTimings { pub pending_payable: PeriodicalScanConfig, pub payable: PeriodicalScanConfig, diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 6ae17ed12..33170ff5b 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -19,14 +19,18 @@ where pub trait PayableScannerMiddleProcedures { fn try_softly( &self, - msg: PayablePaymentSetup, - logger: &Logger, - ) -> Result, String>; + _msg: PayablePaymentSetup, + _logger: &Logger, + ) -> Result, String> { + intentionally_blank!() + } fn get_special_payments_instructions( &self, - setup: AwaitingAdjustment, - logger: &Logger, - ) -> OutcomingPaymentsInstructions; + _setup: AwaitingAdjustment, + _logger: &Logger, + ) -> OutcomingPaymentsInstructions { + intentionally_blank!() + } } #[derive(Debug, PartialEq, Eq)] diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index bc2929261..ee815fb56 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -15,10 +15,16 @@ use crate::accountant::database_access_objects::receivable_dao::{ use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; -use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; +use crate::accountant::scanners::scan_mid_procedures::{ + AwaitingAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, +}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; -use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; -use crate::accountant::{gwei_to_wei, Accountant, ResponseSkeleton, DEFAULT_PENDING_TOO_LONG_SEC}; +use crate::accountant::scanners::{ + BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, Scanner, +}; +use crate::accountant::{ + gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, +}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::blockchain::test_utils::make_tx_hash; @@ -31,12 +37,14 @@ use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::make_bc_with_defaults; -use actix::System; +use actix::{Message, System}; use ethereum_types::H256; use masq_lib::logger::Logger; +use masq_lib::ui_gateway::NodeToUiMessage; use masq_lib::utils::plus; use rusqlite::{Connection, Row}; use std::any::type_name; +use std::any::Any; use std::cell::RefCell; use std::fmt::Debug; use std::rc::Rc; @@ -1455,3 +1463,158 @@ pub fn make_initial_payable_payment_setup_message( response_skeleton_opt, } } + +pub struct NullScanner {} + +impl Scanner for NullScanner +where + BeginMessage: Message, + EndMessage: Message, +{ + fn begin_scan( + &mut self, + _timestamp: SystemTime, + _response_skeleton_opt: Option, + _logger: &Logger, + ) -> Result { + Err(BeginScanError::CalledFromNullScanner) + } + + fn finish_scan(&mut self, _message: EndMessage, _logger: &Logger) -> Option { + panic!("Called finish_scan() from NullScanner"); + } + + fn scan_started_at(&self) -> Option { + panic!("Called scan_started_at() from NullScanner"); + } + + fn mark_as_started(&mut self, _timestamp: SystemTime) { + panic!("Called mark_as_started() from NullScanner"); + } + + fn mark_as_ended(&mut self, _logger: &Logger) { + panic!("Called mark_as_ended() from NullScanner"); + } + + implement_as_any!(); +} + +impl PayableScannerWithMiddleProcedures for NullScanner {} + +impl PayableScannerMiddleProcedures for NullScanner {} + +impl Default for NullScanner { + fn default() -> Self { + Self::new() + } +} + +impl NullScanner { + pub fn new() -> Self { + Self {} + } +} + +pub struct ScannerMock { + begin_scan_params: Arc>>, + begin_scan_results: RefCell>>, + end_scan_params: Arc>>, + end_scan_results: RefCell>>, + stop_system_after_last_message: RefCell, +} + +impl Scanner + for ScannerMock +where + BeginMessage: Message, + EndMessage: Message, +{ + fn begin_scan( + &mut self, + _timestamp: SystemTime, + _response_skeleton_opt: Option, + _logger: &Logger, + ) -> Result { + self.begin_scan_params.lock().unwrap().push(()); + if self.is_allowed_to_stop_the_system() && self.is_last_message() { + System::current().stop(); + } + self.begin_scan_results.borrow_mut().remove(0) + } + + fn finish_scan(&mut self, message: EndMessage, _logger: &Logger) -> Option { + self.end_scan_params.lock().unwrap().push(message); + if self.is_allowed_to_stop_the_system() && self.is_last_message() { + System::current().stop(); + } + self.end_scan_results.borrow_mut().remove(0) + } + + fn scan_started_at(&self) -> Option { + intentionally_blank!() + } + + fn mark_as_started(&mut self, _timestamp: SystemTime) { + intentionally_blank!() + } + + fn mark_as_ended(&mut self, _logger: &Logger) { + intentionally_blank!() + } +} + +impl Default for ScannerMock { + fn default() -> Self { + Self::new() + } +} + +impl ScannerMock { + pub fn new() -> Self { + Self { + begin_scan_params: Arc::new(Mutex::new(vec![])), + begin_scan_results: RefCell::new(vec![]), + end_scan_params: Arc::new(Mutex::new(vec![])), + end_scan_results: RefCell::new(vec![]), + stop_system_after_last_message: RefCell::new(false), + } + } + + pub fn begin_scan_params(mut self, params: &Arc>>) -> Self { + self.begin_scan_params = params.clone(); + self + } + + pub fn begin_scan_result(self, result: Result) -> Self { + self.begin_scan_results.borrow_mut().push(result); + self + } + + pub fn stop_the_system(self) -> Self { + self.stop_system_after_last_message.replace(true); + self + } + + pub fn is_allowed_to_stop_the_system(&self) -> bool { + *self.stop_system_after_last_message.borrow() + } + + pub fn is_last_message(&self) -> bool { + self.is_last_message_from_begin_scan() || self.is_last_message_from_end_scan() + } + + pub fn is_last_message_from_begin_scan(&self) -> bool { + self.begin_scan_results.borrow().len() == 1 && self.end_scan_results.borrow().is_empty() + } + + pub fn is_last_message_from_end_scan(&self) -> bool { + self.end_scan_results.borrow().len() == 1 && self.begin_scan_results.borrow().is_empty() + } +} + +impl PayableScannerWithMiddleProcedures + for ScannerMock +{ +} + +impl PayableScannerMiddleProcedures for ScannerMock {} diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 45671f303..b23994ad7 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -140,11 +140,7 @@ impl Handler for BlockchainBridge { type Result = (); fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) { - self.handle_scan( - Self::handle_payable_payment_setup, - ScanType::Payables, - msg, - ); + self.handle_scan(Self::handle_payable_payment_setup, ScanType::Payables, msg); } } @@ -255,10 +251,7 @@ impl BlockchainBridge { } } - fn handle_payable_payment_setup( - &mut self, - msg: PayablePaymentSetup, - ) -> Result<(), String> { + fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) -> Result<(), String> { let consuming_wallet = match self.consuming_wallet_opt.as_ref() { Some(wallet) => wallet, None => { @@ -660,7 +653,8 @@ mod tests { } #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant() { + fn handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant( + ) { let system = System::new( "handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant", ); diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface.rs index d2a836f43..0c5f6eeed 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface.rs @@ -12,7 +12,7 @@ use crate::sub_lib::wallet::Wallet; use actix::{Message, Recipient}; use futures::{future, Future}; use itertools::Either::{Left, Right}; -use masq_lib::blockchains::chains::{Chain, ChainFamily}; +use masq_lib::blockchains::chains::Chain; use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::logger::Logger; use masq_lib::utils::ExpectValue; @@ -750,6 +750,7 @@ mod tests { use ethsign_crypto::Keccak256; use jsonrpc_core::Version::V2; use jsonrpc_core::{Call, Error, ErrorCode, Id, MethodCall, Params}; + use masq_lib::blockchains::chains::ChainFamily; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::utils::{find_free_port, slice_of_strs_to_vec_of_strings}; From 2545ca94b0d23810cb20046dd25de58ca8fd2bbf Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 2 Jun 2023 09:54:53 +0200 Subject: [PATCH 023/250] GH-672: auto review findings --- node/src/accountant/mod.rs | 30 ++++++++-------- node/src/accountant/payment_adjuster.rs | 16 ++++----- node/src/accountant/scanners/mod.rs | 34 +++++++++---------- .../scanners/scan_mid_procedures.rs | 16 ++++----- node/src/accountant/test_utils.rs | 8 ++--- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 01b2886d1..931949c02 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -30,7 +30,7 @@ use crate::accountant::database_access_objects::utils::{ use crate::accountant::financials::visibility_restricted_module::{ check_query_is_within_tech_limits, financials_entry_check, }; -use crate::accountant::scanners::{ScanTimings, Scanners}; +use crate::accountant::scanners::{ScanSchedulers, Scanners}; use crate::blockchain::blockchain_bridge::{ PendingPayableFingerprint, PendingPayableFingerprintSeeds, RetrieveTransactions, }; @@ -88,7 +88,7 @@ pub struct Accountant { pending_payable_dao: Box, crashable: bool, scanners: Scanners, - scan_timings: ScanTimings, + scan_schedulers: ScanSchedulers, financial_statistics: Rc>, outcoming_payments_instructions_sub_opt: Option>, pps_for_blockchain_bridge_sub_opt: Option>, @@ -230,7 +230,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_payable(msg.response_skeleton_opt); - self.scan_timings.payable.next_scan_period(ctx); + self.scan_schedulers.payable.schedule(ctx); } } @@ -239,7 +239,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPendingPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_pending_payable(msg.response_skeleton_opt); - self.scan_timings.pending_payable.next_scan_period(ctx); + self.scan_schedulers.pending_payable.schedule(ctx); } } @@ -248,7 +248,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); - self.scan_timings.receivable.next_scan_period(ctx); + self.scan_schedulers.receivable.schedule(ctx); } } @@ -424,7 +424,7 @@ impl Accountant { pending_payable_dao, scanners, crashable: config.crash_point == CrashPoint::Message, - scan_timings: ScanTimings::new(scan_intervals), + scan_schedulers: ScanSchedulers::new(scan_intervals), financial_statistics: Rc::clone(&financial_statistics), outcoming_payments_instructions_sub_opt: None, pps_for_blockchain_bridge_sub_opt: None, @@ -644,7 +644,7 @@ impl Accountant { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 self.scanners .payable - .get_special_payments_instructions(unaccepted_msg, &self.logger) + .exacting_payments_instructions(unaccepted_msg, &self.logger) } Err(_e) => todo!("be completed by GH-711"), }; @@ -1001,7 +1001,7 @@ mod tests { use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::payable_scan_setup_msgs::StageData; - use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; + use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, @@ -1168,7 +1168,7 @@ mod tests { ); let financial_statistics = result.financial_statistics().clone(); - let scan_timings = result.scan_timings; + let scan_timings = result.scan_schedulers; scan_timings .pending_payable .handle @@ -1525,8 +1525,8 @@ mod tests { let (cwbqp_msg, captured_now, logger_clone) = adjust_payments_params.remove(0); assert_eq!( cwbqp_msg, - AwaitingAdjustment { - original_msg: consuming_balances_and_qualified_payments, + AwaitedAdjustment { + original_setup_msg: consuming_balances_and_qualified_payments, adjustment: Adjustment::MasqToken } ); @@ -1953,7 +1953,7 @@ mod tests { subject.scanners.payable = Box::new(NullScanner::new()); // Skipping subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping subject.scanners.receivable = Box::new(receivable_scanner); - subject.scan_timings.receivable.handle = Box::new( + subject.scan_schedulers.receivable.handle = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_receivable_params_arc) .permit_to_send_out(), @@ -2020,7 +2020,7 @@ mod tests { subject.scanners.payable = Box::new(NullScanner::new()); //skipping subject.scanners.pending_payable = Box::new(pending_payable_scanner); subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_timings.pending_payable.handle = Box::new( + subject.scan_schedulers.pending_payable.handle = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_pending_payable_params_arc) .permit_to_send_out(), @@ -2089,7 +2089,7 @@ mod tests { subject.scanners.payable = Box::new(payable_scanner); subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_timings.payable.handle = Box::new( + subject.scan_schedulers.payable.handle = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_payables_params_arc) .permit_to_send_out(), @@ -3275,7 +3275,7 @@ mod tests { let notify_later_half_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_scan_for_pending_payable_arc_cloned) .permit_to_send_out(); - subject.scan_timings.pending_payable.handle = Box::new(notify_later_half_mock); + subject.scan_schedulers.pending_payable.handle = Box::new(notify_later_half_mock); subject }); let mut peer_actors = peer_actors_builder().build(); diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 35a5183c2..db6405220 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; -use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; +use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use masq_lib::logger::Logger; #[cfg(test)] @@ -17,7 +17,7 @@ pub trait PaymentAdjuster { fn adjust_payments( &self, - setup: AwaitingAdjustment, + setup: AwaitedAdjustment, now: SystemTime, logger: &Logger, ) -> OutcomingPaymentsInstructions; @@ -38,13 +38,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &self, - setup: AwaitingAdjustment, + setup: AwaitedAdjustment, _now: SystemTime, _logger: &Logger, ) -> OutcomingPaymentsInstructions { OutcomingPaymentsInstructions { - accounts: setup.original_msg.qualified_payables, - response_skeleton_opt: setup.original_msg.response_skeleton_opt, + accounts: setup.original_setup_msg.qualified_payables, + response_skeleton_opt: setup.original_setup_msg.response_skeleton_opt, } } @@ -77,7 +77,7 @@ pub enum AnalysisError {} mod tests { use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup, StageData}; - use crate::accountant::scanners::scan_mid_procedures::AwaitingAdjustment; + use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::test_utils::make_payable_account; use crate::accountant::ResponseSkeleton; use crate::sub_lib::blockchain_bridge::{ @@ -148,8 +148,8 @@ mod tests { let payable_1 = payable_1.clone(); let payable_2 = payable_2.clone(); move |adjustment: Adjustment, response_skeleton_opt: Option| { - AwaitingAdjustment { - original_msg: PayablePaymentSetup { + AwaitedAdjustment { + original_setup_msg: PayablePaymentSetup { qualified_payables: vec![payable_1, payable_2], this_stage_data_opt: Some(FinancialDetails { consuming_wallet_balances: ConsumingWalletBalances { diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 45ed86bc9..72000ddca 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -10,7 +10,7 @@ use crate::accountant::database_access_objects::receivable_dao::ReceivableDao; use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scan_mid_procedures::{ - AwaitingAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, + AwaitedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, @@ -258,20 +258,20 @@ impl PayableScannerMiddleProcedures for PayableScanner { &self, msg: PayablePaymentSetup, logger: &Logger, - ) -> Result, String> { + ) -> Result, String> { match self.payment_adjuster.is_adjustment_required(&msg, logger) { Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, })), - Ok(Some(adjustment)) => Ok(Either::Right(AwaitingAdjustment::new(msg, adjustment))), + Ok(Some(adjustment)) => Ok(Either::Right(AwaitedAdjustment::new(msg, adjustment))), Err(_e) => todo!("be implemented with GH-711"), } } - fn get_special_payments_instructions( + fn exacting_payments_instructions( &self, - setup: AwaitingAdjustment, + setup: AwaitedAdjustment, logger: &Logger, ) -> OutcomingPaymentsInstructions { let now = SystemTime::now(); @@ -933,24 +933,24 @@ impl BeginScanError { } } -pub struct ScanTimings { - pub pending_payable: PeriodicalScanConfig, - pub payable: PeriodicalScanConfig, - pub receivable: PeriodicalScanConfig, +pub struct ScanSchedulers { + pub pending_payable: PeriodicalScanScheduler, + pub payable: PeriodicalScanScheduler, + pub receivable: PeriodicalScanScheduler, } -impl ScanTimings { +impl ScanSchedulers { pub fn new(scan_intervals: ScanIntervals) -> Self { - ScanTimings { - pending_payable: PeriodicalScanConfig { + ScanSchedulers { + pending_payable: PeriodicalScanScheduler { handle: Box::new(NotifyLaterHandleReal::default()), interval: scan_intervals.pending_payable_scan_interval, }, - payable: PeriodicalScanConfig { + payable: PeriodicalScanScheduler { handle: Box::new(NotifyLaterHandleReal::default()), interval: scan_intervals.payable_scan_interval, }, - receivable: PeriodicalScanConfig { + receivable: PeriodicalScanScheduler { handle: Box::new(NotifyLaterHandleReal::default()), interval: scan_intervals.receivable_scan_interval, }, @@ -958,13 +958,13 @@ impl ScanTimings { } } -pub struct PeriodicalScanConfig { +pub struct PeriodicalScanScheduler { pub handle: Box>, pub interval: Duration, } -impl PeriodicalScanConfig { - pub fn next_scan_period(&self, ctx: &mut Context) { +impl PeriodicalScanScheduler { + pub fn schedule(&self, ctx: &mut Context) { // the default of the message implies response_skeleton_opt to be None // because scheduled scans don't respond let _ = self.handle.notify_later(T::default(), self.interval, ctx); diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 33170ff5b..4daefcf6a 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -21,12 +21,12 @@ pub trait PayableScannerMiddleProcedures { &self, _msg: PayablePaymentSetup, _logger: &Logger, - ) -> Result, String> { + ) -> Result, String> { intentionally_blank!() } - fn get_special_payments_instructions( + fn exacting_payments_instructions( &self, - _setup: AwaitingAdjustment, + _setup: AwaitedAdjustment, _logger: &Logger, ) -> OutcomingPaymentsInstructions { intentionally_blank!() @@ -34,15 +34,15 @@ pub trait PayableScannerMiddleProcedures { } #[derive(Debug, PartialEq, Eq)] -pub struct AwaitingAdjustment { - pub original_msg: PayablePaymentSetup, +pub struct AwaitedAdjustment { + pub original_setup_msg: PayablePaymentSetup, pub adjustment: Adjustment, } -impl AwaitingAdjustment { - pub fn new(original_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { +impl AwaitedAdjustment { + pub fn new(original_setup_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { Self { - original_msg, + original_setup_msg, adjustment, } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index ee815fb56..749416edf 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -16,7 +16,7 @@ use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::{ - AwaitingAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, + AwaitedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{ @@ -1389,7 +1389,7 @@ impl PayableThresholdsGaugeMock { pub struct PaymentAdjusterMock { is_adjustment_required_params: Arc>>, is_adjustment_required_results: RefCell, AnalysisError>>>, - adjust_payments_params: Arc>>, + adjust_payments_params: Arc>>, adjust_payments_results: RefCell>, } @@ -1408,7 +1408,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, - setup: AwaitingAdjustment, + setup: AwaitedAdjustment, now: SystemTime, logger: &Logger, ) -> OutcomingPaymentsInstructions { @@ -1441,7 +1441,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self From 66a5b54e6a8e92c1840acc275bdd19de6eb857d9 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 2 Jun 2023 12:13:10 +0200 Subject: [PATCH 024/250] GH-672: refactored scan scheduling code --- masq_lib/src/messages.rs | 2 +- node/src/accountant/mod.rs | 96 +++++++++++++++------------- node/src/accountant/scanners/mod.rs | 98 ++++++++++++++++++++--------- node/src/accountant/test_utils.rs | 86 +++++++++++++++++++++++-- node/src/sub_lib/accountant.rs | 2 +- 5 files changed, 207 insertions(+), 77 deletions(-) diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index b0d6ae15f..c8a2f7079 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -775,7 +775,7 @@ conversation_message!(UiRecoverWalletsRequest, "recoverWallets"); pub struct UiRecoverWalletsResponse {} conversation_message!(UiRecoverWalletsResponse, "recoverWallets"); -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum ScanType { Payables, Receivables, diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 931949c02..39da3e0eb 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -81,7 +81,7 @@ pub const DEFAULT_PENDING_TOO_LONG_SEC: u64 = 21_600; //6 hours pub struct Accountant { suppress_initial_scans: bool, - consuming_wallet: Option, + consuming_wallet_opt: Option, earning_wallet: Rc, payable_dao: Box, receivable_dao: Box, @@ -230,7 +230,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_payable(msg.response_skeleton_opt); - self.scan_schedulers.payable.schedule(ctx); + self.schedule_next_scan(ScanType::Payables, ctx); } } @@ -239,7 +239,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForPendingPayables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_pending_payable(msg.response_skeleton_opt); - self.scan_schedulers.pending_payable.schedule(ctx); + self.schedule_next_scan(ScanType::PendingPayables, ctx); } } @@ -248,7 +248,7 @@ impl Handler for Accountant { fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); - self.scan_schedulers.receivable.schedule(ctx); + self.schedule_next_scan(ScanType::Receivables, ctx); } } @@ -417,7 +417,7 @@ impl Accountant { Accountant { suppress_initial_scans: config.suppress_initial_scans, - consuming_wallet: config.consuming_wallet_opt.clone(), + consuming_wallet_opt: config.consuming_wallet_opt.clone(), earning_wallet: Rc::clone(&earning_wallet), payable_dao, receivable_dao, @@ -532,7 +532,7 @@ impl Accountant { } fn our_wallet(&self, wallet: &Wallet) -> bool { - match &self.consuming_wallet { + match &self.consuming_wallet_opt { Some(ref consuming) if consuming.address() == wallet.address() => true, _ => wallet.address() == self.earning_wallet.address(), } @@ -557,6 +557,14 @@ impl Accountant { info!(self.logger, "Accountant bound"); } + fn schedule_next_scan(&self, scan_type: ScanType, ctx: &mut Context) { + self.scan_schedulers + .schedulers + .get(&scan_type) + .unwrap_or_else(|| panic!("Scan Scheduler {:?} not properly prepared", scan_type)) + .schedule(ctx) + } + fn handle_report_routing_service_provided_message( &mut self, msg: ReportRoutingServiceProvidedMessage, @@ -1007,10 +1015,11 @@ mod tests { ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, - BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, - PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, - PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, + assert_real_scan_schedulers, bc_from_earning_wallet, bc_from_wallets, make_payable_account, + make_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, + PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, + PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, + ReceivableDaoMock, ScannerMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; @@ -1021,12 +1030,11 @@ mod tests { use crate::match_every_type_id; use crate::sub_lib::accountant::{ ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, - DEFAULT_PAYMENT_THRESHOLDS, + DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; - use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; @@ -1168,24 +1176,10 @@ mod tests { ); let financial_statistics = result.financial_statistics().clone(); - let scan_timings = result.scan_schedulers; - scan_timings - .pending_payable - .handle - .as_any() - .downcast_ref::>() - .unwrap(); - scan_timings - .payable - .handle - .as_any() - .downcast_ref::>() - .unwrap(); - scan_timings - .receivable - .handle - .as_any() - .downcast_ref::>(); + assert_real_scan_schedulers(&result.scan_schedulers, ScanIntervals::default()); + assert_eq!(result.consuming_wallet_opt, None); + assert_eq!(*result.earning_wallet, *DEFAULT_EARNING_WALLET); + assert_eq!(result.suppress_initial_scans, false); result .message_id_generator .as_any() @@ -1953,10 +1947,14 @@ mod tests { subject.scanners.payable = Box::new(NullScanner::new()); // Skipping subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping subject.scanners.receivable = Box::new(receivable_scanner); - subject.scan_schedulers.receivable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_receivable_params_arc) - .permit_to_send_out(), + subject.scan_schedulers.update_scheduler( + ScanType::Receivables, + Some(Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_receivable_params_arc) + .permit_to_send_out(), + )), + None, ); let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); @@ -2020,10 +2018,14 @@ mod tests { subject.scanners.payable = Box::new(NullScanner::new()); //skipping subject.scanners.pending_payable = Box::new(pending_payable_scanner); subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_schedulers.pending_payable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_pending_payable_params_arc) - .permit_to_send_out(), + subject.scan_schedulers.update_scheduler( + ScanType::PendingPayables, + Some(Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_pending_payable_params_arc) + .permit_to_send_out(), + )), + None, ); let subject_addr: Addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); @@ -2089,10 +2091,14 @@ mod tests { subject.scanners.payable = Box::new(payable_scanner); subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping subject.scanners.receivable = Box::new(NullScanner::new()); //skipping - subject.scan_schedulers.payable.handle = Box::new( - NotifyLaterHandleMock::default() - .notify_later_params(¬ify_later_payables_params_arc) - .permit_to_send_out(), + subject.scan_schedulers.update_scheduler( + ScanType::Payables, + Some(Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_later_payables_params_arc) + .permit_to_send_out(), + )), + None, ); let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); @@ -3275,7 +3281,11 @@ mod tests { let notify_later_half_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_scan_for_pending_payable_arc_cloned) .permit_to_send_out(); - subject.scan_schedulers.pending_payable.handle = Box::new(notify_later_half_mock); + subject.scan_schedulers.update_scheduler( + ScanType::PendingPayables, + Some(Box::new(notify_later_half_mock)), + None, + ); subject }); let mut peer_actors = peer_actors_builder().build(); diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 72000ddca..7b0cf15be 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -54,6 +54,7 @@ use masq_lib::utils::ExpectValue; #[cfg(test)] use std::any::Any; use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; use std::time::{Duration, SystemTime}; use time::format_description::parse; @@ -934,27 +935,35 @@ impl BeginScanError { } pub struct ScanSchedulers { - pub pending_payable: PeriodicalScanScheduler, - pub payable: PeriodicalScanScheduler, - pub receivable: PeriodicalScanScheduler, + pub schedulers: HashMap>, } impl ScanSchedulers { pub fn new(scan_intervals: ScanIntervals) -> Self { - ScanSchedulers { - pending_payable: PeriodicalScanScheduler { - handle: Box::new(NotifyLaterHandleReal::default()), - interval: scan_intervals.pending_payable_scan_interval, - }, - payable: PeriodicalScanScheduler { - handle: Box::new(NotifyLaterHandleReal::default()), - interval: scan_intervals.payable_scan_interval, - }, - receivable: PeriodicalScanScheduler { - handle: Box::new(NotifyLaterHandleReal::default()), - interval: scan_intervals.receivable_scan_interval, - }, - } + let schedulers = HashMap::from_iter([ + ( + ScanType::Payables, + Box::new(PeriodicalScanScheduler:: { + handle: Box::new(NotifyLaterHandleReal::default()), + interval: scan_intervals.payable_scan_interval, + }) as Box, + ), + ( + ScanType::PendingPayables, + Box::new(PeriodicalScanScheduler:: { + handle: Box::new(NotifyLaterHandleReal::default()), + interval: scan_intervals.pending_payable_scan_interval, + }), + ), + ( + ScanType::Receivables, + Box::new(PeriodicalScanScheduler:: { + handle: Box::new(NotifyLaterHandleReal::default()), + interval: scan_intervals.receivable_scan_interval, + }), + ), + ]); + ScanSchedulers { schedulers } } } @@ -963,27 +972,43 @@ pub struct PeriodicalScanScheduler { pub interval: Duration, } -impl PeriodicalScanScheduler { - pub fn schedule(&self, ctx: &mut Context) { +pub trait ScanScheduler { + fn schedule(&self, ctx: &mut Context); + + declare_as_any!(); + + #[cfg(test)] + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +impl ScanScheduler for PeriodicalScanScheduler { + fn schedule(&self, ctx: &mut Context) { // the default of the message implies response_skeleton_opt to be None // because scheduled scans don't respond let _ = self.handle.notify_later(T::default(), self.interval, ctx); } + + implement_as_any!(); + + #[cfg(test)] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } #[cfg(test)] mod tests { use crate::accountant::scanners::{ - BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, Scanner, - ScannerCommon, Scanners, + BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, ScanSchedulers, + Scanner, ScannerCommon, Scanners, }; use crate::accountant::test_utils::{ - make_custom_payment_thresholds, make_payable_account, make_payables, - make_pending_payable_fingerprint, make_receivable_account, BannedDaoFactoryMock, - BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, - PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, - PendingPayableScannerBuilder, ReceivableDaoFactoryMock, ReceivableDaoMock, - ReceivableScannerBuilder, + assert_real_scan_schedulers, make_custom_payment_thresholds, make_payable_account, + make_payables, make_pending_payable_fingerprint, make_receivable_account, + BannedDaoFactoryMock, BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, + PayableScannerBuilder, PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, + PendingPayableDaoMock, PendingPayableScannerBuilder, ReceivableDaoFactoryMock, + ReceivableDaoMock, ReceivableScannerBuilder, }; use crate::accountant::{ gwei_to_wei, PendingPayableId, ReceivedPayments, ReportTransactionReceipts, @@ -1009,7 +1034,8 @@ mod tests { }; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::{ - DaoFactories, FinancialStatistics, PaymentThresholds, DEFAULT_PAYMENT_THRESHOLDS, + DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, + DEFAULT_PAYMENT_THRESHOLDS, }; use crate::test_utils::make_wallet; use actix::{Message, System}; @@ -2849,4 +2875,20 @@ mod tests { &log_handler, ); } + + #[test] + fn scan_schedulers_can_be_properly_initialized() { + let payable_interval = Duration::from_secs(240); + let pending_payable_interval = Duration::from_secs(300); + let receivable_interval = Duration::from_secs(360); + let scan_intervals = ScanIntervals { + payable_scan_interval: payable_interval, + pending_payable_scan_interval: pending_payable_interval, + receivable_scan_interval: receivable_interval, + }; + + let result = ScanSchedulers::new(scan_intervals); + + assert_real_scan_schedulers(&result, scan_intervals) + } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 749416edf..de8ee3405 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -20,10 +20,12 @@ use crate::accountant::scanners::scan_mid_procedures::{ }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{ - BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, Scanner, + BeginScanError, PayableScanner, PendingPayableScanner, PeriodicalScanScheduler, + ReceivableScanner, ScanSchedulers, Scanner, }; use crate::accountant::{ - gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, + gwei_to_wei, Accountant, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, + ScanForReceivables, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; @@ -31,15 +33,17 @@ use crate::blockchain::test_utils::make_tx_hash; use crate::bootstrapper::BootstrapperConfig; use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; -use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics}; +use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics, ScanIntervals}; use crate::sub_lib::accountant::{MessageIdGenerator, PaymentThresholds}; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; +use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::make_bc_with_defaults; use actix::{Message, System}; use ethereum_types::H256; use masq_lib::logger::Logger; +use masq_lib::messages::ScanType; use masq_lib::ui_gateway::NodeToUiMessage; use masq_lib::utils::plus; use rusqlite::{Connection, Row}; @@ -49,7 +53,7 @@ use std::cell::RefCell; use std::fmt::Debug; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use std::time::SystemTime; +use std::time::{Duration, SystemTime}; pub fn make_receivable_account(n: u64, expected_delinquent: bool) -> ReceivableAccount { let now = to_time_t(SystemTime::now()); @@ -1618,3 +1622,77 @@ impl PayableScannerWithMiddleProcedures } impl PayableScannerMiddleProcedures for ScannerMock {} + +impl ScanSchedulers { + pub fn update_scheduler( + &mut self, + scan_type: ScanType, + handle_opt: Option>>, + interval_opt: Option, + ) { + let scheduler = self + .schedulers + .get_mut(&scan_type) + .unwrap() + .as_any_mut() + .downcast_mut::>() + .unwrap(); + if let Some(new_handle) = handle_opt { + scheduler.handle = new_handle + } + if let Some(new_interval) = interval_opt { + scheduler.interval = new_interval + } + } +} + +pub fn assert_real_scan_schedulers(subject: &ScanSchedulers, scan_intervals: ScanIntervals) { + let payable_scheduler = subject + .schedulers + .get(&ScanType::Payables) + .unwrap() + .as_any() + .downcast_ref::>() + .unwrap(); + assert_eq!( + payable_scheduler.interval, + scan_intervals.payable_scan_interval + ); + payable_scheduler + .handle + .as_any() + .downcast_ref::>() + .unwrap(); + let pending_payable_scheduler = subject + .schedulers + .get(&ScanType::PendingPayables) + .unwrap() + .as_any() + .downcast_ref::>() + .unwrap(); + assert_eq!( + pending_payable_scheduler.interval, + scan_intervals.pending_payable_scan_interval + ); + pending_payable_scheduler + .handle + .as_any() + .downcast_ref::>() + .unwrap(); + let receivable_scheduler = subject + .schedulers + .get(&ScanType::Receivables) + .unwrap() + .as_any() + .downcast_ref::>() + .unwrap(); + assert_eq!( + receivable_scheduler.interval, + scan_intervals.receivable_scan_interval + ); + receivable_scheduler + .handle + .as_any() + .downcast_ref::>() + .unwrap(); +} diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 01ddd33c0..2c8fbb2fd 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -77,8 +77,8 @@ pub struct DaoFactories { #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct ScanIntervals { - pub pending_payable_scan_interval: Duration, pub payable_scan_interval: Duration, + pub pending_payable_scan_interval: Duration, pub receivable_scan_interval: Duration, } From c90355e5ced958447ccb8b0e1ebd060cc5a48505 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 6 Jun 2023 17:29:17 +0200 Subject: [PATCH 025/250] GH-711: just a log fixed in test --- node/src/accountant/payment_adjuster.rs | 55 +++++++++++-------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index ea4306205..520ea4b72 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -47,33 +47,27 @@ impl PaymentAdjuster for PaymentAdjusterReal { logger: &Logger, ) -> Result, AnalysisError> { let qualified_payables = msg.qualified_payables.as_slice(); - - // let total_gas_required_gwei = - // U256::from(msg.this_stage_data.estimated_gas_limit_per_transaction) - // * U256::from(qualified_payables.len()) - // * U256::from(msg.this_stage_data.desired_gas_price_gwei); - // eprintln!("total gwei required: {}", total_gas_required_gwei); - // let total_gas_required_wei = gwei_to_wei::(total_gas_required_gwei); - // eprintln!("available wei: {}", msg.this_stage_data.consuming_wallet_balances.gas_currency_wei); - // let limit_by_gas_opt = if total_gas_required_wei - // <= msg - // .this_stage_data - // .consuming_wallet_balances - // .gas_currency_wei - // { - // //TODO drive in both < and = - // false - // } else { - // true - // }; - //TODO use question mark later - let limit_by_gas_opt = match Self::determine_feasible_count_to_pay_regarding_gas( + let limit_by_gas_opt = match Self::determine_transactions_count_limint_by_gas( &msg.this_stage_data, qualified_payables.len(), ) { Ok(None) => None, - Ok(Some(limiting_count)) => Some(limiting_count), + Ok(Some(limiting_count)) => { + warning!( + logger, + "Gas amount {} wei cannot cover anticipated fees from sending {} \ + transactions. Maximum is {}. The payments need to be adjusted in \ + their count.", + msg.this_stage_data + .consuming_wallet_balances + .masq_tokens_wei + .separate_with_commas(), + qualified_payables.len(), + limiting_count + ); + Some(limiting_count) + } Err(e) => todo!(), }; @@ -89,7 +83,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { todo!() } else { - Self::log_adjustment_required(logger, required_masq_sum, cw_masq_balance); + Self::log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance); true }; @@ -173,7 +167,7 @@ impl PaymentAdjusterReal { .into() } - fn determine_feasible_count_to_pay_regarding_gas( + fn determine_transactions_count_limint_by_gas( tech_info: &ConsumingWalletBalancesAndGasParams, required_max_count: usize, ) -> Result, AnalysisError> { @@ -340,7 +334,7 @@ impl PaymentAdjusterReal { } } - fn log_adjustment_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { + fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { warning!( logger, "Total of {} wei in MASQ was ordered while the consuming wallet held \ @@ -547,12 +541,11 @@ mod tests { limiting_count: expected_limiting_count })) ); - // TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Payments for wallets \ - // 0x00000000000000000000000077616c6c65743835, 0x00000000000000000000000077616c6c65743136 would \ - // require 100 gwei wei while the consuming wallet holds only 100,000,000,000 wei. \ - // Going to adjust them to fit in the limit, by cutting back the number of payments or their \ - // size.")); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: blaaaah msg")); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Gas amount 18,446,744,073,709,551,615,000,000,000 wei \ + cannot cover anticipated fees from sending 3 transactions. Maximum is 2. \ + The payments need to be adjusted in their count." + )); } #[test] From 449e19695c2ba7121e685b361a5f733720a5d258 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 8 Jun 2023 23:47:15 +0200 Subject: [PATCH 026/250] GH-711: debug machinery improved and works right --- node/src/accountant/payment_adjuster.rs | 350 ++++++++++++++++-------- 1 file changed, 241 insertions(+), 109 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index ef572aac2..d80d73558 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -9,15 +9,18 @@ use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; -use itertools::Itertools; +use futures::collect; +use itertools::{Either, Itertools}; use lazy_static::lazy_static; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::logger::Logger; +use rand::distributions::uniform::SampleBorrow; use rusqlite::ffi::sqlite3_keyword_name; #[cfg(test)] use std::any::Any; use std::collections::HashMap; use std::iter::{once, successors}; +use std::ops::Not; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; @@ -57,23 +60,17 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; //TODO use question mark later - let limit_by_gas_opt = match Self::determine_transactions_count_limint_by_gas( + let limit_by_gas_opt = match Self::determine_transactions_count_limit_by_gas( &this_stage_data, qualified_payables.len(), ) { Ok(None) => None, Ok(Some(limiting_count)) => { - warning!( + Self::log_insufficient_gas_balance( logger, - "Gas amount {} wei cannot cover anticipated fees from sending {} \ - transactions. Maximum is {}. The payments need to be adjusted in \ - their count.", - this_stage_data - .consuming_wallet_balances - .masq_tokens_wei - .separate_with_commas(), - qualified_payables.len(), - limiting_count + qualified_payables, + this_stage_data, + limiting_count, ); Some(limiting_count) } @@ -113,6 +110,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { StageData::FinancialAndTechDetails(details) => details, }; let qualified_payables: Vec = msg.qualified_payables; + let debug_log_printer_opt = logger .debug_enabled() @@ -129,17 +127,21 @@ impl PaymentAdjuster for PaymentAdjusterReal { Adjustment::MasqToken => None, }; - let adjusted_accounts = Self::run_recursively( + let (adjusted_accounts, debug_info_opt) = Self::run_recursively( current_stage_data, initial_known_elimination_limits, qualified_payables, now, + logger.debug_enabled(), ); debug!( logger, "{}", - debug_log_printer_opt.expect("debug message missing")(&adjusted_accounts) + debug_log_printer_opt.expect("debug message missing")( + debug_info_opt.expectv("debug info"), + &adjusted_accounts + ) ); OutcomingPaymentsInstructions { @@ -189,7 +191,7 @@ impl PaymentAdjusterReal { .into() } - fn determine_transactions_count_limint_by_gas( + fn determine_transactions_count_limit_by_gas( tech_info: &FinancialAndTechDetails, required_max_count: usize, ) -> Result, AnalysisError> { @@ -334,58 +336,91 @@ impl PaymentAdjusterReal { } fn format_brief_accounts_summary( - mut original_accounts: HashMap>, - adjusted_accounts: impl Iterator, + mut original_accounts: HashMap, + adjusted_accounts: impl Iterator, ) -> String { - adjusted_accounts.for_each(|(wallet, balance)| { - let _ = original_accounts - .entry(wallet) - .and_modify(|balances| balances.push(balance)); + adjusted_accounts.for_each(|(criterion, wallet, adjusted_balance)| { + let _ = original_accounts.entry(wallet).and_modify(|details| { + let _ = { + details.adjusted_balance_opt.replace(adjusted_balance); + details.used_criterion = criterion + }; + }); }); let (adjusted, excluded): (Vec<_>, Vec<_>) = original_accounts .into_iter() - .partition(|(wallet, balances)| balances.len() > 1); - let adjusted_accounts = adjusted + .partition(|(wallet, details)| details.adjusted_balance_opt.is_some()); + let adjusted_accounts_as_strings = adjusted .into_iter() - .map(|(wallet, balances)| { + .sorted_by(|(_, details_a), (_, details_b)| { + Ord::cmp(&details_b.used_criterion, &details_a.used_criterion) + }) + .map(|(wallet, details)| { format!( "{} {}\n{:^length$} {}", wallet, - balances[0], + details.initial_balance, "", - balances[1], + details.adjusted_balance_opt.expectv("adjusted balance"), length = WALLET_ADDRESS_LENGTH ) }) + .chain(once("\n".to_string())) .join("\n"); - let excluded_accounts = once("Excluded less important accounts:\n".to_string()) - .chain( - excluded - .into_iter() - .map(|(wallet, balance)| format!("{} {}", wallet, balance[0])), - ) - .join("\n"); - vec![adjusted_accounts, excluded_accounts].join("\n") + eprintln!("excluded {:?}", excluded); + let excluded_accounts_opt = excluded.is_empty().not().then(|| todo!()); + + // once("Excluded less important accounts:\n".to_string()) + // .chain( + // excluded + // .into_iter() + // .map(|(wallet, balance)| format!("{} {}", wallet, balance[0])), + // ) + // .join("\n")}; + + vec![Some(adjusted_accounts_as_strings), excluded_accounts_opt] + .into_iter() + .flatten() + .join("\n") } - fn wallets_and_balances<'a>( - accounts: &'a [PayableAccount], - ) -> impl Iterator + 'a { - accounts - .iter() - .map(|account| (account.wallet.clone(), account.balance_wei)) + fn wallets_and_balances_with_criteria_opt<'a>( + accounts: Either<&'a [PayableAccount], &'a [(u128, &'a PayableAccount)]>, + ) -> Either< + impl Iterator + 'a, + impl Iterator + 'a, + > { + match accounts { + Either::Left(just_accounts) => Either::Left( + just_accounts + .iter() + .map(|account| (account.wallet.clone(), account.balance_wei)), + ), + Either::Right(criteria_and_accounts) => { + Either::Right(criteria_and_accounts.iter().map(|(criterion, account)| { + (*criterion, account.wallet.clone(), account.balance_wei) + })) + } + } } fn before_and_after_debug_msg_formatter( original: &[PayableAccount], - ) -> impl FnOnce(&[PayableAccount]) -> String { - let original_prefabricated = Self::wallets_and_balances(original) - .map(|(wallet, balance)| (wallet, vec![balance])) - .collect::>>(); - move |adjusted_accounts: &[PayableAccount]| { + ) -> impl FnOnce(Vec, &[PayableAccount]) -> String { + let original_prefabricated = + Self::wallets_and_balances_with_criteria_opt(Either::Left(original)) + .left() + .expectv("wallet + balance") + .map(|(wallet, balance)| (wallet, AccountAdjustmentDetails::new(balance))) + .collect::>(); + move |criteria: Vec, adjusted_accounts: &[PayableAccount]| { + let concise_input = criteria + .into_iter() + .zip(adjusted_accounts.iter()) + .collect::>(); let prefabricated_adjusted = //TODO extend the collection of adjusted up to the initial length using Option - Self::wallets_and_balances(adjusted_accounts); + Self::wallets_and_balances_with_criteria_opt(Either::Right(&concise_input)).right().expectv("criterion + wallet + balance"); format!( "\nAdjusted payables:\n\ {:<42} {}\n\ @@ -408,9 +443,8 @@ impl PaymentAdjusterReal { fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { warning!( logger, - "Total of {} wei in MASQ was ordered while the consuming wallet held \ - only {} wei of the MASQ token. Adjustment in their count or the amounts \ - is required.", + "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ + the MASQ token. Adjustment in their count or the amounts is required.", payables_sum.separate_with_commas(), cw_masq_balance.separate_with_commas() ) @@ -421,12 +455,20 @@ impl PaymentAdjusterReal { known_elimination_limits_opt: Option, qualified_payables: Vec, now: SystemTime, - ) -> Vec { + runs_in_debug: bool, + ) -> (Vec, DebugInfoOpt) { let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); let accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); + let debug_info_opt = runs_in_debug.then(|| { + accounts_with_individual_criteria + .iter() + .map(|(criterion, _)| *criterion) + .collect() + }); + let accounts_with_individual_criteria = Self::cut_back_by_known_limits( accounts_with_individual_criteria, known_elimination_limits_opt, @@ -449,11 +491,31 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria, ), //TODO test properly for both!!! }; - adjusted_accounts + (adjusted_accounts, debug_info_opt) } // Gas{g_lim} | Masq | Both {g_lim} // Gas{lim} | Masq {accounts} ...Gas is always applied just once + + fn log_insufficient_gas_balance( + logger: &Logger, + qualified_payables: &[PayableAccount], + this_stage_data: &FinancialAndTechDetails, + limiting_count: u16, + ) { + warning!( + logger, + "Gas amount {} wei cannot cover anticipated fees from sending {} \ + transactions. Maximum is {}. The payments need to be adjusted in \ + their count.", + this_stage_data + .consuming_wallet_balances + .masq_tokens_wei + .separate_with_commas(), + qualified_payables.len(), + limiting_count + ); + } } // replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` @@ -462,6 +524,26 @@ fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } +//saves the computed criteria +type DebugInfoOpt = Option>; + +#[derive(Debug)] +struct AccountAdjustmentDetails { + initial_balance: u128, + adjusted_balance_opt: Option, + used_criterion: u128, +} + +impl AccountAdjustmentDetails { + fn new(initial_balance: u128) -> Self { + AccountAdjustmentDetails { + initial_balance, + adjusted_balance_opt: None, + used_criterion: 0, + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, @@ -497,6 +579,7 @@ mod tests { ConsumingWalletBalances, OutcomingPaymentsInstructions, }; use crate::test_utils::make_wallet; + use itertools::Itertools; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; @@ -759,14 +842,14 @@ mod tests { let subject = PaymentAdjusterReal::new(); let accounts_sum: u128 = 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 - let consuming_wallet_masq_balance = U256::from(accounts_sum - 600_000_000_000_000_000); + let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 600_000_000_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(150), - masq_tokens_wei: consuming_wallet_masq_balance, + masq_tokens_wei: consuming_wallet_masq_balance_wei, }, estimated_gas_limit_per_transaction: 165_000, desired_gas_price_gwei: 222222222222222222, @@ -781,57 +864,11 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - let expected_criteria_computation_output = { - let time_criteria = vec![ - secs_elapsed(account_1.last_paid_timestamp, now), - secs_elapsed(account_2.last_paid_timestamp, now), - secs_elapsed(account_3.last_paid_timestamp, now), - ]; - let amount_criteria = vec![ - account_1.balance_wei * log_10(account_1.balance_wei) as u128, - account_2.balance_wei * log_10(account_2.balance_wei) as u128, - account_3.balance_wei * log_10(account_3.balance_wei) as u128, - ]; - let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( - vec![0, 0, 0], - |acc: Vec, current| { - vec![ - acc[0] + current[0], - acc[1] + current[1], - acc[2] + current[2], - ] - }, - ); - let final_criteria_sum = U256::from(final_criteria.iter().sum::()); - let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( - consuming_wallet_masq_balance, - final_criteria_sum, - ); - let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance - * U256::from(multiplication_coeff) - / final_criteria_sum) - .as_u128(); - let balanced_portions = vec![ - in_ratio_fragment_of_available_balance * final_criteria[0] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[1] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[2] / multiplication_coeff, - ]; - let new_total_amount_to_pay = balanced_portions.iter().sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance.as_u128()); - assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance.as_u128() * 100) / 102, - "new total amount to pay: {}, consuming wallet masq balance: {}", - new_total_amount_to_pay, - consuming_wallet_masq_balance - ); - let mut account_1_adjusted = account_1; - account_1_adjusted.balance_wei = balanced_portions[0]; - let mut account_2_adjusted = account_2; - account_2_adjusted.balance_wei = balanced_portions[1]; - let mut account_3_adjusted = account_3; - account_3_adjusted.balance_wei = balanced_portions[2]; - vec![account_2_adjusted, account_1_adjusted, account_3_adjusted] //the output is sorted in the descending order by significance - }; + let expected_criteria_computation_output = emulation_of_the_actual_adjustment_algorithm( + (account_1, account_2, account_3), + consuming_wallet_masq_balance_wei, + now, + ); assert_eq!( result, OutcomingPaymentsInstructions { @@ -846,16 +883,82 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000616263 444444444444444444 -| 333000000000000051 |0x0000000000000000000000000000000000646566 666666666666000000000000 -| 665999999999334000000004 +| 665599999999334400000004 +|0x0000000000000000000000000000000000616263 444444444444444444 +| 332800000000000051 |0x000000000000000000000000000000000067686b 22000000000000 -| 12820500003284" +| 12812800003282 +|" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } + fn emulation_of_the_actual_adjustment_algorithm( + (account_1, account_2, account_3): (PayableAccount, PayableAccount, PayableAccount), + consuming_wallet_masq_balance_wei: U256, + now: SystemTime, + ) -> Vec { + let time_criteria = vec![ + secs_elapsed(account_1.last_paid_timestamp, now), + secs_elapsed(account_2.last_paid_timestamp, now), + secs_elapsed(account_3.last_paid_timestamp, now), + ]; + let amount_criteria = vec![ + account_1.balance_wei * log_10(account_1.balance_wei) as u128, + account_2.balance_wei * log_10(account_2.balance_wei) as u128, + account_3.balance_wei * log_10(account_3.balance_wei) as u128, + ]; + let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( + vec![0, 0, 0], + |acc: Vec, current| { + vec![ + acc[0] + current[0], + acc[1] + current[1], + acc[2] + current[2], + ] + }, + ); + let final_criteria_sum = U256::from(final_criteria.iter().sum::()); + let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); + let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance_wei + * U256::from(multiplication_coeff) + / final_criteria_sum) + .as_u128(); + let balanced_portions = vec![ + in_ratio_fragment_of_available_balance * final_criteria[0] / multiplication_coeff, + in_ratio_fragment_of_available_balance * final_criteria[1] / multiplication_coeff, + in_ratio_fragment_of_available_balance * final_criteria[2] / multiplication_coeff, + ]; + let new_total_amount_to_pay = balanced_portions.iter().sum::(); + assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei.as_u128()); + assert!( + new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei.as_u128() * 100) / 102, + "new total amount to pay: {}, consuming wallet masq balance: {}", + new_total_amount_to_pay, + consuming_wallet_masq_balance_wei + ); + let mut account_1_adjusted = account_1; + account_1_adjusted.balance_wei = balanced_portions[0]; + let mut account_2_adjusted = account_2; + account_2_adjusted.balance_wei = balanced_portions[1]; + let mut account_3_adjusted = account_3; + account_3_adjusted.balance_wei = balanced_portions[2]; + + vec![ + (final_criteria[0], account_1_adjusted), + (final_criteria[1], account_2_adjusted), + (final_criteria[2], account_3_adjusted), + ] + .into_iter() + .sorted_by(|(criterion_a, _), (criterion_b, _)| Ord::cmp(&criterion_b, &criterion_a)) + .map(|(_, account)| account) + .collect() + } + #[test] fn adjust_payments_when_gas_limits_the_final_transaction_count() { init_test_logging(); @@ -914,7 +1017,36 @@ mod tests { #[test] fn adjust_payments_when_masq_token_limits_the_final_transaction_count() { - todo!() + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 444_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let zero_criteria_accounts = + PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); + + let weights_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + + let only_accounts = weights_and_accounts + .iter() + .map(|(_, account)| account) + .collect::>(); + assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } #[test] From 81c219188793aabd1d94e7888fffd06f78fcb0f0 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 9 Jun 2023 00:42:08 +0200 Subject: [PATCH 027/250] GH-711: interim commit --- node/src/accountant/payment_adjuster.rs | 180 ++++++++++++++++-------- 1 file changed, 124 insertions(+), 56 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index d80d73558..01af529fd 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -135,6 +135,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { logger.debug_enabled(), ); + eprintln!("adjusted_accounts: {:?}", adjusted_accounts); debug!( logger, "{}", @@ -335,7 +336,7 @@ impl PaymentAdjusterReal { .collect() } - fn format_brief_accounts_summary( + fn format_brief_adjustment_summary( mut original_accounts: HashMap, adjusted_accounts: impl Iterator, ) -> String { @@ -347,9 +348,11 @@ impl PaymentAdjusterReal { }; }); }); + let (adjusted, excluded): (Vec<_>, Vec<_>) = original_accounts .into_iter() .partition(|(wallet, details)| details.adjusted_balance_opt.is_some()); + let adjusted_accounts_as_strings = adjusted .into_iter() .sorted_by(|(_, details_a), (_, details_b)| { @@ -365,18 +368,17 @@ impl PaymentAdjusterReal { length = WALLET_ADDRESS_LENGTH ) }) - .chain(once("\n".to_string())) .join("\n"); - eprintln!("excluded {:?}", excluded); - let excluded_accounts_opt = excluded.is_empty().not().then(|| todo!()); - // once("Excluded less important accounts:\n".to_string()) - // .chain( - // excluded - // .into_iter() - // .map(|(wallet, balance)| format!("{} {}", wallet, balance[0])), - // ) - // .join("\n")}; + let excluded_accounts_opt = excluded.is_empty().not().then(|| { + once("\nExcluded less important accounts:\n".to_string()) + .chain( + excluded + .into_iter() + .map(|(wallet, balance)| format!("{} {}", wallet, balance.initial_balance)), + ) + .join("\n") + }); vec![Some(adjusted_accounts_as_strings), excluded_accounts_opt] .into_iter() @@ -434,7 +436,10 @@ impl PaymentAdjusterReal { "Original", "", "Adjusted", - Self::format_brief_accounts_summary(original_prefabricated, prefabricated_adjusted) + Self::format_brief_adjustment_summary( + original_prefabricated, + prefabricated_adjusted + ) ) //TODO mention accounts that will be excluded completely } @@ -865,7 +870,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); let expected_criteria_computation_output = emulation_of_the_actual_adjustment_algorithm( - (account_1, account_2, account_3), + account_1, + account_2, + Some(account_3), consuming_wallet_masq_balance_wei, now, ); @@ -888,35 +895,42 @@ mod tests { |0x0000000000000000000000000000000000616263 444444444444444444 | 332800000000000051 |0x000000000000000000000000000000000067686b 22000000000000 -| 12812800003282 -|" +| 12812800003282" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } fn emulation_of_the_actual_adjustment_algorithm( - (account_1, account_2, account_3): (PayableAccount, PayableAccount, PayableAccount), + account_1: PayableAccount, + account_2: PayableAccount, + account_3_opt: Option, consuming_wallet_masq_balance_wei: U256, now: SystemTime, ) -> Vec { - let time_criteria = vec![ - secs_elapsed(account_1.last_paid_timestamp, now), - secs_elapsed(account_2.last_paid_timestamp, now), - secs_elapsed(account_3.last_paid_timestamp, now), - ]; - let amount_criteria = vec![ - account_1.balance_wei * log_10(account_1.balance_wei) as u128, - account_2.balance_wei * log_10(account_2.balance_wei) as u128, - account_3.balance_wei * log_10(account_3.balance_wei) as u128, - ]; + let accounts = vec![ + Some(account_1.clone()), + Some(account_2.clone()), + account_3_opt.clone(), + ] + .into_iter() + .flatten() + .collect::>(); + let time_criteria = accounts + .iter() + .map(|account| secs_elapsed(account.last_paid_timestamp, now)) + .collect(); + let amount_criteria = accounts + .iter() + .map(|account| account.balance_wei * log_10(account.balance_wei) as u128) + .collect(); + let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( vec![0, 0, 0], - |acc: Vec, current| { - vec![ - acc[0] + current[0], - acc[1] + current[1], - acc[2] + current[2], - ] + |acc: Vec, current: Vec| { + acc.into_iter() + .zip(current.into_iter()) + .map(|(partial_acc, partial_current)| partial_acc + partial_current) + .collect() }, ); let final_criteria_sum = U256::from(final_criteria.iter().sum::()); @@ -928,11 +942,12 @@ mod tests { * U256::from(multiplication_coeff) / final_criteria_sum) .as_u128(); - let balanced_portions = vec![ - in_ratio_fragment_of_available_balance * final_criteria[0] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[1] / multiplication_coeff, - in_ratio_fragment_of_available_balance * final_criteria[2] / multiplication_coeff, - ]; + let balanced_portions = final_criteria + .iter() + .map(|criterion| { + in_ratio_fragment_of_available_balance * criterion / multiplication_coeff + }) + .collect::>(); let new_total_amount_to_pay = balanced_portions.iter().sum::(); assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei.as_u128()); assert!( @@ -945,15 +960,26 @@ mod tests { account_1_adjusted.balance_wei = balanced_portions[0]; let mut account_2_adjusted = account_2; account_2_adjusted.balance_wei = balanced_portions[1]; - let mut account_3_adjusted = account_3; - account_3_adjusted.balance_wei = balanced_portions[2]; + let account_3_adjusted_opt = { + match account_3_opt { + Some(mut account) => Some({ + account.balance_wei = balanced_portions[2]; + account + }), + None => None, + } + }; vec![ - (final_criteria[0], account_1_adjusted), - (final_criteria[1], account_2_adjusted), - (final_criteria[2], account_3_adjusted), + Some((final_criteria[0], account_1_adjusted)), + Some((final_criteria[1], account_2_adjusted)), + match account_3_adjusted_opt { + Some(account) => Some((final_criteria[2], account)), + None => None, + }, ] .into_iter() + .flatten() .sorted_by(|(criterion_a, _), (criterion_b, _)| Ord::cmp(&criterion_b, &criterion_a)) .map(|(_, account)| account) .collect() @@ -1012,41 +1038,83 @@ mod tests { accounts: vec![account_2, account_3], response_skeleton_opt: None } - ) + ); + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Adjusted payables: +|Account wallet Balance wei +| Original +| Adjusted +| +|0x0000000000000000000000000000000000646566 333000000000000 +| 333000000000000 +|0x000000000000000000000000000000000067686b 222000000000000 +| 222000000000000 +| +|Excluded less important accounts: +| +|0x0000000000000000000000000000000000616263 111000000000000" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn adjust_payments_when_masq_token_limits_the_final_transaction_count() { + let test_name = "adjust_payments_when_masq_token_limits_the_final_transaction_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + balance_wei: 111_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(10000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghk"), - balance_wei: 444_000_000_000_000, + balance_wei: 111_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let zero_criteria_accounts = - PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); + let subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance_wei = + U256::from(333_000_000_000_u64 + 111_000_000_000 + 60_000_000); + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(5_000_000_000_000_000_000_000_000_u128), + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_wei: consuming_wallet_masq_balance_wei, + }, + estimated_gas_limit_per_transaction: 77_000, + desired_gas_price_gwei: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::MasqToken, + }; - let weights_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - let only_accounts = weights_and_accounts - .iter() - .map(|(_, account)| account) - .collect::>(); - assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) + let expected_accounts = emulation_of_the_actual_adjustment_algorithm( + account_1, + account_2, + None, + consuming_wallet_masq_balance_wei, + now, + ); + assert_eq!(result.accounts, expected_accounts) + //TODO log eventually } #[test] From 2b934579900d78e338eae2d367e1c60f080a3734 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 12 Jun 2023 16:29:32 +0200 Subject: [PATCH 028/250] GH-711: more tests being set up --- node/src/accountant/payment_adjuster.rs | 421 ++++++++++++++++++------ 1 file changed, 316 insertions(+), 105 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 01af529fd..d667500fa 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -9,13 +9,9 @@ use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; -use futures::collect; use itertools::{Either, Itertools}; -use lazy_static::lazy_static; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::logger::Logger; -use rand::distributions::uniform::SampleBorrow; -use rusqlite::ffi::sqlite3_keyword_name; #[cfg(test)] use std::any::Any; use std::collections::HashMap; @@ -77,8 +73,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => todo!(), }; - let required_masq_sum = - Self::sum_as_u256(qualified_payables, |payable| payable.balance_wei); + let required_masq_sum: U256 = + Self::sum_as(qualified_payables, |payable| payable.balance_wei); let cw_masq_balance = this_stage_data.consuming_wallet_balances.masq_tokens_wei; let required_by_masq_token = if U256::from(required_masq_sum) <= cw_masq_balance { @@ -128,6 +124,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let (adjusted_accounts, debug_info_opt) = Self::run_recursively( + vec![], current_stage_data, initial_known_elimination_limits, qualified_payables, @@ -165,8 +162,9 @@ impl PaymentAdjusterReal { Self {} } - fn sum_as_u256(collection: &[T], arranger: F) -> U256 + fn sum_as(collection: &[T], arranger: F) -> N where + N: From, F: Fn(&T) -> u128, { collection.iter().map(arranger).sum::().into() @@ -217,17 +215,18 @@ impl PaymentAdjusterReal { } } - fn find_multiplication_coeff(cw_masq_balance: U256, criteria_sum: U256) -> u128 { + //TODO write a test that tries really big balances...if we can kill the ceiling + fn find_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { eprintln!( "criteria sum: {}, cw_masq_balance {}", criteria_sum, cw_masq_balance ); - let num_len_criteria = log_10(criteria_sum.as_u128()); //TODO make the log_10 fn generic - let num_len_cw_balance = log_10(cw_masq_balance.as_u128()); + let num_len_criteria = log_10(criteria_sum); //TODO make the log_10 fn generic + let num_len_cw_balance = log_10(cw_masq_balance); let smallest_mul_coeff_between = num_len_criteria .checked_sub(num_len_cw_balance) .unwrap_or(1); - let safe_mul_coeff = smallest_mul_coeff_between + 2; //empiric, affects the precision of the computed results + let safe_mul_coeff = smallest_mul_coeff_between + 6; //empiric, affects the precision of the computed results 1_u128 * 10_u128.pow(safe_mul_coeff as u32) } @@ -266,22 +265,46 @@ impl PaymentAdjusterReal { fn handle_adjustment( cw_masq_balance: U256, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> Vec { - let criteria_sum = - Self::sum_as_u256(&accounts_with_individual_criteria, |(criteria, _)| { + ) -> AdjustmentIterationResult { + let required_balance_total: u128 = + Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { + account.balance_wei + }); + let criteria_sum: u128 = //TODO is u128 safe? + Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { *criteria }); + if let Some(prioritized_wallets) = + Self::check_possible_prioritized_accounts_that_qualify_right_away( + required_balance_total, + criteria_sum, + &accounts_with_individual_criteria, + ) + { + let (prioritized, remaining) = accounts_with_individual_criteria + .into_iter() + .map(|(_, account)| account) + .partition(|account| prioritized_wallets.contains(&account.wallet)); + return AdjustmentIterationResult { + decided_accounts: prioritized, + remaining_accounts: remaining, + }; + }; let multiplication_coeff = - PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_sum); + PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance.as_u128(), criteria_sum); eprintln!("multiplication coefficient: {}", multiplication_coeff); let proportional_fragment_of_cw_balance = - cw_masq_balance.as_u128() * multiplication_coeff / criteria_sum.as_u128(); + cw_masq_balance.as_u128() * multiplication_coeff / criteria_sum; - Self::recreate_accounts_with_proportioned_balances( + let decided_accounts = Self::recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, proportional_fragment_of_cw_balance, multiplication_coeff, - ) + ); + AdjustmentIterationResult { + decided_accounts, + remaining_accounts: vec![], + } } fn apply_criteria( @@ -292,17 +315,36 @@ impl PaymentAdjusterReal { Box (u128, PayableAccount) + 'a>; //define individual criteria as closures to be used in a map() + //TODO always remember to use checked math operations let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let criteria = now + let elapsed_sec: u64 = now .duration_since(account.last_paid_timestamp) .expect("time traveller") - .as_secs() as u128; - (criteria_sum + criteria, account) + .as_secs(); + //TODO how should I treat the sqrt? + let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; + let criterion = (elapsed_sec as u128) + .pow(4) + .checked_div(divisor) + .expect("div overflow"); + ( + criteria_sum.checked_add(criterion).expect("add overflow"), + account, + ) }); let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { let digits_weight = log_10(account.balance_wei); - let additional_criteria = account.balance_wei * digits_weight as u128; - (criteria_sum + additional_criteria, account) + let multiplier = (digits_weight as u128) + .checked_pow(3) + .expect("pow overflow"); + let criterion = account + .balance_wei + .checked_mul(multiplier) + .expect("mul overflow"); + ( + criteria_sum.checked_add(criterion).expect("add overflow"), + account, + ) }); let weights_and_accounts = accounts_with_zero_criteria @@ -328,6 +370,30 @@ impl PaymentAdjusterReal { } } + fn check_possible_prioritized_accounts_that_qualify_right_away( + required_balance_total: u128, + criteria_total: u128, + accounts_with_individual_criteria: &[(u128, PayableAccount)], + ) -> Option> { + let required_balance_for_safe_math = required_balance_total * 10_000; + let criteria_total_for_safe_math = criteria_total * 10_000; + let accounts_to_be_prioritized = accounts_with_individual_criteria + .iter() + .filter(|(criterion, account)| { + //account.balance_wei is still the original balance + let balance_ratio = required_balance_for_safe_math / (account.balance_wei * 10_000); + let criterion_ratio = criteria_total_for_safe_math / (criterion * 10_000); + balance_ratio > criterion_ratio + }) + .map(|(_, account)| account.wallet.clone()) + .collect::>(); + if !accounts_to_be_prioritized.is_empty() { + Some(accounts_to_be_prioritized) + } else { + None + } + } + fn sort_in_descendant_order_by_weights( unsorted: impl Iterator, ) -> Vec<(u128, PayableAccount)> { @@ -337,7 +403,7 @@ impl PaymentAdjusterReal { } fn format_brief_adjustment_summary( - mut original_accounts: HashMap, + mut original_accounts: HashMap, adjusted_accounts: impl Iterator, ) -> String { adjusted_accounts.for_each(|(criterion, wallet, adjusted_balance)| { @@ -413,8 +479,8 @@ impl PaymentAdjusterReal { Self::wallets_and_balances_with_criteria_opt(Either::Left(original)) .left() .expectv("wallet + balance") - .map(|(wallet, balance)| (wallet, AccountAdjustmentDetails::new(balance))) - .collect::>(); + .map(|(wallet, balance)| (wallet, AccountAdjustmentDebugDetails::new(balance))) + .collect::>(); move |criteria: Vec, adjusted_accounts: &[PayableAccount]| { let concise_input = criteria .into_iter() @@ -425,9 +491,9 @@ impl PaymentAdjusterReal { Self::wallets_and_balances_with_criteria_opt(Either::Right(&concise_input)).right().expectv("criterion + wallet + balance"); format!( "\nAdjusted payables:\n\ - {:<42} {}\n\ - {: <42} {}\n\ - {: <42} {}\n\ + {:, + collected_setup_data: FinancialAndTechDetails, known_elimination_limits_opt: Option, qualified_payables: Vec, now: SystemTime, @@ -479,24 +546,44 @@ impl PaymentAdjusterReal { known_elimination_limits_opt, ); //TODO check for masq token sufficiency and if still not enough run again - let adjusted_accounts: Vec = match known_elimination_limits_opt { + let adjustment_result = match known_elimination_limits_opt { Some(KnownEliminationLimits::GasCountLimit { is_masq_token_insufficient, .. }) => match is_masq_token_insufficient { - false => accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .collect(), + false => { + return ( + accounts_with_individual_criteria + .into_iter() + .map(|(_, account)| account) + .collect(), + debug_info_opt, + ) + } true => todo!(), }, Some(KnownEliminationLimits::MasqTokenExcludedAccounts) => todo!(), None => Self::handle_adjustment( - current_stage_data.consuming_wallet_balances.masq_tokens_wei, + collected_setup_data + .consuming_wallet_balances + .masq_tokens_wei, accounts_with_individual_criteria, ), //TODO test properly for both!!! }; - (adjusted_accounts, debug_info_opt) + let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { + adjustment_result.decided_accounts + } else { + return Self::run_recursively( + adjustment_result.decided_accounts, + collected_setup_data, + None, + adjustment_result.remaining_accounts, + now, + runs_in_debug, + ); + }; + + (fully_qualified_accounts.into_iter().chain(adjusted_accounts.into_iter()).collect(), debug_info_opt) } // Gas{g_lim} | Masq | Both {g_lim} @@ -532,16 +619,21 @@ fn log_10(num: u128) -> usize { //saves the computed criteria type DebugInfoOpt = Option>; +struct AdjustmentIterationResult { + decided_accounts: Vec, + remaining_accounts: Vec, +} + #[derive(Debug)] -struct AccountAdjustmentDetails { +struct AccountAdjustmentDebugDetails { initial_balance: u128, adjusted_balance_opt: Option, used_criterion: u128, } -impl AccountAdjustmentDetails { +impl AccountAdjustmentDebugDetails { fn new(initial_balance: u128) -> Self { - AccountAdjustmentDetails { + AccountAdjustmentDebugDetails { initial_balance, adjusted_balance_opt: None, used_criterion: 0, @@ -585,10 +677,12 @@ mod tests { }; use crate::test_utils::make_wallet; use itertools::Itertools; + use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::vec; + use thousands::Separable; use web3::types::U256; fn type_definite_conversion(gwei: u64) -> u128 { @@ -801,12 +895,12 @@ mod tests { #[test] fn multiplication_coeff_for_integers_to_be_above_one_instead_of_fractional_numbers() { - let final_criteria_sum = U256::from(5_000_000_000_000_u64); + let final_criteria_sum = 5_000_000_000_000_u128; let consuming_wallet_balances = vec![ - U256::from(222_222_222_222_u64), - U256::from(100_000), - U256::from(123_456_789), - U256::from(5_555_000_000_000_u64), //the only one bigger than the criteria sum + 222_222_222_222_u128, + 100_000, + 123_456_789, + 5_555_000_000_000, //the only one bigger than the criteria sum ]; let result = consuming_wallet_balances @@ -817,7 +911,130 @@ mod tests { }) .collect::>(); - assert_eq!(result, vec![1_000, 1_000_000_000, 1_000_000, 100]) + assert_eq!(result, vec![10_000_000, 10_000_000_000_000, 10_000_000_000, 1_000_000]) + } + + #[test] + fn testing_criteria_on_overflow_safeness() { + let now = SystemTime::now(); + let extremely_big_balance_wei = MASQ_TOTAL_SUPPLY as u128 * 10u128.pow(18); + let very_long_period_of_time_sec = 5_u64 * 365 * 24 * 60 * 60; //five years + let account = PayableAccount { + wallet: make_wallet("blah"), + balance_wei: extremely_big_balance_wei, + last_paid_timestamp: now + .checked_sub(Duration::from_secs(very_long_period_of_time_sec)) + .unwrap(), + pending_payable_opt: None, + }; + let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(vec![account]); + let mut criteria_and_accounts = + PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + assert_eq!(criteria_and_accounts.len(), 1); + //operands in apply_criteria have to be their checked version therefore we passed through without a panic and so no overflow occurred + } + + #[test] + fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 444_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let zero_criteria_accounts = + PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); + + let weights_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + + let only_accounts = weights_and_accounts + .iter() + .map(|(_, account)| account) + .collect::>(); + assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) + } + + #[test] + fn small_debt_with_extreme_age_is_paid_prioritized_but_not_more_money_than_required() { + let now = SystemTime::now(); + let collected_setup_data = FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(u128::MAX), + masq_tokens_wei: U256::from(1_500_000_000_000_u64 - 25_000_000), + }, + desired_gas_price_gwei: 50, + estimated_gas_limit_per_transaction: 55_000, + }; + let balance_1 = 1_500_000_000_000; + let balance_2 = 25_000_000; + let wallet_1 = make_wallet("blah"); + let last_paid_timestamp_1 = now.checked_sub(Duration::from_secs(5_500)).unwrap(); + let account_1 = PayableAccount { + wallet: wallet_1, + balance_wei: balance_1, + last_paid_timestamp: last_paid_timestamp_1, + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("argh"), + balance_wei: balance_2, + last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone()]; + + let (result, _) = PaymentAdjusterReal::run_recursively( + vec![], + collected_setup_data, + None, + qualified_payables.clone(), + now, + false, + ); + + //first a presentation of why this test is important + let total = balance_1 * 10_000 + balance_2 * 10_000; + let ratio_2_u128 = total / (balance_2 * 10_000); + let ratio_2 = ratio_2_u128 as f64 / 10_000.0; + let zero_criteria_accounts = + PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); + let criteria = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + let account_1_criterion = criteria[0].0 * 10_000; + let account_2_criterion = criteria[1].0 * 10_000; + let criteria_total = account_1_criterion + account_2_criterion; + let criterion_2_ratio = ((criteria_total / account_2_criterion) as f64) / 10_000.0; + //the next assertion reads as the weight of the second account grew faster and bigger than at the first account; + //also, the time parameter has a strong impact on the final criteria; + //consequences are that redistributing the new balances according to the computed weights would've attributed + //the second account with more tokens to pay than it had when the test started; + //to prevent it, we've got a rule that any account can never demand more than 100% of the initial amount + assert!(ratio_2 > criterion_2_ratio); + assert_eq!( + result, + vec![ + account_2, //prioritized accounts take the first places + PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 1_499_972_866_199, + last_paid_timestamp: last_paid_timestamp_1, + pending_payable_opt: None, + }, + ] + ); } #[test] @@ -828,44 +1045,44 @@ mod tests { let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 444_444_444_444_444_444, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1234)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_234)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 666_666_666_666_000_000_000_000, + balance_wei: 666_666_666_666_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(100)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghk"), balance_wei: 22_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(78910)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let subject = PaymentAdjusterReal::new(); let accounts_sum: u128 = - 444_444_444_444_444_444 + 666_666_666_666_000_000_000_000 + 22_000_000_000_000; //= 666_667_111_132_444_444_444_444 - let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 600_000_000_000_000_000); + 444_444_444_444_444_444 + 666_666_666_666_000_000 + 22_000_000_000_000; //= 1_000_022_000_000_444_444 + let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 6_000_000_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(150), + gas_currency_wei: U256::from(u32::MAX), masq_tokens_wei: consuming_wallet_masq_balance_wei, }, - estimated_gas_limit_per_transaction: 165_000, - desired_gas_price_gwei: 222222222222222222, + estimated_gas_limit_per_transaction: 70_000, + desired_gas_price_gwei: 120, }, )), response_skeleton_opt: None, }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; //TODO what to do with the required adjustment? + adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual gas balance limitations + }; let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); @@ -890,12 +1107,12 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000646566 666666666666000000000000 -| 665599999999334400000004 +|0x0000000000000000000000000000000000646566 666666666666000000 +| 662903999999338801 |0x0000000000000000000000000000000000616263 444444444444444444 -| 332800000000000051 +| 441936000010982026 |0x000000000000000000000000000000000067686b 22000000000000 -| 12812800003282" +| 15049998464285" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -917,11 +1134,19 @@ mod tests { .collect::>(); let time_criteria = accounts .iter() - .map(|account| secs_elapsed(account.last_paid_timestamp, now)) + .map(|account| { + let elapsed = secs_elapsed(account.last_paid_timestamp, now); + let criterion = elapsed.pow(4) / (((elapsed as f64).sqrt().ceil()) as u128); + eprintln!("time criterion: {}", criterion.separate_with_commas()); + criterion + }) .collect(); let amount_criteria = accounts .iter() - .map(|account| account.balance_wei * log_10(account.balance_wei) as u128) + .map(|account| { + let significance = log_10(account.balance_wei) as u128; + account.balance_wei * significance.pow(3) + } as u128) .collect(); let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( @@ -933,10 +1158,11 @@ mod tests { .collect() }, ); + eprintln!("final criteria {:?}", final_criteria); let final_criteria_sum = U256::from(final_criteria.iter().sum::()); let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( - consuming_wallet_masq_balance_wei, - final_criteria_sum, + consuming_wallet_masq_balance_wei.as_u128(), + final_criteria_sum.as_u128(), ); let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance_wei * U256::from(multiplication_coeff) @@ -1074,16 +1300,18 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(10000)).unwrap(), pending_payable_opt: None, }; + todo!("serious problem, thanks to the time weights we can overweight and get more than what the account required at the beginning...we need to implement a ceiling"); + let wallet_3 = make_wallet("ghk"); + let balance_3 = 1_000_000; let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 111_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + wallet: wallet_3.clone(), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = - U256::from(333_000_000_000_u64 + 111_000_000_000 + 60_000_000); + let consuming_wallet_masq_balance_wei = U256::from(333_000_000_000_u64 + 50_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1106,6 +1334,23 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let expected_accounts_first_iteration = emulation_of_the_actual_adjustment_algorithm( + account_1.clone(), + account_2.clone(), + Some(account_3), + consuming_wallet_masq_balance_wei, + now, + ); + eprintln!("print expected: {:?}", expected_accounts_first_iteration); + eprintln!("print all adjusted: {:?}", result.accounts); + let account_3_adjusted_balance = expected_accounts_first_iteration + .iter() + .find(|account| account.wallet == wallet_3) + .unwrap() + .balance_wei; + eprintln!("account 3 adjusted {}", account_3_adjusted_balance); + assert!( account_3_adjusted_balance < (balance_3 / 2), "balance for account 3 after adjustment from the first iteration is {} but we need it smaller than {}",account_3_adjusted_balance.separate_with_commas(), (balance_3 / 2).separate_with_commas()); + let expected_accounts = emulation_of_the_actual_adjustment_algorithm( account_1, account_2, @@ -1122,40 +1367,6 @@ mod tests { todo!() } - #[test] - fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 444_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let zero_criteria_accounts = - PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - - let weights_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); - - let only_accounts = weights_and_accounts - .iter() - .map(|(_, account)| account) - .collect::>(); - assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) - } - #[test] fn output_with_response_skeleton_opt_some() { todo!("rather include into some other special test??") From 86cce2bbf687392853a3d499ea798f032126d566 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 13 Jun 2023 11:57:29 +0200 Subject: [PATCH 029/250] GH-711: another situation solved, plus also other tests are nicely passing --- node/src/accountant/payment_adjuster.rs | 345 +++++++++++++++++++----- 1 file changed, 285 insertions(+), 60 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index d667500fa..e23634833 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -7,7 +7,7 @@ use crate::accountant::scanners::payable_scan_setup_msgs::{ }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::masq_lib::utils::ExpectValue; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; +use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; use masq_lib::constants::WALLET_ADDRESS_LENGTH; @@ -129,10 +129,9 @@ impl PaymentAdjuster for PaymentAdjusterReal { initial_known_elimination_limits, qualified_payables, now, - logger.debug_enabled(), + logger, ); - eprintln!("adjusted_accounts: {:?}", adjusted_accounts); debug!( logger, "{}", @@ -201,8 +200,6 @@ impl PaymentAdjusterReal { .expectv("small number for gas price"); let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; - eprintln!("available wei: {:?}", available_wei); - eprintln!("wei per tx: {:?}", grpt_in_wei); let possible_payment_count = (available_wei / grpt_in_wei).as_u128(); if possible_payment_count == 0 { todo!() @@ -217,10 +214,6 @@ impl PaymentAdjusterReal { //TODO write a test that tries really big balances...if we can kill the ceiling fn find_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - eprintln!( - "criteria sum: {}, cw_masq_balance {}", - criteria_sum, cw_masq_balance - ); let num_len_criteria = log_10(criteria_sum); //TODO make the log_10 fn generic let num_len_cw_balance = log_10(cw_masq_balance); let smallest_mul_coeff_between = num_len_criteria @@ -248,18 +241,48 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, proportional_fragment_of_cw_balance: u128, multiplication_coeff: u128, - ) -> Vec { - let rebuild_account = |(criteria_sum, mut account): (u128, PayableAccount)| { - let proportional_amount_to_pay = - criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - account.balance_wei = proportional_amount_to_pay; - account - }; + ) -> (Vec, Vec) { + accounts_with_individual_criteria.into_iter().fold( + (vec![], vec![]), + |(mut decided, mut disqualified), (criteria_sum, mut account)| { + let original_balance = account.balance_wei; + let proposed_adjusted_balance = + criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + eprintln!("prod balanced portions: {}", proposed_adjusted_balance); + if proposed_adjusted_balance < original_balance / 2 + //TODO write a small test to add '=' too + { + let disqualified_account = DisqualifiedPayableAccount::new( + account.wallet, + original_balance, + proposed_adjusted_balance, + ); + disqualified.push(disqualified_account); + (decided, disqualified) + } else { + account.balance_wei = proposed_adjusted_balance; + let decided_account = DecidedPayableAccount::new(account, original_balance); + decided.push(decided_account); + (decided, disqualified) + } + }, + ) + } - accounts_with_individual_criteria - .into_iter() - .map(rebuild_account) - .collect() + fn log_info_for_disqualified_accounts( + logger: &Logger, + disqualified_accounts: &[DisqualifiedPayableAccount], + ) { + disqualified_accounts.iter().for_each(|account| { + info!( + logger, + "Recently qualified payable for wallet {} is being ignored as the limited \ + consuming balance implied adjustment of its balance down to {} wei, which is not at least \ + half of the debt", + account.wallet, + account.proposed_adjusted_balance.separate_with_commas() + ) + }); } fn handle_adjustment( @@ -281,29 +304,65 @@ impl PaymentAdjusterReal { &accounts_with_individual_criteria, ) { - let (prioritized, remaining) = accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .partition(|account| prioritized_wallets.contains(&account.wallet)); + let (prioritized, remaining): (Vec, Vec) = + accounts_with_individual_criteria + .into_iter() + .map(|(_, account)| account) + .partition(|account| prioritized_wallets.contains(&account.wallet)); + return AdjustmentIterationResult { decided_accounts: prioritized, remaining_accounts: remaining, + disqualified_accounts: vec![], }; }; let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance.as_u128(), criteria_sum); - eprintln!("multiplication coefficient: {}", multiplication_coeff); - let proportional_fragment_of_cw_balance = - cw_masq_balance.as_u128() * multiplication_coeff / criteria_sum; - - let decided_accounts = Self::recreate_accounts_with_proportioned_balances( + let proportional_fragment_of_cw_balance = cw_masq_balance + .as_u128() + .checked_mul(multiplication_coeff) + .expect("mul overflow") + .checked_div(criteria_sum) + .expect("div overflow"); + eprintln!( + "prod proportional_fragment: {} for balance {}", + proportional_fragment_of_cw_balance, cw_masq_balance + ); + let (decided_accounts, disqualified_accounts): ( + Vec, + Vec, + ) = Self::recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, proportional_fragment_of_cw_balance, multiplication_coeff, ); - AdjustmentIterationResult { - decided_accounts, - remaining_accounts: vec![], + if disqualified_accounts.is_empty() { + let decided_accounts = decided_accounts + .into_iter() + .map(|decided_account| { + PayableAccount::from(( + decided_account, + DecidedPayableAccountResolution::Finalize, + )) + }) + .collect(); + AdjustmentIterationResult { + decided_accounts, + remaining_accounts: vec![], + disqualified_accounts: vec![], + } + } else { + let remaining_accounts = decided_accounts + .into_iter() + .map(|decided_account| { + PayableAccount::from((decided_account, DecidedPayableAccountResolution::Revert)) + }) + .collect(); + AdjustmentIterationResult { + decided_accounts: vec![], + remaining_accounts, + disqualified_accounts, + } } } @@ -394,6 +453,34 @@ impl PaymentAdjusterReal { } } + fn adjust_cw_balance_in_setup_data( + current_data: FinancialAndTechDetails, + processed_prioritized: &[PayableAccount], + disqualified_accounts: &[DisqualifiedPayableAccount], + ) -> FinancialAndTechDetails { + let subtrahend_total: u128 = if !disqualified_accounts.is_empty() { + Self::sum_as(disqualified_accounts, |disq_account| { + disq_account.original_balance + }) + } else { + Self::sum_as(processed_prioritized, |account| account.balance_wei) + }; + let consuming_wallet_balances = ConsumingWalletBalances { + gas_currency_wei: current_data.consuming_wallet_balances.gas_currency_wei, + masq_tokens_wei: U256::from( + current_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128() + - subtrahend_total, + ), + }; + FinancialAndTechDetails { + consuming_wallet_balances, + ..current_data + } + } + fn sort_in_descendant_order_by_weights( unsorted: impl Iterator, ) -> Vec<(u128, PayableAccount)> { @@ -527,14 +614,15 @@ impl PaymentAdjusterReal { known_elimination_limits_opt: Option, qualified_payables: Vec, now: SystemTime, - runs_in_debug: bool, + logger: &Logger, ) -> (Vec, DebugInfoOpt) { + eprintln!("run recursively input:\n fully_qualified_accounts: {:?}, qualified_payables: {:?}, consuming_wallet: {}\n", fully_qualified_accounts, qualified_payables, collected_setup_data.consuming_wallet_balances.masq_tokens_wei); let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); let accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); - let debug_info_opt = runs_in_debug.then(|| { + let debug_info_opt = logger.debug_enabled().then(|| { accounts_with_individual_criteria .iter() .map(|(criterion, _)| *criterion) @@ -546,6 +634,10 @@ impl PaymentAdjusterReal { known_elimination_limits_opt, ); //TODO check for masq token sufficiency and if still not enough run again + eprintln!( + "prod code accounts with criteria: {:?}", + accounts_with_individual_criteria + ); let adjustment_result = match known_elimination_limits_opt { Some(KnownEliminationLimits::GasCountLimit { is_masq_token_insufficient, @@ -570,20 +662,36 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria, ), //TODO test properly for both!!! }; + eprintln!("adjustment result: {:?}", adjustment_result); + + Self::log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); + let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts } else { + let adjusted_setup_data = Self::adjust_cw_balance_in_setup_data( + collected_setup_data, + &adjustment_result.decided_accounts, + &adjustment_result.disqualified_accounts, + ); + //TODO what happens if we choose one that will get us into negative when subtracted return Self::run_recursively( adjustment_result.decided_accounts, - collected_setup_data, + adjusted_setup_data, None, adjustment_result.remaining_accounts, now, - runs_in_debug, + logger, ); }; - (fully_qualified_accounts.into_iter().chain(adjusted_accounts.into_iter()).collect(), debug_info_opt) + ( + fully_qualified_accounts + .into_iter() + .chain(adjusted_accounts.into_iter()) + .collect(), + debug_info_opt, + ) } // Gas{g_lim} | Masq | Both {g_lim} @@ -619,9 +727,63 @@ fn log_10(num: u128) -> usize { //saves the computed criteria type DebugInfoOpt = Option>; +#[derive(Debug)] struct AdjustmentIterationResult { decided_accounts: Vec, remaining_accounts: Vec, + disqualified_accounts: Vec, +} + +#[derive(Debug)] +struct DecidedPayableAccount { + adjusted_account: PayableAccount, + former_balance: u128, +} + +enum DecidedPayableAccountResolution { + Finalize, + Revert, +} + +impl DecidedPayableAccount { + fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { + Self { + adjusted_account, + former_balance, + } + } +} + +impl From<(DecidedPayableAccount, DecidedPayableAccountResolution)> for PayableAccount { + fn from( + (decided_account, resolution): (DecidedPayableAccount, DecidedPayableAccountResolution), + ) -> Self { + match resolution { + DecidedPayableAccountResolution::Finalize => decided_account.adjusted_account, + DecidedPayableAccountResolution::Revert => { + let mut reverted_account = decided_account.adjusted_account; + reverted_account.balance_wei = decided_account.former_balance; + reverted_account + } + } + } +} + +#[derive(Debug)] +struct DisqualifiedPayableAccount { + wallet: Wallet, + proposed_adjusted_balance: u128, + original_balance: u128, +} + +impl DisqualifiedPayableAccount { + fn new(wallet: Wallet, original_balance: u128, proposed_adjusted_balance: u128) -> Self { + Self { + wallet, + proposed_adjusted_balance, + original_balance, + } + } } #[derive(Debug)] @@ -665,7 +827,7 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::gwei_to_wei; use crate::accountant::payment_adjuster::{ - log_10, Adjustment, PaymentAdjuster, PaymentAdjusterReal, + log_10, Adjustment, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -675,6 +837,7 @@ mod tests { use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; + use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Itertools; use masq_lib::constants::MASQ_TOTAL_SUPPLY; @@ -911,7 +1074,10 @@ mod tests { }) .collect::>(); - assert_eq!(result, vec![10_000_000, 10_000_000_000_000, 10_000_000_000, 1_000_000]) + assert_eq!( + result, + vec![10_000_000, 10_000_000_000_000, 10_000_000_000, 1_000_000] + ) } #[test] @@ -968,6 +1134,39 @@ mod tests { assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } + #[test] + fn log_info_for_disqualified_accounts_can_log_multiple_accounts() { + init_test_logging(); + let wallet_1 = make_wallet("abc"); + let wallet_2 = make_wallet("efg"); + let balance_1 = 456_789_012_345; + let balance_2 = 222_444_777; + let disqualified_accounts = vec![ + DisqualifiedPayableAccount { + wallet: wallet_1.clone(), + original_balance: 500_000_000_000, + proposed_adjusted_balance: balance_1, + }, + DisqualifiedPayableAccount { + wallet: wallet_2.clone(), + original_balance: 300_000_000, + proposed_adjusted_balance: balance_2, + }, + ]; + let logger = Logger::new("log_info_for_disqualified_accounts_can_log_multiple_accounts"); + + PaymentAdjusterReal::log_info_for_disqualified_accounts(&logger, &disqualified_accounts); + + let make_expected_msg = |wallet: &Wallet, balance: u128| -> String { + format!("Recently qualified payable for wallet {wallet} is being ignored as the limited consuming \ + balance implied adjustment of its balance down to {} wei, which is not at least half of the debt", balance.separate_with_commas()) + }; + TestLogHandler::new().assert_logs_contain_in_order(vec![ + &make_expected_msg(&wallet_1, balance_1), + &make_expected_msg(&wallet_2, balance_2), + ]); + } + #[test] fn small_debt_with_extreme_age_is_paid_prioritized_but_not_more_money_than_required() { let now = SystemTime::now(); @@ -995,6 +1194,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), pending_payable_opt: None, }; + let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; let (result, _) = PaymentAdjusterReal::run_recursively( @@ -1003,7 +1203,7 @@ mod tests { None, qualified_payables.clone(), now, - false, + &logger, ); //first a presentation of why this test is important @@ -1026,10 +1226,10 @@ mod tests { assert_eq!( result, vec![ - account_2, //prioritized accounts take the first places + account_2, //prioritized accounts take the first places PayableAccount { wallet: make_wallet("blah"), - balance_wei: 1_499_972_866_199, + balance_wei: 1_499_949_712_293, last_paid_timestamp: last_paid_timestamp_1, pending_payable_opt: None, }, @@ -1108,11 +1308,11 @@ mod tests { | Adjusted | |0x0000000000000000000000000000000000646566 666666666666000000 -| 662903999999338801 +| 663067295999338638 |0x0000000000000000000000000000000000616263 444444444444444444 -| 441936000010982026 +| 442044864010984732 |0x000000000000000000000000000000000067686b 22000000000000 -| 15049998464285" +| 15053705795285" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1158,22 +1358,35 @@ mod tests { .collect() }, ); + eprintln!("final criteria {:?}", final_criteria); - let final_criteria_sum = U256::from(final_criteria.iter().sum::()); + let final_criteria_sum = final_criteria.iter().sum::(); let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( consuming_wallet_masq_balance_wei.as_u128(), - final_criteria_sum.as_u128(), + final_criteria_sum, + ); + eprintln!( + "emul: consuming balance for fragment computation: {}", + consuming_wallet_masq_balance_wei + ); + let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei + .as_u128() + .checked_mul(multiplication_coeff) + .unwrap() + .checked_div(final_criteria_sum) + .unwrap(); + + eprintln!( + "emulated in ration fragment: {}", + in_ratio_fragment_of_available_balance ); - let in_ratio_fragment_of_available_balance = (consuming_wallet_masq_balance_wei - * U256::from(multiplication_coeff) - / final_criteria_sum) - .as_u128(); let balanced_portions = final_criteria .iter() .map(|criterion| { in_ratio_fragment_of_available_balance * criterion / multiplication_coeff }) .collect::>(); + eprintln!("balanced portions: {:?}", balanced_portions); let new_total_amount_to_pay = balanced_portions.iter().sum::(); assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei.as_u128()); assert!( @@ -1211,6 +1424,8 @@ mod tests { .collect() } + //3887999999996122000000, 2592000000064410715061, 88269785714285714 + //2592000000064410715061, 3887999999996122000000, 88269785714285714] #[test] fn adjust_payments_when_gas_limits_the_final_transaction_count() { init_test_logging(); @@ -1286,6 +1501,7 @@ mod tests { #[test] fn adjust_payments_when_masq_token_limits_the_final_transaction_count() { + init_test_logging(); let test_name = "adjust_payments_when_masq_token_limits_the_final_transaction_count"; let now = SystemTime::now(); let account_1 = PayableAccount { @@ -1297,16 +1513,15 @@ mod tests { let account_2 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 111_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(10000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(5000)).unwrap(), pending_payable_opt: None, }; - todo!("serious problem, thanks to the time weights we can overweight and get more than what the account required at the beginning...we need to implement a ceiling"); let wallet_3 = make_wallet("ghk"); - let balance_3 = 1_000_000; + let balance_3 = 50_000_000; let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; @@ -1349,17 +1564,27 @@ mod tests { .unwrap() .balance_wei; eprintln!("account 3 adjusted {}", account_3_adjusted_balance); - assert!( account_3_adjusted_balance < (balance_3 / 2), "balance for account 3 after adjustment from the first iteration is {} but we need it smaller than {}",account_3_adjusted_balance.separate_with_commas(), (balance_3 / 2).separate_with_commas()); - + assert!( + account_3_adjusted_balance < (balance_3 / 2), + "balance for account 3 after \ + adjustment from the first iteration is {} but we need it smaller than {}", + account_3_adjusted_balance.separate_with_commas(), + (balance_3 / 2).separate_with_commas() + ); + let adjusted_cw_balance_after_prioritizing_one_account = + U256::from(consuming_wallet_masq_balance_wei.as_u128() - balance_3); let expected_accounts = emulation_of_the_actual_adjustment_algorithm( account_1, account_2, None, - consuming_wallet_masq_balance_wei, + adjusted_cw_balance_after_prioritizing_one_account, now, ); - assert_eq!(result.accounts, expected_accounts) - //TODO log eventually + assert_eq!(result.accounts, expected_accounts); + TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ + payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ + consuming balance implied adjustment of its balance down to 22,572,576 wei, which is not at \ + least half of the debt")); } #[test] From 05812c1be9a2d1489c24186508d46a3e5ef98398 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 14 Jun 2023 22:13:33 +0200 Subject: [PATCH 030/250] GH-711: so far planned functionality is working and reliable --- node/src/accountant/payment_adjuster.rs | 311 ++++++++++++++++-------- node/src/sub_lib/blockchain_bridge.rs | 2 +- 2 files changed, 211 insertions(+), 102 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index e23634833..d18a03f84 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -73,24 +73,25 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => todo!(), }; - let required_masq_sum: U256 = - Self::sum_as(qualified_payables, |payable| payable.balance_wei); - let cw_masq_balance = this_stage_data.consuming_wallet_balances.masq_tokens_wei; - - let required_by_masq_token = if U256::from(required_masq_sum) <= cw_masq_balance { - false - } else if U256::from(Self::find_smallest_debt(qualified_payables)) > cw_masq_balance { - todo!() - } else { - Self::log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance); - - true + //TODO use question mark later + let required_by_masq_token = match Self::check_need_of_masq_balances_adjustment( + logger, + Either::Left(qualified_payables), + this_stage_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128(), + ) { + Ok(required) => required, + Err(e) => todo!(), }; match (limit_by_gas_opt, required_by_masq_token) { (None, false) => Ok(None), (None, true) => Ok(Some(Adjustment::MasqToken)), - (Some(limiting_count), false) => Ok(Some(Adjustment::Gas { limiting_count })), + (Some(limited_count_from_gas), false) => Ok(Some(Adjustment::Gas { + limited_count_from_gas, + })), (Some(limiting_count), true) => todo!(), } } @@ -115,11 +116,18 @@ impl PaymentAdjuster for PaymentAdjusterReal { )); let initial_known_elimination_limits = match setup.adjustment { - Adjustment::Gas { limiting_count } => Some(KnownEliminationLimits::GasCountLimit { - firm_count_limit: limiting_count, + Adjustment::Gas { + limited_count_from_gas, + } => Some(KnownEliminationLimits { + limited_count_from_gas, is_masq_token_insufficient: false, }), - Adjustment::Both { limiting_count } => todo!(), + Adjustment::Both { + limited_count_from_gas, + } => Some(KnownEliminationLimits { + limited_count_from_gas, + is_masq_token_insufficient: true, + }), Adjustment::MasqToken => None, }; @@ -177,7 +185,7 @@ impl PaymentAdjusterReal { .into() } - fn find_smallest_debt(qualified_accounts: &[PayableAccount]) -> U256 { + fn find_smallest_debt(qualified_accounts: &[&PayableAccount]) -> u128 { qualified_accounts .iter() .sorted_by(|account_a, account_b| { @@ -285,7 +293,7 @@ impl PaymentAdjusterReal { }); } - fn handle_adjustment( + fn handle_masq_token_adjustment( cw_masq_balance: U256, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { @@ -324,10 +332,7 @@ impl PaymentAdjusterReal { .expect("mul overflow") .checked_div(criteria_sum) .expect("div overflow"); - eprintln!( - "prod proportional_fragment: {} for balance {}", - proportional_fragment_of_cw_balance, cw_masq_balance - ); + let (decided_accounts, disqualified_accounts): ( Vec, Vec, @@ -374,7 +379,8 @@ impl PaymentAdjusterReal { Box (u128, PayableAccount) + 'a>; //define individual criteria as closures to be used in a map() - //TODO always remember to use checked math operations + //caution: always remember to use checked math operations! + let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { let elapsed_sec: u64 = now .duration_since(account.last_paid_timestamp) @@ -413,20 +419,14 @@ impl PaymentAdjusterReal { Self::sort_in_descendant_order_by_weights(weights_and_accounts) } - fn cut_back_by_known_limits( + fn cut_back_by_gas_count_limit( weights_and_accounts: Vec<(u128, PayableAccount)>, - limits: Option, + limit: u16, ) -> Vec<(u128, PayableAccount)> { - match limits { - Some(KnownEliminationLimits::GasCountLimit { - firm_count_limit, .. - }) => weights_and_accounts - .into_iter() - .take(firm_count_limit as usize) - .collect(), - Some(KnownEliminationLimits::MasqTokenExcludedAccounts) => todo!(), - None => weights_and_accounts, - } + weights_and_accounts + .into_iter() + .take(limit as usize) + .collect() } fn check_possible_prioritized_accounts_that_qualify_right_away( @@ -598,7 +598,7 @@ impl PaymentAdjusterReal { } } - fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: U256, cw_masq_balance: U256) { + fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_masq_balance: u128) { warning!( logger, "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ @@ -608,8 +608,15 @@ impl PaymentAdjusterReal { ) } + fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { + criteria_and_accounts + .into_iter() + .map(|(_, account)| account) + .collect() + } + fn run_recursively( - mut fully_qualified_accounts: Vec, + fully_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, known_elimination_limits_opt: Option, qualified_payables: Vec, @@ -629,40 +636,49 @@ impl PaymentAdjusterReal { .collect() }); - let accounts_with_individual_criteria = Self::cut_back_by_known_limits( - accounts_with_individual_criteria, - known_elimination_limits_opt, - ); //TODO check for masq token sufficiency and if still not enough run again - - eprintln!( - "prod code accounts with criteria: {:?}", - accounts_with_individual_criteria - ); - let adjustment_result = match known_elimination_limits_opt { - Some(KnownEliminationLimits::GasCountLimit { - is_masq_token_insufficient, - .. - }) => match is_masq_token_insufficient { - false => { - return ( - accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .collect(), - debug_info_opt, - ) + let adjustment_result: AdjustmentIterationResult = match known_elimination_limits_opt { + Some(known_limits) => { + let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( + accounts_with_individual_criteria, + known_limits.limited_count_from_gas, + ); + match known_limits.is_masq_token_insufficient { + true => { + match Self::check_need_of_masq_balances_adjustment( + logger, + Either::Right(&weighted_accounts_cut_by_gas), + collected_setup_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128(), + ) { + Ok(is_needed) => match is_needed { + true => Self::handle_masq_token_adjustment( + collected_setup_data + .consuming_wallet_balances + .masq_tokens_wei, + weighted_accounts_cut_by_gas, + ), + false => todo!(), + }, + Err(e) => todo!(), + } + } + false => { + return ( + Self::rebuild_accounts(weighted_accounts_cut_by_gas), + debug_info_opt, + ) + } } - true => todo!(), - }, - Some(KnownEliminationLimits::MasqTokenExcludedAccounts) => todo!(), - None => Self::handle_adjustment( + } + None => Self::handle_masq_token_adjustment( collected_setup_data .consuming_wallet_balances .masq_tokens_wei, accounts_with_individual_criteria, - ), //TODO test properly for both!!! + ), }; - eprintln!("adjustment result: {:?}", adjustment_result); Self::log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); @@ -716,6 +732,37 @@ impl PaymentAdjusterReal { limiting_count ); } + + fn check_need_of_masq_balances_adjustment( + logger: &Logger, + qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, + consuming_wallet_balance_wei: u128, + ) -> Result { + let qualified_payables: Vec<&PayableAccount> = match qualified_payables { + Either::Left(accounts) => accounts.iter().collect(), + Either::Right(criteria_and_accounts) => criteria_and_accounts + .iter() + .map(|(_, account)| account) + .collect(), + }; + let required_masq_sum: u128 = + Self::sum_as(&qualified_payables, |account: &&PayableAccount| { + account.balance_wei + }); + + if required_masq_sum <= consuming_wallet_balance_wei { + Ok(false) + } else if Self::find_smallest_debt(&qualified_payables) > consuming_wallet_balance_wei { + todo!() + } else { + Self::log_adjustment_by_masq_required( + logger, + required_masq_sum, + consuming_wallet_balance_wei, + ); + Ok(true) + } + } } // replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` @@ -806,17 +853,14 @@ impl AccountAdjustmentDebugDetails { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - Gas { limiting_count: u16 }, - Both { limiting_count: u16 }, + Gas { limited_count_from_gas: u16 }, + Both { limited_count_from_gas: u16 }, } #[derive(Clone, Copy)] -enum KnownEliminationLimits { - GasCountLimit { - firm_count_limit: u16, - is_masq_token_insufficient: bool, - }, - MasqTokenExcludedAccounts, +struct KnownEliminationLimits { + limited_count_from_gas: u16, + is_masq_token_insufficient: bool, } #[derive(Debug, PartialEq, Eq)] @@ -825,7 +869,6 @@ pub enum AnalysisError {} #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::gwei_to_wei; use crate::accountant::payment_adjuster::{ log_10, Adjustment, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, }; @@ -834,6 +877,7 @@ mod tests { }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::test_utils::make_payable_account; + use crate::accountant::{gwei_to_wei, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; @@ -1008,7 +1052,7 @@ mod tests { assert_eq!( result, Ok(Some(Adjustment::Gas { - limiting_count: expected_limiting_count + limited_count_from_gas: expected_limiting_count })) ); TestLogHandler::new().exists_log_containing(&format!( @@ -1027,20 +1071,22 @@ mod tests { let mut payable_2 = make_payable_account(222); payable_2.balance_wei = 3_000_000; let qualified_payables = vec![payable_1, payable_2, payable_3]; + let referenced_qualified_payables = qualified_payables.iter().collect::>(); - let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); + let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - assert_eq!(min, U256::from(111_110)) + assert_eq!(min, 111_110) } #[test] fn find_smallest_debt_handles_just_one_account() { let payable = make_payable_account(111); let qualified_payables = vec![payable]; + let referenced_qualified_payables = qualified_payables.iter().collect::>(); - let min = PaymentAdjusterReal::find_smallest_debt(&qualified_payables); + let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - assert_eq!(min, U256::from(111_000_000_000_u128)) + assert_eq!(min, 111_000_000_000) } #[test] @@ -1094,7 +1140,7 @@ mod tests { pending_payable_opt: None, }; let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(vec![account]); - let mut criteria_and_accounts = + let criteria_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); assert_eq!(criteria_and_accounts.len(), 1); //operands in apply_criteria have to be their checked version therefore we passed through without a panic and so no overflow occurred @@ -1290,7 +1336,7 @@ mod tests { account_1, account_2, Some(account_3), - consuming_wallet_masq_balance_wei, + consuming_wallet_masq_balance_wei.as_u128(), now, ); assert_eq!( @@ -1321,7 +1367,7 @@ mod tests { account_1: PayableAccount, account_2: PayableAccount, account_3_opt: Option, - consuming_wallet_masq_balance_wei: U256, + consuming_wallet_masq_balance_wei: u128, now: SystemTime, ) -> Vec { let accounts = vec![ @@ -1362,7 +1408,7 @@ mod tests { eprintln!("final criteria {:?}", final_criteria); let final_criteria_sum = final_criteria.iter().sum::(); let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( - consuming_wallet_masq_balance_wei.as_u128(), + consuming_wallet_masq_balance_wei, final_criteria_sum, ); eprintln!( @@ -1370,7 +1416,6 @@ mod tests { consuming_wallet_masq_balance_wei ); let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei - .as_u128() .checked_mul(multiplication_coeff) .unwrap() .checked_div(final_criteria_sum) @@ -1388,9 +1433,9 @@ mod tests { .collect::>(); eprintln!("balanced portions: {:?}", balanced_portions); let new_total_amount_to_pay = balanced_portions.iter().sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei.as_u128()); + assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei.as_u128() * 100) / 102, + new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, "new total amount to pay: {}, consuming wallet masq balance: {}", new_total_amount_to_pay, consuming_wallet_masq_balance_wei @@ -1424,8 +1469,6 @@ mod tests { .collect() } - //3887999999996122000000, 2592000000064410715061, 88269785714285714 - //2592000000064410715061, 3887999999996122000000, 88269785714285714] #[test] fn adjust_payments_when_gas_limits_the_final_transaction_count() { init_test_logging(); @@ -1468,7 +1511,9 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::Gas { limiting_count: 2 }, + adjustment: Adjustment::Gas { + limited_count_from_gas: 2, + }, }; let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); @@ -1540,7 +1585,10 @@ mod tests { desired_gas_price_gwei: 24, }, )), - response_skeleton_opt: None, + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 111, + context_id: 234, + }), }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, @@ -1553,17 +1601,14 @@ mod tests { account_1.clone(), account_2.clone(), Some(account_3), - consuming_wallet_masq_balance_wei, + consuming_wallet_masq_balance_wei.as_u128(), now, ); - eprintln!("print expected: {:?}", expected_accounts_first_iteration); - eprintln!("print all adjusted: {:?}", result.accounts); let account_3_adjusted_balance = expected_accounts_first_iteration .iter() .find(|account| account.wallet == wallet_3) .unwrap() .balance_wei; - eprintln!("account 3 adjusted {}", account_3_adjusted_balance); assert!( account_3_adjusted_balance < (balance_3 / 2), "balance for account 3 after \ @@ -1572,7 +1617,7 @@ mod tests { (balance_3 / 2).separate_with_commas() ); let adjusted_cw_balance_after_prioritizing_one_account = - U256::from(consuming_wallet_masq_balance_wei.as_u128() - balance_3); + consuming_wallet_masq_balance_wei.as_u128() - balance_3; let expected_accounts = emulation_of_the_actual_adjustment_algorithm( account_1, account_2, @@ -1581,6 +1626,13 @@ mod tests { now, ); assert_eq!(result.accounts, expected_accounts); + assert_eq!( + result.response_skeleton_opt, + Some(ResponseSkeleton { + client_id: 111, + context_id: 234 + }) + ); TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ consuming balance implied adjustment of its balance down to 22,572,576 wei, which is not at \ @@ -1588,13 +1640,70 @@ mod tests { } #[test] - fn adjust_payments_when_both_parameters_limit_the_final_transaction_count() { - todo!() - } + fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( + ) { + init_test_logging(); + let test_name = "adjust_payments_when_gas_limits_the_final_transaction_count"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + }, + estimated_gas_limit_per_transaction: 77_000, + desired_gas_price_gwei: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::Both { + limited_count_from_gas: 2, + }, + }; - #[test] - fn output_with_response_skeleton_opt_some() { - todo!("rather include into some other special test??") + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + + let expected_accounts = emulation_of_the_actual_adjustment_algorithm( + account_2, + account_3, + None, + consuming_wallet_masq_balance, + now, + ); + assert_eq!( + result, + OutcomingPaymentsInstructions { + accounts: expected_accounts, + response_skeleton_opt: None + } + ) } fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 413d5e3cc..c7536e82a 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -57,7 +57,7 @@ impl SkeletonOptHolder for OutcomingPaymentsInstructions { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConsumingWalletBalances { pub gas_currency_wei: U256, - pub masq_tokens_wei: U256, + pub masq_tokens_wei: U256, //TODO should be u128 instead } #[cfg(test)] From 8ce15d0174ddcfc8d1067ade214e879fec9086ae Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 14 Jun 2023 23:53:40 +0200 Subject: [PATCH 031/250] GH-711: starting to clean up various TODOs I left behin me --- node/src/accountant/payment_adjuster.rs | 274 ++++++++++++++++++------ 1 file changed, 210 insertions(+), 64 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index d18a03f84..7bf4b6dc6 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -63,6 +63,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { Ok(None) => None, Ok(Some(limiting_count)) => { Self::log_insufficient_gas_balance( + //TODO user needs to supply up the wallet!! logger, qualified_payables, this_stage_data, @@ -86,6 +87,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => todo!(), }; + //TODO add logging similar to the above one... but also instruct the user to supply up their wallet + match (limit_by_gas_opt, required_by_masq_token) { (None, false) => Ok(None), (None, true) => Ok(Some(Adjustment::MasqToken)), @@ -115,16 +118,16 @@ impl PaymentAdjuster for PaymentAdjusterReal { &qualified_payables, )); - let initial_known_elimination_limits = match setup.adjustment { + let gas_limitation_context_opt = match setup.adjustment { Adjustment::Gas { limited_count_from_gas, - } => Some(KnownEliminationLimits { + } => Some(GasLimitationContext { limited_count_from_gas, is_masq_token_insufficient: false, }), Adjustment::Both { limited_count_from_gas, - } => Some(KnownEliminationLimits { + } => Some(GasLimitationContext { limited_count_from_gas, is_masq_token_insufficient: true, }), @@ -134,7 +137,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let (adjusted_accounts, debug_info_opt) = Self::run_recursively( vec![], current_stage_data, - initial_known_elimination_limits, + gas_limitation_context_opt, qualified_payables, now, logger, @@ -222,12 +225,13 @@ impl PaymentAdjusterReal { //TODO write a test that tries really big balances...if we can kill the ceiling fn find_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { + const EMPIRIC_PRECISION_LEVERAGING_COEFFICIENT: usize = 6; let num_len_criteria = log_10(criteria_sum); //TODO make the log_10 fn generic let num_len_cw_balance = log_10(cw_masq_balance); let smallest_mul_coeff_between = num_len_criteria .checked_sub(num_len_cw_balance) .unwrap_or(1); - let safe_mul_coeff = smallest_mul_coeff_between + 6; //empiric, affects the precision of the computed results + let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_LEVERAGING_COEFFICIENT; 1_u128 * 10_u128.pow(safe_mul_coeff as u32) } @@ -256,7 +260,6 @@ impl PaymentAdjusterReal { let original_balance = account.balance_wei; let proposed_adjusted_balance = criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - eprintln!("prod balanced portions: {}", proposed_adjusted_balance); if proposed_adjusted_balance < original_balance / 2 //TODO write a small test to add '=' too { @@ -294,14 +297,14 @@ impl PaymentAdjusterReal { } fn handle_masq_token_adjustment( - cw_masq_balance: U256, + cw_masq_balance: u128, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { let required_balance_total: u128 = Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { account.balance_wei }); - let criteria_sum: u128 = //TODO is u128 safe? + let criteria_sum: u128 = Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { *criteria }); @@ -325,9 +328,8 @@ impl PaymentAdjusterReal { }; }; let multiplication_coeff = - PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance.as_u128(), criteria_sum); + PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_sum); let proportional_fragment_of_cw_balance = cw_masq_balance - .as_u128() .checked_mul(multiplication_coeff) .expect("mul overflow") .checked_div(criteria_sum) @@ -386,7 +388,6 @@ impl PaymentAdjusterReal { .duration_since(account.last_paid_timestamp) .expect("time traveller") .as_secs(); - //TODO how should I treat the sqrt? let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; let criterion = (elapsed_sec as u128) .pow(4) @@ -618,12 +619,11 @@ impl PaymentAdjusterReal { fn run_recursively( fully_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, - known_elimination_limits_opt: Option, + gas_limitation_context_opt: Option, qualified_payables: Vec, now: SystemTime, logger: &Logger, ) -> (Vec, DebugInfoOpt) { - eprintln!("run recursively input:\n fully_qualified_accounts: {:?}, qualified_payables: {:?}, consuming_wallet: {}\n", fully_qualified_accounts, qualified_payables, collected_setup_data.consuming_wallet_balances.masq_tokens_wei); let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); let accounts_with_individual_criteria = @@ -636,49 +636,21 @@ impl PaymentAdjusterReal { .collect() }); - let adjustment_result: AdjustmentIterationResult = match known_elimination_limits_opt { - Some(known_limits) => { - let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( - accounts_with_individual_criteria, - known_limits.limited_count_from_gas, - ); - match known_limits.is_masq_token_insufficient { - true => { - match Self::check_need_of_masq_balances_adjustment( - logger, - Either::Right(&weighted_accounts_cut_by_gas), - collected_setup_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(), - ) { - Ok(is_needed) => match is_needed { - true => Self::handle_masq_token_adjustment( - collected_setup_data - .consuming_wallet_balances - .masq_tokens_wei, - weighted_accounts_cut_by_gas, - ), - false => todo!(), - }, - Err(e) => todo!(), - } - } - false => { - return ( - Self::rebuild_accounts(weighted_accounts_cut_by_gas), - debug_info_opt, - ) - } - } - } - None => Self::handle_masq_token_adjustment( + let adjustment_result: AdjustmentIterationResult = + match Self::give_the_job_to_adjustment_workers( + gas_limitation_context_opt, + accounts_with_individual_criteria, collected_setup_data .consuming_wallet_balances - .masq_tokens_wei, - accounts_with_individual_criteria, - ), - }; + .masq_tokens_wei + .as_u128(), + logger, + ) { + AdjustmentCompletion::Finished(accounts_adjusted) => { + return (accounts_adjusted, debug_info_opt) + } + AdjustmentCompletion::Continue(iteration_result) => iteration_result, + }; Self::log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); @@ -710,8 +682,50 @@ impl PaymentAdjusterReal { ) } - // Gas{g_lim} | Masq | Both {g_lim} - // Gas{lim} | Masq {accounts} ...Gas is always applied just once + fn give_the_job_to_adjustment_workers( + gas_limitation_context_opt: Option, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + cw_masq_balance_wei: u128, + logger: &Logger, + ) -> AdjustmentCompletion { + match gas_limitation_context_opt { + Some(known_limits) => { + let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( + accounts_with_individual_criteria, + known_limits.limited_count_from_gas, + ); + match known_limits.is_masq_token_insufficient { + true => { + match Self::check_need_of_masq_balances_adjustment( + logger, + Either::Right(&weighted_accounts_cut_by_gas), + cw_masq_balance_wei, + ) { + Ok(is_needed) => match is_needed { + true => AdjustmentCompletion::Continue( + Self::handle_masq_token_adjustment( + cw_masq_balance_wei, + weighted_accounts_cut_by_gas, + ), + ), + false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + weighted_accounts_cut_by_gas, + )), + }, + Err(e) => todo!(), + } + } + false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + weighted_accounts_cut_by_gas, + )), + } + } + None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( + cw_masq_balance_wei, + accounts_with_individual_criteria, + )), + } + } fn log_insufficient_gas_balance( logger: &Logger, @@ -792,6 +806,11 @@ enum DecidedPayableAccountResolution { Revert, } +enum AdjustmentCompletion { + Finished(Vec), + Continue(AdjustmentIterationResult), +} + impl DecidedPayableAccount { fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { Self { @@ -858,7 +877,7 @@ pub enum Adjustment { } #[derive(Clone, Copy)] -struct KnownEliminationLimits { +struct GasLimitationContext { limited_count_from_gas: u16, is_masq_token_insufficient: bool, } @@ -887,7 +906,7 @@ mod tests { use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use std::time::{Duration, SystemTime}; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::vec; use thousands::Separable; use web3::types::U256; @@ -1214,7 +1233,7 @@ mod tests { } #[test] - fn small_debt_with_extreme_age_is_paid_prioritized_but_not_more_money_than_required() { + fn small_debt_with_extreme_age_is_paid_prioritized_but_not_with_more_money_than_required() { let now = SystemTime::now(); let collected_setup_data = FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { @@ -1470,9 +1489,9 @@ mod tests { } #[test] - fn adjust_payments_when_gas_limits_the_final_transaction_count() { + fn adjust_payments_when_only_gas_limits_the_final_transaction_count() { init_test_logging(); - let test_name = "adjust_payments_when_gas_limits_the_final_transaction_count"; + let test_name = "adjust_payments_when_only_gas_limits_the_final_transaction_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1545,9 +1564,9 @@ mod tests { } #[test] - fn adjust_payments_when_masq_token_limits_the_final_transaction_count() { + fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count() { init_test_logging(); - let test_name = "adjust_payments_when_masq_token_limits_the_final_transaction_count"; + let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("def"), @@ -1703,7 +1722,134 @@ mod tests { accounts: expected_accounts, response_skeleton_opt: None } - ) + ); + } + + #[test] + fn adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut( + ) { + init_test_logging(); + let test_name = "adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance = 333_000_000_000_000_u128 + 222_000_000_000_000 + 1; + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + }, + estimated_gas_limit_per_transaction: 77_000, + desired_gas_price_gwei: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::Both { + limited_count_from_gas: 2, + }, + }; + + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + + assert_eq!( + result, + OutcomingPaymentsInstructions { + accounts: vec![account_2, account_3], + response_skeleton_opt: None + } + ); + } + + #[test] + fn adjust_payments_when_both_parameters_and_masq_as_well_as_gas_will_limit_the_count() { + init_test_logging(); + let test_name = + "adjust_payments_when_both_parameters_and_masq_as_well_as_gas_will_limit_the_count"; + let now = SystemTime::now(); + //thrown away by gas + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 44_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + //thrown away because not enough significant + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 55_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghk"); + let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(4444)).unwrap(); + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance = 300_000_000_000_000_u128; + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + }, + estimated_gas_limit_per_transaction: 77_000, + desired_gas_price_gwei: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::Both { + limited_count_from_gas: 2, + }, + }; + + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + + assert_eq!(result.accounts.len(), 1); + assert_eq!(result.response_skeleton_opt, None); + let only_account = &result.accounts[0]; + assert_eq!(&only_account.wallet, &wallet_3); + assert!( + ((300_000_000_000_000 * 1000) / 1001) <= only_account.balance_wei + && only_account.balance_wei <= 300_000_000_000_000 + ); + assert_eq!(only_account.last_paid_timestamp, last_paid_timestamp_3); + assert_eq!(only_account.pending_payable_opt, None) } fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { From ebb9657fa1dc783d7b6eb9b8a12587acc2cb794e Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 22 Jun 2023 00:07:20 +0200 Subject: [PATCH 032/250] GH-711: improved adjustment logging --- node/src/accountant/payment_adjuster.rs | 125 ++++++++++++++++-------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 7bf4b6dc6..4b92f8b07 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -21,6 +21,10 @@ use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +const REFILL_RECOMMENDATION: &str = "\ +In order to continue using services of other Nodes and avoid delinquency \ +bans you will need to put more funds into your consuming wallet."; + pub trait PaymentAdjuster { fn is_adjustment_required( &self, @@ -59,18 +63,10 @@ impl PaymentAdjuster for PaymentAdjusterReal { let limit_by_gas_opt = match Self::determine_transactions_count_limit_by_gas( &this_stage_data, qualified_payables.len(), + logger, ) { Ok(None) => None, - Ok(Some(limiting_count)) => { - Self::log_insufficient_gas_balance( - //TODO user needs to supply up the wallet!! - logger, - qualified_payables, - this_stage_data, - limiting_count, - ); - Some(limiting_count) - } + Ok(Some(limiting_count)) => Some(limiting_count), Err(e) => todo!(), }; @@ -87,8 +83,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => todo!(), }; - //TODO add logging similar to the above one... but also instruct the user to supply up their wallet - match (limit_by_gas_opt, required_by_masq_token) { (None, false) => Ok(None), (None, true) => Ok(Some(Adjustment::MasqToken)), @@ -202,7 +196,8 @@ impl PaymentAdjusterReal { fn determine_transactions_count_limit_by_gas( tech_info: &FinancialAndTechDetails, - required_max_count: usize, + required_transactions_count: usize, + logger: &Logger, ) -> Result, AnalysisError> { let gas_required_per_transaction_gwei = u128::try_from(tech_info.estimated_gas_limit_per_transaction) @@ -211,15 +206,21 @@ impl PaymentAdjusterReal { .expectv("small number for gas price"); let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; - let possible_payment_count = (available_wei / grpt_in_wei).as_u128(); - if possible_payment_count == 0 { + let limiting_max_possible_count = (available_wei / grpt_in_wei).as_u128(); + if limiting_max_possible_count == 0 { todo!() - } else if possible_payment_count >= required_max_count as u128 { + } else if limiting_max_possible_count >= required_transactions_count as u128 { Ok(None) } else { - let type_limited_possible_count = - u16::try_from(possible_payment_count).expectv("small number for possible tx count"); - Ok(Some(type_limited_possible_count)) + let limiting_count = u16::try_from(limiting_max_possible_count) + .expectv("small number for possible tx count"); + Self::log_insufficient_transaction_fee_balance( + logger, + required_transactions_count, + tech_info, + limiting_count, + ); + Ok(Some(limiting_count)) } } @@ -505,7 +506,7 @@ impl PaymentAdjusterReal { let (adjusted, excluded): (Vec<_>, Vec<_>) = original_accounts .into_iter() - .partition(|(wallet, details)| details.adjusted_balance_opt.is_some()); + .partition(|(_, details)| details.adjusted_balance_opt.is_some()); let adjusted_accounts_as_strings = adjusted .into_iter() @@ -525,13 +526,17 @@ impl PaymentAdjusterReal { .join("\n"); let excluded_accounts_opt = excluded.is_empty().not().then(|| { - once("\nExcluded less important accounts:\n".to_string()) - .chain( - excluded - .into_iter() - .map(|(wallet, balance)| format!("{} {}", wallet, balance.initial_balance)), - ) - .join("\n") + once(format!( + "\n{:) -> Vec { @@ -727,9 +734,9 @@ impl PaymentAdjusterReal { } } - fn log_insufficient_gas_balance( + fn log_insufficient_transaction_fee_balance( logger: &Logger, - qualified_payables: &[PayableAccount], + required_transactions_count: usize, this_stage_data: &FinancialAndTechDetails, limiting_count: u16, ) { @@ -742,9 +749,10 @@ impl PaymentAdjusterReal { .consuming_wallet_balances .masq_tokens_wei .separate_with_commas(), - qualified_payables.len(), + required_transactions_count, limiting_count ); + info!(logger, "{}", REFILL_RECOMMENDATION) } fn check_need_of_masq_balances_adjustment( @@ -890,6 +898,7 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ log_10, Adjustment, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, + REFILL_RECOMMENDATION, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -906,7 +915,7 @@ mod tests { use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use std::time::{Duration, SystemTime}; use std::vec; use thousands::Separable; use web3::types::U256; @@ -915,6 +924,16 @@ mod tests { gwei_to_wei(gwei) } + #[test] + fn constants_are_correct() { + assert_eq!( + REFILL_RECOMMENDATION, + "\ +In order to continue using services of other Nodes and avoid delinquency \ +bans you will need to put more funds into your consuming wallet." + ) + } + #[test] fn sum_payable_balances_works() { let qualified_payables = vec![ @@ -1043,9 +1062,12 @@ mod tests { let result = subject.is_adjustment_required(&msg, &logger); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Total of 101,000,000,000 \ + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 101,000,000,000 \ wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ Adjustment in their count or the amounts is required.")); + log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue using services \ + of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); } #[test] @@ -1074,11 +1096,14 @@ mod tests { limited_count_from_gas: expected_limiting_count })) ); - TestLogHandler::new().exists_log_containing(&format!( + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!( "WARN: {test_name}: Gas amount 18,446,744,073,709,551,615,000,000,000 wei \ cannot cover anticipated fees from sending 3 transactions. Maximum is 2. \ The payments need to be adjusted in their count." )); + log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue using services \ + of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); } #[test] @@ -1367,9 +1392,9 @@ mod tests { ); let log_msg = format!( "DEBUG: {test_name}: \n\ -|Adjusted payables: |Account wallet Balance wei -| Original +| +|Adjusted payables Original | Adjusted | |0x0000000000000000000000000000000000646566 666666666666000000 @@ -1546,9 +1571,9 @@ mod tests { ); let log_msg = format!( "DEBUG: {test_name}: \n\ -|Adjusted payables: |Account wallet Balance wei -| Original +| +|Adjusted payables Original | Adjusted | |0x0000000000000000000000000000000000646566 333000000000000 @@ -1556,7 +1581,7 @@ mod tests { |0x000000000000000000000000000000000067686b 222000000000000 | 222000000000000 | -|Excluded less important accounts: +|Ignored minor payables Original | |0x0000000000000000000000000000000000616263 111000000000000" ); @@ -1849,7 +1874,23 @@ mod tests { && only_account.balance_wei <= 300_000_000_000_000 ); assert_eq!(only_account.last_paid_timestamp, last_paid_timestamp_3); - assert_eq!(only_account.pending_payable_opt, None) + assert_eq!(only_account.pending_payable_opt, None); + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Account wallet Balance wei +| +|Adjusted payables Original +| Adjusted +| +|0x000000000000000000000000000000000067686b 333000000000000 +| 299944910012241 +| +|Ignored minor payables Original +| +|0x0000000000000000000000000000000000616263 44000000000 +|0x0000000000000000000000000000000000646566 55000000000" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { From 7e2f17069d16cde8cd2f833ae09b37e8e17c9f38 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 22 Jun 2023 10:56:27 +0200 Subject: [PATCH 033/250] GH-711: interim commit --- node/src/accountant/mod.rs | 20 +++-- node/src/accountant/payment_adjuster.rs | 104 +++++++++++++++++------- node/src/accountant/scanners/mod.rs | 5 +- node/src/accountant/test_utils.rs | 21 +++-- 4 files changed, 104 insertions(+), 46 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 68be11e9f..e0ba6c567 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1378,7 +1378,7 @@ mod tests { // all that is mocked in this test init_test_logging(); let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; - let is_adjustment_required_params_arc = Arc::new(Mutex::new(vec![])); + let search_for_indispensable_adjustment_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge .system_stop_conditions(match_every_type_id!(OutcomingPaymentsInstructions)) @@ -1386,8 +1386,10 @@ mod tests { .recipient(); let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() - .is_adjustment_required_params(&is_adjustment_required_params_arc) - .is_adjustment_required_result(Ok(None)); + .search_for_indispensable_adjustment_params( + &search_for_indispensable_adjustment_params_arc, + ) + .search_for_indispensable_adjustment_result(Ok(None)); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -1421,9 +1423,13 @@ mod tests { .unwrap(); system.run(); - let mut is_adjustment_required_params = is_adjustment_required_params_arc.lock().unwrap(); - let (payable_payment_setup_msg, logger_clone) = is_adjustment_required_params.remove(0); - assert!(is_adjustment_required_params.is_empty()); + let mut search_for_indispensable_adjustment_params = + search_for_indispensable_adjustment_params_arc + .lock() + .unwrap(); + let (payable_payment_setup_msg, logger_clone) = + search_for_indispensable_adjustment_params.remove(0); + assert!(search_for_indispensable_adjustment_params.is_empty()); assert_eq!( payable_payment_setup_msg, consuming_balances_and_qualified_payments @@ -1486,7 +1492,7 @@ mod tests { response_skeleton_opt: Some(response_skeleton), }; let payment_adjuster = PaymentAdjusterMock::default() - .is_adjustment_required_result(Ok(Some(Adjustment::MasqToken))) + .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(adjusted_payments_instructions); let payable_scanner = PayableScannerBuilder::new() diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 4b92f8b07..75deca498 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -26,7 +26,7 @@ In order to continue using services of other Nodes and avoid delinquency \ bans you will need to put more funds into your consuming wallet."; pub trait PaymentAdjuster { - fn is_adjustment_required( + fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, logger: &Logger, @@ -45,7 +45,7 @@ pub trait PaymentAdjuster { pub struct PaymentAdjusterReal {} impl PaymentAdjuster for PaymentAdjusterReal { - fn is_adjustment_required( + fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, logger: &Logger, @@ -261,9 +261,12 @@ impl PaymentAdjusterReal { let original_balance = account.balance_wei; let proposed_adjusted_balance = criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - if proposed_adjusted_balance < original_balance / 2 - //TODO write a small test to add '=' too - { + if ((original_balance * 10) / 2) <= (proposed_adjusted_balance * 10) { + account.balance_wei = proposed_adjusted_balance; + let decided_account = DecidedPayableAccount::new(account, original_balance); + decided.push(decided_account); + (decided, disqualified) + } else { let disqualified_account = DisqualifiedPayableAccount::new( account.wallet, original_balance, @@ -271,11 +274,6 @@ impl PaymentAdjusterReal { ); disqualified.push(disqualified_account); (decided, disqualified) - } else { - account.balance_wei = proposed_adjusted_balance; - let decided_account = DecidedPayableAccount::new(account, original_balance); - decided.push(decided_account); - (decided, disqualified) } }, ) @@ -534,7 +532,15 @@ impl PaymentAdjusterReal { .chain( excluded .into_iter() - .map(|(wallet, balance)| format!("{} {}", wallet, balance.initial_balance)), + .sorted_by(|(_, debug_details_a), (_, debug_details_b)| { + Ord::cmp( + &debug_details_b.used_criterion, + &debug_details_a.used_criterion, + ) + }) + .map(|(wallet, debug_details)| { + format!("{} {}", wallet, debug_details.initial_balance) + }), ) .join("\n") }); @@ -580,8 +586,9 @@ impl PaymentAdjusterReal { .zip(adjusted_accounts.iter()) .collect::>(); let prefabricated_adjusted = - //TODO extend the collection of adjusted up to the initial length using Option - Self::wallets_and_balances_with_criteria_opt(Either::Right(&concise_input)).right().expectv("criterion + wallet + balance"); + Self::wallets_and_balances_with_criteria_opt(Either::Right(&concise_input)) + .right() + .expectv("criterion + wallet + balance"); format!( "\n\ {:, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] struct DecidedPayableAccount { adjusted_account: PayableAccount, former_balance: u128, @@ -843,7 +850,7 @@ impl From<(DecidedPayableAccount, DecidedPayableAccountResolution)> for PayableA } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] struct DisqualifiedPayableAccount { wallet: Wallet, proposed_adjusted_balance: u128, @@ -897,8 +904,8 @@ pub enum AnalysisError {} mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ - log_10, Adjustment, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, - REFILL_RECOMMENDATION, + log_10, Adjustment, DecidedPayableAccount, DisqualifiedPayableAccount, PaymentAdjuster, + PaymentAdjusterReal, REFILL_RECOMMENDATION, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -1006,9 +1013,9 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn is_adjustment_required_negative_answer() { + fn search_for_indispensable_adjustment_negative_answer() { init_test_logging(); - let test_name = "is_adjustment_required_negative_answer"; + let test_name = "search_for_indispensable_adjustment_negative_answer"; let subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); //masq balance > payments @@ -1040,7 +1047,7 @@ bans you will need to put more funds into your consuming wallet." [msg_1, msg_2, msg_3, msg_4].into_iter().for_each(|msg| { assert_eq!( - subject.is_adjustment_required(&msg, &logger), + subject.search_for_indispensable_adjustment(&msg, &logger), Ok(None), "failed for msg {:?}", msg @@ -1051,15 +1058,15 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn is_adjustment_required_positive_for_masq_token() { + fn search_for_indispensable_adjustment_positive_for_masq_token() { init_test_logging(); - let test_name = "is_adjustment_required_positive_for_masq_token"; + let test_name = "search_for_indispensable_adjustment_positive_for_masq_token"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); let msg = make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 16], 100)), None); - let result = subject.is_adjustment_required(&msg, &logger); + let result = subject.search_for_indispensable_adjustment(&msg, &logger); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); let log_handler = TestLogHandler::new(); @@ -1071,9 +1078,9 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn is_adjustment_required_positive_for_gas() { + fn search_for_indispensable_adjustment_positive_for_gas() { init_test_logging(); - let test_name = "is_adjustment_required_positive_for_gas"; + let test_name = "search_for_indispensable_adjustment_positive_for_gas"; let logger = Logger::new(test_name); let subject = PaymentAdjusterReal::new(); let number_of_payments = 3; @@ -1087,7 +1094,7 @@ bans you will need to put more funds into your consuming wallet." }), ); - let result = subject.is_adjustment_required(&msg, &logger); + let result = subject.search_for_indispensable_adjustment(&msg, &logger); let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( @@ -1170,6 +1177,46 @@ bans you will need to put more funds into your consuming wallet." ) } + #[test] + fn recreate_accounts_with_proportioned_balances_accepts_exact_adjustment_by_half_but_not_by_more( + ) { + let mut payable_account_1 = make_payable_account(1); + payable_account_1.balance_wei = 1_000_000; + let mut payable_account_2 = make_payable_account(2); + payable_account_2.balance_wei = 1_000_001; + let proportional_fragment_of_cw_balance = 200_000; + let multiplication_coeff = 10; + let expected_adjusted_balance = 500_000; + let criterion = + expected_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 + let weights_and_accounts = vec![ + (criterion, payable_account_1.clone()), + (criterion, payable_account_2.clone()), + ]; + + let (decided_accounts, disqualified_accounts) = + PaymentAdjusterReal::recreate_accounts_with_proportioned_balances( + weights_and_accounts, + proportional_fragment_of_cw_balance, + multiplication_coeff, + ); + + let expected_decided_payable_account = DecidedPayableAccount { + adjusted_account: PayableAccount { + balance_wei: 500_000, + ..payable_account_1 + }, + former_balance: 1_000_000, + }; + let expected_disqualified_account = DisqualifiedPayableAccount { + wallet: payable_account_2.wallet, + proposed_adjusted_balance: 500_000, + original_balance: 1_000_001, + }; + assert_eq!(decided_accounts, vec![expected_decided_payable_account]); + assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) + } + #[test] fn testing_criteria_on_overflow_safeness() { let now = SystemTime::now(); @@ -1811,10 +1858,9 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn adjust_payments_when_both_parameters_and_masq_as_well_as_gas_will_limit_the_count() { + fn adjust_payments_when_masq_as_well_as_gas_will_limit_the_count() { init_test_logging(); - let test_name = - "adjust_payments_when_both_parameters_and_masq_as_well_as_gas_will_limit_the_count"; + let test_name = "adjust_payments_when_masq_as_well_as_gas_will_limit_the_count"; let now = SystemTime::now(); //thrown away by gas let account_1 = PayableAccount { diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 7b0cf15be..e45cabd00 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -260,7 +260,10 @@ impl PayableScannerMiddleProcedures for PayableScanner { msg: PayablePaymentSetup, logger: &Logger, ) -> Result, String> { - match self.payment_adjuster.is_adjustment_required(&msg, logger) { + match self + .payment_adjuster + .search_for_indispensable_adjustment(&msg, logger) + { Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { accounts: msg.qualified_payables, response_skeleton_opt: msg.response_skeleton_opt, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index de8ee3405..1e66604c5 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1391,23 +1391,26 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - is_adjustment_required_params: Arc>>, - is_adjustment_required_results: RefCell, AnalysisError>>>, + search_for_indispensable_adjustment_params: Arc>>, + search_for_indispensable_adjustment_results: + RefCell, AnalysisError>>>, adjust_payments_params: Arc>>, adjust_payments_results: RefCell>, } impl PaymentAdjuster for PaymentAdjusterMock { - fn is_adjustment_required( + fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, logger: &Logger, ) -> Result, AnalysisError> { - self.is_adjustment_required_params + self.search_for_indispensable_adjustment_params .lock() .unwrap() .push((msg.clone(), logger.clone())); - self.is_adjustment_required_results.borrow_mut().remove(0) + self.search_for_indispensable_adjustment_results + .borrow_mut() + .remove(0) } fn adjust_payments( @@ -1425,19 +1428,19 @@ impl PaymentAdjuster for PaymentAdjusterMock { } impl PaymentAdjusterMock { - pub fn is_adjustment_required_params( + pub fn search_for_indispensable_adjustment_params( mut self, params: &Arc>>, ) -> Self { - self.is_adjustment_required_params = params.clone(); + self.search_for_indispensable_adjustment_params = params.clone(); self } - pub fn is_adjustment_required_result( + pub fn search_for_indispensable_adjustment_result( self, result: Result, AnalysisError>, ) -> Self { - self.is_adjustment_required_results + self.search_for_indispensable_adjustment_results .borrow_mut() .push(result); self From 714240a8c644ec2a20fb3523f890c7075b61e3f5 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 23 Jun 2023 10:47:08 +0200 Subject: [PATCH 034/250] GH-711: interim commit --- node/src/accountant/payment_adjuster.rs | 343 +++++++++++------------- 1 file changed, 163 insertions(+), 180 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 75deca498..140a0b0ed 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -59,19 +59,17 @@ impl PaymentAdjuster for PaymentAdjusterReal { StageData::FinancialAndTechDetails(details) => details, }; - //TODO use question mark later - let limit_by_gas_opt = match Self::determine_transactions_count_limit_by_gas( + match Self::determine_transactions_count_limit_by_gas( &this_stage_data, qualified_payables.len(), logger, ) { - Ok(None) => None, - Ok(Some(limiting_count)) => Some(limiting_count), + Ok(None) => (), + Ok(Some(limited_count_from_gas)) => return Ok(Some(Adjustment::GasFirstAndThenMaybeMasq {limited_count_from_gas})), Err(e) => todo!(), }; - //TODO use question mark later - let required_by_masq_token = match Self::check_need_of_masq_balances_adjustment( + match Self::check_need_of_masq_balances_adjustment( logger, Either::Left(qualified_payables), this_stage_data @@ -79,17 +77,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { .masq_tokens_wei .as_u128(), ) { - Ok(required) => required, + Ok(required) => match required { + true => Ok(Some(Adjustment::MasqToken)), + false => Ok(None) + } Err(e) => todo!(), - }; - - match (limit_by_gas_opt, required_by_masq_token) { - (None, false) => Ok(None), - (None, true) => Ok(Some(Adjustment::MasqToken)), - (Some(limited_count_from_gas), false) => Ok(Some(Adjustment::Gas { - limited_count_from_gas, - })), - (Some(limiting_count), true) => todo!(), } } @@ -112,26 +104,19 @@ impl PaymentAdjuster for PaymentAdjusterReal { &qualified_payables, )); - let gas_limitation_context_opt = match setup.adjustment { - Adjustment::Gas { - limited_count_from_gas, - } => Some(GasLimitationContext { + let gas_limitation_opt = match setup.adjustment { + Adjustment::GasFirstAndThenMaybeMasq { limited_count_from_gas, - is_masq_token_insufficient: false, - }), - Adjustment::Both { - limited_count_from_gas, - } => Some(GasLimitationContext { - limited_count_from_gas, - is_masq_token_insufficient: true, - }), + } => Some( + limited_count_from_gas + ), Adjustment::MasqToken => None, }; let (adjusted_accounts, debug_info_opt) = Self::run_recursively( vec![], current_stage_data, - gas_limitation_context_opt, + gas_limitation_opt, qualified_payables, now, logger, @@ -623,6 +608,10 @@ impl PaymentAdjusterReal { info!(logger, "{}", REFILL_RECOMMENDATION) } + fn stuff_previously_fully_qualified_accounts_with_sentinels(criteria_of_newly_qualified: Vec, count_of_previously_fully_qualified: usize)->Vec{ + todo!() + } + fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { criteria_and_accounts .into_iter() @@ -633,7 +622,7 @@ impl PaymentAdjusterReal { fn run_recursively( fully_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, - gas_limitation_context_opt: Option, + gas_limitation_opt: Option, qualified_payables: Vec, now: SystemTime, logger: &Logger, @@ -652,7 +641,7 @@ impl PaymentAdjusterReal { let adjustment_result: AdjustmentIterationResult = match Self::give_the_job_to_adjustment_workers( - gas_limitation_context_opt, + gas_limitation_opt, accounts_with_individual_criteria, collected_setup_data .consuming_wallet_balances @@ -687,6 +676,8 @@ impl PaymentAdjusterReal { ); }; + let debug_info_opt = debug_info_opt.map(|vec_of_criteria| Self::stuff_previously_fully_qualified_accounts_with_sentinels(vec_of_criteria, fully_qualified_accounts.len())); + ( fully_qualified_accounts .into_iter() @@ -697,19 +688,17 @@ impl PaymentAdjusterReal { } fn give_the_job_to_adjustment_workers( - gas_limitation_context_opt: Option, + gas_limitation_opt: Option, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, cw_masq_balance_wei: u128, logger: &Logger, ) -> AdjustmentCompletion { - match gas_limitation_context_opt { - Some(known_limits) => { + match gas_limitation_opt { + Some(gas_limit) => { let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( accounts_with_individual_criteria, - known_limits.limited_count_from_gas, + gas_limit, ); - match known_limits.is_masq_token_insufficient { - true => { match Self::check_need_of_masq_balances_adjustment( logger, Either::Right(&weighted_accounts_cut_by_gas), @@ -729,11 +718,6 @@ impl PaymentAdjusterReal { Err(e) => todo!(), } } - false => AdjustmentCompletion::Finished(Self::rebuild_accounts( - weighted_accounts_cut_by_gas, - )), - } - } None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( cw_masq_balance_wei, accounts_with_individual_criteria, @@ -887,8 +871,7 @@ impl AccountAdjustmentDebugDetails { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - Gas { limited_count_from_gas: u16 }, - Both { limited_count_from_gas: u16 }, + GasFirstAndThenMaybeMasq { limited_count_from_gas: u16 }, } #[derive(Clone, Copy)] @@ -974,7 +957,7 @@ bans you will need to put more funds into your consuming wallet." conditions.desired_gas_price_gwei, conditions.number_of_payments, conditions.estimated_gas_limit_per_transaction, - conditions.consuming_wallet_masq_gwei, + conditions.consuming_wallet_gas_gwei, ), None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), }; @@ -1009,7 +992,7 @@ bans you will need to put more funds into your consuming wallet." desired_gas_price_gwei: u64, number_of_payments: usize, estimated_gas_limit_per_transaction: u64, - consuming_wallet_masq_gwei: u64, + consuming_wallet_gas_gwei: u64, } #[test] @@ -1031,7 +1014,7 @@ bans you will need to put more funds into your consuming wallet." desired_gas_price_gwei: 111, number_of_payments: 5, estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_masq_gwei: (111 * 5 * 53_000) + 1, + consuming_wallet_gas_gwei: (111 * 5 * 53_000) + 1, }), ); //gas balance = payments @@ -1041,7 +1024,7 @@ bans you will need to put more funds into your consuming wallet." desired_gas_price_gwei: 100, number_of_payments: 6, estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_masq_gwei: 100 * 6 * 53_000, + consuming_wallet_gas_gwei: 100 * 6 * 53_000, }), ); @@ -1090,7 +1073,7 @@ bans you will need to put more funds into your consuming wallet." desired_gas_price_gwei: 100, number_of_payments, estimated_gas_limit_per_transaction: 55_000, - consuming_wallet_masq_gwei: 100 * 3 * 55_000 - 1, + consuming_wallet_gas_gwei: 100 * 3 * 55_000 - 1, }), ); @@ -1099,7 +1082,7 @@ bans you will need to put more funds into your consuming wallet." let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( result, - Ok(Some(Adjustment::Gas { + Ok(Some(Adjustment::GasFirstAndThenMaybeMasq { limited_count_from_gas: expected_limiting_count })) ); @@ -1561,9 +1544,9 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn adjust_payments_when_only_gas_limits_the_final_transaction_count() { + fn adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut() { init_test_logging(); - let test_name = "adjust_payments_when_only_gas_limits_the_final_transaction_count"; + let test_name = "adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1602,7 +1585,7 @@ bans you will need to put more funds into your consuming wallet." }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::Gas { + adjustment: Adjustment::GasFirstAndThenMaybeMasq { limited_count_from_gas: 2, }, }; @@ -1730,132 +1713,132 @@ bans you will need to put more funds into your consuming wallet." least half of the debt")); } - #[test] - fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( - ) { - init_test_logging(); - let test_name = "adjust_payments_when_gas_limits_the_final_transaction_count"; - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: U256::from(consuming_wallet_masq_balance), - }, - estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = AwaitedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::Both { - limited_count_from_gas: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - - let expected_accounts = emulation_of_the_actual_adjustment_algorithm( - account_2, - account_3, - None, - consuming_wallet_masq_balance, - now, - ); - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: expected_accounts, - response_skeleton_opt: None - } - ); - } - - #[test] - fn adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut( - ) { - init_test_logging(); - let test_name = "adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut"; - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance = 333_000_000_000_000_u128 + 222_000_000_000_000 + 1; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: U256::from(consuming_wallet_masq_balance), - }, - estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = AwaitedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::Both { - limited_count_from_gas: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: vec![account_2, account_3], - response_skeleton_opt: None - } - ); - } + // #[test] + // fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( + // ) { + // init_test_logging(); + // let test_name = "adjust_payments_when_gas_limits_the_final_transaction_count"; + // let now = SystemTime::now(); + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 111_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghk"), + // balance_wei: 222_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + // let subject = PaymentAdjusterReal::new(); + // let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; + // let setup_msg = PayablePaymentSetup { + // qualified_payables, + // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + // }, + // estimated_gas_limit_per_transaction: 77_000, + // desired_gas_price_gwei: 24, + // }, + // )), + // response_skeleton_opt: None, + // }; + // let adjustment_setup = AwaitedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::Both { + // limited_count_from_gas: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + // + // let expected_accounts = emulation_of_the_actual_adjustment_algorithm( + // account_2, + // account_3, + // None, + // consuming_wallet_masq_balance, + // now, + // ); + // assert_eq!( + // result, + // OutcomingPaymentsInstructions { + // accounts: expected_accounts, + // response_skeleton_opt: None + // } + // ); + // } + // + // #[test] + // fn adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut( + // ) { + // init_test_logging(); + // let test_name = "adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut"; + // let now = SystemTime::now(); + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 111_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghk"), + // balance_wei: 222_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + // let subject = PaymentAdjusterReal::new(); + // let consuming_wallet_masq_balance = 333_000_000_000_000_u128 + 222_000_000_000_000 + 1; + // let setup_msg = PayablePaymentSetup { + // qualified_payables, + // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + // }, + // estimated_gas_limit_per_transaction: 77_000, + // desired_gas_price_gwei: 24, + // }, + // )), + // response_skeleton_opt: None, + // }; + // let adjustment_setup = AwaitedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::Both { + // limited_count_from_gas: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + // + // assert_eq!( + // result, + // OutcomingPaymentsInstructions { + // accounts: vec![account_2, account_3], + // response_skeleton_opt: None + // } + // ); + // } #[test] fn adjust_payments_when_masq_as_well_as_gas_will_limit_the_count() { @@ -1904,7 +1887,7 @@ bans you will need to put more funds into your consuming wallet." }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::Both { + adjustment: Adjustment::GasFirstAndThenMaybeMasq { limited_count_from_gas: 2, }, }; From a82ea16ee483f345b43f666aeb9c2fa8effd1f29 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 11 Jul 2023 11:10:37 +0200 Subject: [PATCH 035/250] GH-711: savepoint --- node/src/accountant/payment_adjuster.rs | 123 +++++++++++++----------- 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 140a0b0ed..7345dc8fc 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -97,7 +97,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let qualified_payables: Vec = msg.qualified_payables; - let debug_log_printer_opt = + let debug_log_writer_opt = logger .debug_enabled() .then_some(Self::before_and_after_debug_msg_formatter( @@ -125,7 +125,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { debug!( logger, "{}", - debug_log_printer_opt.expect("debug message missing")( + debug_log_writer_opt.expectv("debug writer")( debug_info_opt.expectv("debug info"), &adjusted_accounts ) @@ -239,7 +239,7 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, proportional_fragment_of_cw_balance: u128, multiplication_coeff: u128, - ) -> (Vec, Vec) { + ) -> (Vec, Vec) { accounts_with_individual_criteria.into_iter().fold( (vec![], vec![]), |(mut decided, mut disqualified), (criteria_sum, mut account)| { @@ -248,7 +248,7 @@ impl PaymentAdjusterReal { criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; if ((original_balance * 10) / 2) <= (proposed_adjusted_balance * 10) { account.balance_wei = proposed_adjusted_balance; - let decided_account = DecidedPayableAccount::new(account, original_balance); + let decided_account = ReversiblePayableAccount::new(account, original_balance); decided.push(decided_account); (decided, disqualified) } else { @@ -264,22 +264,6 @@ impl PaymentAdjusterReal { ) } - fn log_info_for_disqualified_accounts( - logger: &Logger, - disqualified_accounts: &[DisqualifiedPayableAccount], - ) { - disqualified_accounts.iter().for_each(|account| { - info!( - logger, - "Recently qualified payable for wallet {} is being ignored as the limited \ - consuming balance implied adjustment of its balance down to {} wei, which is not at least \ - half of the debt", - account.wallet, - account.proposed_adjusted_balance.separate_with_commas() - ) - }); - } - fn handle_masq_token_adjustment( cw_masq_balance: u128, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, @@ -288,15 +272,15 @@ impl PaymentAdjusterReal { Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { account.balance_wei }); - let criteria_sum: u128 = + let criteria_total: u128 = Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { *criteria }); if let Some(prioritized_wallets) = - Self::check_possible_prioritized_accounts_that_qualify_right_away( - required_balance_total, - criteria_sum, + Self::check_for_prioritized_accounts_that_qualify_without_prolongation( &accounts_with_individual_criteria, + required_balance_total, + criteria_total, ) { let (prioritized, remaining): (Vec, Vec) = @@ -312,15 +296,15 @@ impl PaymentAdjusterReal { }; }; let multiplication_coeff = - PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_sum); + PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_total); let proportional_fragment_of_cw_balance = cw_masq_balance .checked_mul(multiplication_coeff) .expect("mul overflow") - .checked_div(criteria_sum) + .checked_div(criteria_total) .expect("div overflow"); let (decided_accounts, disqualified_accounts): ( - Vec, + Vec, Vec, ) = Self::recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, @@ -328,6 +312,7 @@ impl PaymentAdjusterReal { multiplication_coeff, ); if disqualified_accounts.is_empty() { + //TODO this part and the one almost identical below can be refactored let decided_accounts = decided_accounts .into_iter() .map(|decided_account| { @@ -343,6 +328,9 @@ impl PaymentAdjusterReal { disqualified_accounts: vec![], } } else { + // reverting decided accounts because after we lose the disqualified ones from + // the compilation it may be that the remaining accounts could be now paid + // in more favorable proportions or even in the full size let remaining_accounts = decided_accounts .into_iter() .map(|decided_account| { @@ -414,19 +402,22 @@ impl PaymentAdjusterReal { .collect() } - fn check_possible_prioritized_accounts_that_qualify_right_away( + fn check_for_prioritized_accounts_that_qualify_without_prolongation( + accounts_with_individual_criteria: &[(u128, PayableAccount)], required_balance_total: u128, criteria_total: u128, - accounts_with_individual_criteria: &[(u128, PayableAccount)], ) -> Option> { - let required_balance_for_safe_math = required_balance_total * 10_000; + let required_balance_total_for_safe_math = required_balance_total * 10_000; let criteria_total_for_safe_math = criteria_total * 10_000; let accounts_to_be_prioritized = accounts_with_individual_criteria .iter() .filter(|(criterion, account)| { //account.balance_wei is still the original balance - let balance_ratio = required_balance_for_safe_math / (account.balance_wei * 10_000); + let balance_ratio = required_balance_total_for_safe_math / (account.balance_wei * 10_000); let criterion_ratio = criteria_total_for_safe_math / (criterion * 10_000); + // true means we would pay more than we were asked to pay at the beginning, + // this happens when the debt size is quite small but its age is large and + // plays the main factor balance_ratio > criterion_ratio }) .map(|(_, account)| account.wallet.clone()) @@ -565,15 +556,18 @@ impl PaymentAdjusterReal { .expectv("wallet + balance") .map(|(wallet, balance)| (wallet, AccountAdjustmentDebugDetails::new(balance))) .collect::>(); + move |criteria: Vec, adjusted_accounts: &[PayableAccount]| { - let concise_input = criteria + let input_pairs = criteria .into_iter() .zip(adjusted_accounts.iter()) .collect::>(); + let prefabricated_adjusted = - Self::wallets_and_balances_with_criteria_opt(Either::Right(&concise_input)) + Self::wallets_and_balances_with_criteria_opt(Either::Right(&input_pairs)) .right() .expectv("criterion + wallet + balance"); + format!( "\n\ {:, count_of_previously_fully_qualified: usize)->Vec{ - todo!() - } - fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { criteria_and_accounts .into_iter() @@ -620,33 +626,34 @@ impl PaymentAdjusterReal { } fn run_recursively( - fully_qualified_accounts: Vec, + already_fully_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, gas_limitation_opt: Option, qualified_payables: Vec, now: SystemTime, logger: &Logger, - ) -> (Vec, DebugInfoOpt) { + ) -> (Vec, DebugInfoOpt) { //TODO create `struct DebugablePayableAccount{payable_account: PayableAccount, criteria_opt: Option}` ??? let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); - let accounts_with_individual_criteria = + let sorted_accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); let debug_info_opt = logger.debug_enabled().then(|| { - accounts_with_individual_criteria + sorted_accounts_with_individual_criteria .iter() .map(|(criterion, _)| *criterion) .collect() }); + let cw_masq_balance_wei = collected_setup_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128(); let adjustment_result: AdjustmentIterationResult = - match Self::give_the_job_to_adjustment_workers( + match Self::give_job_to_adjustment_workers( gas_limitation_opt, - accounts_with_individual_criteria, - collected_setup_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(), + sorted_accounts_with_individual_criteria, + cw_masq_balance_wei, logger, ) { AdjustmentCompletion::Finished(accounts_adjusted) => { @@ -676,18 +683,18 @@ impl PaymentAdjusterReal { ); }; - let debug_info_opt = debug_info_opt.map(|vec_of_criteria| Self::stuff_previously_fully_qualified_accounts_with_sentinels(vec_of_criteria, fully_qualified_accounts.len())); + // let debug_info_opt = debug_info_opt.map(|vec_of_criteria| Self::stuff_previously_fully_qualified_accounts_with_sentinels(vec_of_criteria, fully_qualified_accounts.len())); ( - fully_qualified_accounts + already_fully_qualified_accounts .into_iter() - .chain(adjusted_accounts.into_iter()) + .chain(adjusted_accounts.into_iter())//TODO potentially bad? .collect(), debug_info_opt, ) } - fn give_the_job_to_adjustment_workers( + fn give_job_to_adjustment_workers( gas_limitation_opt: Option, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, cw_masq_balance_wei: u128, @@ -789,13 +796,13 @@ type DebugInfoOpt = Option>; #[derive(Debug)] struct AdjustmentIterationResult { - decided_accounts: Vec, + decided_accounts: Vec, //TODO what about DebugablePayableAccount??? remaining_accounts: Vec, disqualified_accounts: Vec, } #[derive(Debug, PartialEq, Eq)] -struct DecidedPayableAccount { +struct ReversiblePayableAccount { adjusted_account: PayableAccount, former_balance: u128, } @@ -810,7 +817,7 @@ enum AdjustmentCompletion { Continue(AdjustmentIterationResult), } -impl DecidedPayableAccount { +impl ReversiblePayableAccount { fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { Self { adjusted_account, @@ -819,9 +826,9 @@ impl DecidedPayableAccount { } } -impl From<(DecidedPayableAccount, DecidedPayableAccountResolution)> for PayableAccount { +impl From<(ReversiblePayableAccount, DecidedPayableAccountResolution)> for PayableAccount { fn from( - (decided_account, resolution): (DecidedPayableAccount, DecidedPayableAccountResolution), + (decided_account, resolution): (ReversiblePayableAccount, DecidedPayableAccountResolution), ) -> Self { match resolution { DecidedPayableAccountResolution::Finalize => decided_account.adjusted_account, @@ -887,7 +894,7 @@ pub enum AnalysisError {} mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ - log_10, Adjustment, DecidedPayableAccount, DisqualifiedPayableAccount, PaymentAdjuster, + log_10, Adjustment, ReversiblePayableAccount, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, REFILL_RECOMMENDATION, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ @@ -1184,7 +1191,7 @@ bans you will need to put more funds into your consuming wallet." multiplication_coeff, ); - let expected_decided_payable_account = DecidedPayableAccount { + let expected_decided_payable_account = ReversiblePayableAccount { adjusted_account: PayableAccount { balance_wei: 500_000, ..payable_account_1 From aeebc763b64e763587884267a0996ddcddf07165 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 11 Jul 2023 23:07:11 +0200 Subject: [PATCH 036/250] GH-711: debugging process refactored well; plus some small improvements --- node/src/accountant/payment_adjuster.rs | 435 +++++++++++------------- 1 file changed, 193 insertions(+), 242 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 7345dc8fc..241549edf 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -65,7 +65,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { logger, ) { Ok(None) => (), - Ok(Some(limited_count_from_gas)) => return Ok(Some(Adjustment::GasFirstAndThenMaybeMasq {limited_count_from_gas})), + Ok(Some(limited_count_from_gas)) => { + return Ok(Some(Adjustment::GasFirstAndThenMaybeMasq { + limited_count_from_gas, + })) + } Err(e) => todo!(), }; @@ -79,8 +83,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(required) => match required { true => Ok(Some(Adjustment::MasqToken)), - false => Ok(None) - } + false => Ok(None), + }, Err(e) => todo!(), } } @@ -97,23 +101,21 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let qualified_payables: Vec = msg.qualified_payables; - let debug_log_writer_opt = - logger - .debug_enabled() - .then_some(Self::before_and_after_debug_msg_formatter( - &qualified_payables, - )); - let gas_limitation_opt = match setup.adjustment { Adjustment::GasFirstAndThenMaybeMasq { limited_count_from_gas, - } => Some( - limited_count_from_gas - ), + } => Some(limited_count_from_gas), Adjustment::MasqToken => None, }; - let (adjusted_accounts, debug_info_opt) = Self::run_recursively( + let debug_info_opt = logger.debug_enabled().then(|| { + qualified_payables + .iter() + .map(|account| (account.wallet.clone(), account.balance_wei)) + .collect::>() + }); + + let adjusted_accounts = Self::run_recursively( vec![], current_stage_data, gas_limitation_opt, @@ -125,7 +127,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { debug!( logger, "{}", - debug_log_writer_opt.expectv("debug writer")( + Self::before_and_after_debug_msg( debug_info_opt.expectv("debug info"), &adjusted_accounts ) @@ -210,7 +212,7 @@ impl PaymentAdjusterReal { } //TODO write a test that tries really big balances...if we can kill the ceiling - fn find_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { + fn find_decent_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { const EMPIRIC_PRECISION_LEVERAGING_COEFFICIENT: usize = 6; let num_len_criteria = log_10(criteria_sum); //TODO make the log_10 fn generic let num_len_cw_balance = log_10(cw_masq_balance); @@ -237,9 +239,21 @@ impl PaymentAdjusterReal { fn recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - proportional_fragment_of_cw_balance: u128, - multiplication_coeff: u128, - ) -> (Vec, Vec) { + cw_masq_balance: u128, + criteria_total: u128, + ) -> ( + Vec, + Vec, + ) { + let multiplication_coeff = + PaymentAdjusterReal::find_decent_multiplication_coeff(cw_masq_balance, criteria_total); + + let proportional_fragment_of_cw_balance = cw_masq_balance + .checked_mul(multiplication_coeff) + .expect("mul overflow") + .checked_div(criteria_total) + .expect("div overflow"); + accounts_with_individual_criteria.into_iter().fold( (vec![], vec![]), |(mut decided, mut disqualified), (criteria_sum, mut account)| { @@ -268,14 +282,9 @@ impl PaymentAdjusterReal { cw_masq_balance: u128, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { - let required_balance_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { - account.balance_wei - }); - let criteria_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }); + let (required_balance_total, criteria_total) = + Self::compute_totals(&accounts_with_individual_criteria); + if let Some(prioritized_wallets) = Self::check_for_prioritized_accounts_that_qualify_without_prolongation( &accounts_with_individual_criteria, @@ -288,40 +297,28 @@ impl PaymentAdjusterReal { .into_iter() .map(|(_, account)| account) .partition(|account| prioritized_wallets.contains(&account.wallet)); - - return AdjustmentIterationResult { + let result = AdjustmentIterationResult { decided_accounts: prioritized, remaining_accounts: remaining, disqualified_accounts: vec![], }; + return result; }; - let multiplication_coeff = - PaymentAdjusterReal::find_multiplication_coeff(cw_masq_balance, criteria_total); - let proportional_fragment_of_cw_balance = cw_masq_balance - .checked_mul(multiplication_coeff) - .expect("mul overflow") - .checked_div(criteria_total) - .expect("div overflow"); let (decided_accounts, disqualified_accounts): ( Vec, Vec, ) = Self::recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, - proportional_fragment_of_cw_balance, - multiplication_coeff, + cw_masq_balance, + criteria_total, ); if disqualified_accounts.is_empty() { //TODO this part and the one almost identical below can be refactored - let decided_accounts = decided_accounts - .into_iter() - .map(|decided_account| { - PayableAccount::from(( - decided_account, - DecidedPayableAccountResolution::Finalize, - )) - }) - .collect(); + let decided_accounts = Self::finalize_decided_accounts( + decided_accounts, + DecidedPayableAccountResolution::Finalize, + ); AdjustmentIterationResult { decided_accounts, remaining_accounts: vec![], @@ -331,12 +328,10 @@ impl PaymentAdjusterReal { // reverting decided accounts because after we lose the disqualified ones from // the compilation it may be that the remaining accounts could be now paid // in more favorable proportions or even in the full size - let remaining_accounts = decided_accounts - .into_iter() - .map(|decided_account| { - PayableAccount::from((decided_account, DecidedPayableAccountResolution::Revert)) - }) - .collect(); + let remaining_accounts = Self::finalize_decided_accounts( + decided_accounts, + DecidedPayableAccountResolution::Revert, + ); AdjustmentIterationResult { decided_accounts: vec![], remaining_accounts, @@ -345,6 +340,31 @@ impl PaymentAdjusterReal { } } + fn compute_totals( + accounts_with_individual_criteria: &[(u128, PayableAccount)], + ) -> (u128, u128) { + let required_balance_total: u128 = + Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { + account.balance_wei + }); + + let criteria_total: u128 = + Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { + *criteria + }); + (required_balance_total, criteria_total) + } + + fn finalize_decided_accounts( + decided_accounts: Vec, + resolution: DecidedPayableAccountResolution, + ) -> Vec { + decided_accounts + .into_iter() + .map(|decided_account| PayableAccount::from((decided_account, resolution))) + .collect() + } + fn apply_criteria( accounts_with_zero_criteria: impl Iterator, now: SystemTime, @@ -413,7 +433,8 @@ impl PaymentAdjusterReal { .iter() .filter(|(criterion, account)| { //account.balance_wei is still the original balance - let balance_ratio = required_balance_total_for_safe_math / (account.balance_wei * 10_000); + let balance_ratio = + required_balance_total_for_safe_math / (account.balance_wei * 10_000); let criterion_ratio = criteria_total_for_safe_math / (criterion * 10_000); // true means we would pay more than we were asked to pay at the beginning, // this happens when the debt size is quite small but its age is large and @@ -466,40 +487,43 @@ impl PaymentAdjusterReal { } fn format_brief_adjustment_summary( - mut original_accounts: HashMap, - adjusted_accounts: impl Iterator, + original_account_balances_mapped: HashMap, + adjusted_accounts: &[PayableAccount], ) -> String { - adjusted_accounts.for_each(|(criterion, wallet, adjusted_balance)| { - let _ = original_accounts.entry(wallet).and_modify(|details| { - let _ = { - details.adjusted_balance_opt.replace(adjusted_balance); - details.used_criterion = criterion - }; - }); - }); - - let (adjusted, excluded): (Vec<_>, Vec<_>) = original_accounts - .into_iter() - .partition(|(_, details)| details.adjusted_balance_opt.is_some()); + let adjusted_accounts_enumerated: Vec<&Wallet> = adjusted_accounts + .iter() + .map(|account| &account.wallet) + .collect(); + let excluded: Vec<(&Wallet, &u128)> = original_account_balances_mapped.iter().fold( + vec![], + |mut acc, (wallet, original_balance)| { + if !adjusted_accounts_enumerated.contains(&wallet) { + acc.push((wallet, original_balance)); + } + acc + }, + ); - let adjusted_accounts_as_strings = adjusted + let adjusted_accounts_summary = adjusted_accounts .into_iter() - .sorted_by(|(_, details_a), (_, details_b)| { - Ord::cmp(&details_b.used_criterion, &details_a.used_criterion) + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) }) - .map(|(wallet, details)| { + .map(|account| { format!( "{} {}\n{:^length$} {}", - wallet, - details.initial_balance, + account.wallet, + original_account_balances_mapped + .get(&account.wallet) + .expectv("initial balance"), "", - details.adjusted_balance_opt.expectv("adjusted balance"), + account.balance_wei, length = WALLET_ADDRESS_LENGTH ) }) .join("\n"); - let excluded_accounts_opt = excluded.is_empty().not().then(|| { + let excluded_accounts_summary_opt = excluded.is_empty().not().then(|| { once(format!( "\n{:( - accounts: Either<&'a [PayableAccount], &'a [(u128, &'a PayableAccount)]>, - ) -> Either< - impl Iterator + 'a, - impl Iterator + 'a, - > { - match accounts { - Either::Left(just_accounts) => Either::Left( - just_accounts - .iter() - .map(|account| (account.wallet.clone(), account.balance_wei)), + fn before_and_after_debug_msg( + original_account_balances_mapped: HashMap, + adjusted_accounts: &[PayableAccount], + ) -> String { + format!( + "\n\ + {: { - Either::Right(criteria_and_accounts.iter().map(|(criterion, account)| { - (*criterion, account.wallet.clone(), account.balance_wei) - })) - } - } - } - - fn before_and_after_debug_msg_formatter( - original: &[PayableAccount], - ) -> impl FnOnce(Vec, &[PayableAccount]) -> String { - let original_prefabricated = - Self::wallets_and_balances_with_criteria_opt(Either::Left(original)) - .left() - .expectv("wallet + balance") - .map(|(wallet, balance)| (wallet, AccountAdjustmentDebugDetails::new(balance))) - .collect::>(); - - move |criteria: Vec, adjusted_accounts: &[PayableAccount]| { - let input_pairs = criteria - .into_iter() - .zip(adjusted_accounts.iter()) - .collect::>(); - - let prefabricated_adjusted = - Self::wallets_and_balances_with_criteria_opt(Either::Right(&input_pairs)) - .right() - .expectv("criterion + wallet + balance"); - - format!( - "\n\ - {:) -> Vec { criteria_and_accounts .into_iter() @@ -632,19 +637,11 @@ impl PaymentAdjusterReal { qualified_payables: Vec, now: SystemTime, logger: &Logger, - ) -> (Vec, DebugInfoOpt) { //TODO create `struct DebugablePayableAccount{payable_account: PayableAccount, criteria_opt: Option}` ??? + ) -> Vec { let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); - let sorted_accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); - let debug_info_opt = logger.debug_enabled().then(|| { - sorted_accounts_with_individual_criteria - .iter() - .map(|(criterion, _)| *criterion) - .collect() - }); - let cw_masq_balance_wei = collected_setup_data .consuming_wallet_balances .masq_tokens_wei @@ -656,9 +653,7 @@ impl PaymentAdjusterReal { cw_masq_balance_wei, logger, ) { - AdjustmentCompletion::Finished(accounts_adjusted) => { - return (accounts_adjusted, debug_info_opt) - } + AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, AdjustmentCompletion::Continue(iteration_result) => iteration_result, }; @@ -683,15 +678,11 @@ impl PaymentAdjusterReal { ); }; - // let debug_info_opt = debug_info_opt.map(|vec_of_criteria| Self::stuff_previously_fully_qualified_accounts_with_sentinels(vec_of_criteria, fully_qualified_accounts.len())); - - ( - already_fully_qualified_accounts - .into_iter() - .chain(adjusted_accounts.into_iter())//TODO potentially bad? - .collect(), - debug_info_opt, - ) + let adjusted_accounts_iter = adjusted_accounts.into_iter(); + already_fully_qualified_accounts + .into_iter() + .chain(adjusted_accounts_iter) + .collect() } fn give_job_to_adjustment_workers( @@ -702,29 +693,25 @@ impl PaymentAdjusterReal { ) -> AdjustmentCompletion { match gas_limitation_opt { Some(gas_limit) => { - let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( - accounts_with_individual_criteria, - gas_limit, - ); - match Self::check_need_of_masq_balances_adjustment( - logger, - Either::Right(&weighted_accounts_cut_by_gas), + let weighted_accounts_cut_by_gas = + Self::cut_back_by_gas_count_limit(accounts_with_individual_criteria, gas_limit); + match Self::check_need_of_masq_balances_adjustment( + logger, + Either::Right(&weighted_accounts_cut_by_gas), + cw_masq_balance_wei, + ) { + Ok(is_needed) => match is_needed { + true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( cw_masq_balance_wei, - ) { - Ok(is_needed) => match is_needed { - true => AdjustmentCompletion::Continue( - Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - weighted_accounts_cut_by_gas, - ), - ), - false => AdjustmentCompletion::Finished(Self::rebuild_accounts( - weighted_accounts_cut_by_gas, - )), - }, - Err(e) => todo!(), - } - } + weighted_accounts_cut_by_gas, + )), + false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + weighted_accounts_cut_by_gas, + )), + }, + Err(e) => todo!(), + } + } None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( cw_masq_balance_wei, accounts_with_individual_criteria, @@ -732,27 +719,6 @@ impl PaymentAdjusterReal { } } - fn log_insufficient_transaction_fee_balance( - logger: &Logger, - required_transactions_count: usize, - this_stage_data: &FinancialAndTechDetails, - limiting_count: u16, - ) { - warning!( - logger, - "Gas amount {} wei cannot cover anticipated fees from sending {} \ - transactions. Maximum is {}. The payments need to be adjusted in \ - their count.", - this_stage_data - .consuming_wallet_balances - .masq_tokens_wei - .separate_with_commas(), - required_transactions_count, - limiting_count - ); - info!(logger, "{}", REFILL_RECOMMENDATION) - } - fn check_need_of_masq_balances_adjustment( logger: &Logger, qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, @@ -791,12 +757,9 @@ fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } -//saves the computed criteria -type DebugInfoOpt = Option>; - #[derive(Debug)] struct AdjustmentIterationResult { - decided_accounts: Vec, //TODO what about DebugablePayableAccount??? + decided_accounts: Vec, remaining_accounts: Vec, disqualified_accounts: Vec, } @@ -807,6 +770,7 @@ struct ReversiblePayableAccount { former_balance: u128, } +#[derive(Clone, Copy)] enum DecidedPayableAccountResolution { Finalize, Revert, @@ -858,23 +822,6 @@ impl DisqualifiedPayableAccount { } } -#[derive(Debug)] -struct AccountAdjustmentDebugDetails { - initial_balance: u128, - adjusted_balance_opt: Option, - used_criterion: u128, -} - -impl AccountAdjustmentDebugDetails { - fn new(initial_balance: u128) -> Self { - AccountAdjustmentDebugDetails { - initial_balance, - adjusted_balance_opt: None, - used_criterion: 0, - } - } -} - #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, @@ -894,8 +841,8 @@ pub enum AnalysisError {} mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ - log_10, Adjustment, ReversiblePayableAccount, DisqualifiedPayableAccount, PaymentAdjuster, - PaymentAdjusterReal, REFILL_RECOMMENDATION, + log_10, Adjustment, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, + ReversiblePayableAccount, REFILL_RECOMMENDATION, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -1157,7 +1104,10 @@ bans you will need to put more funds into your consuming wallet." .clone() .into_iter() .map(|cw_balance| { - PaymentAdjusterReal::find_multiplication_coeff(cw_balance, final_criteria_sum) + PaymentAdjusterReal::find_decent_multiplication_coeff( + cw_balance, + final_criteria_sum, + ) }) .collect::>(); @@ -1324,7 +1274,7 @@ bans you will need to put more funds into your consuming wallet." let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; - let (result, _) = PaymentAdjusterReal::run_recursively( + let result = PaymentAdjusterReal::run_recursively( vec![], collected_setup_data, None, @@ -1488,7 +1438,7 @@ bans you will need to put more funds into your consuming wallet." eprintln!("final criteria {:?}", final_criteria); let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = PaymentAdjusterReal::find_multiplication_coeff( + let multiplication_coeff = PaymentAdjusterReal::find_decent_multiplication_coeff( consuming_wallet_masq_balance_wei, final_criteria_sum, ); @@ -1551,7 +1501,8 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut() { + fn adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut( + ) { init_test_logging(); let test_name = "adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut"; let now = SystemTime::now(); @@ -1923,8 +1874,8 @@ bans you will need to put more funds into your consuming wallet." | |Ignored minor payables Original | -|0x0000000000000000000000000000000000616263 44000000000 -|0x0000000000000000000000000000000000646566 55000000000" +|0x0000000000000000000000000000000000646566 55000000000 +|0x0000000000000000000000000000000000616263 44000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } From bee105adeca47f9365b436693da7999d2ab31c39 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 12 Jul 2023 22:57:26 +0200 Subject: [PATCH 037/250] GH-711: continuing knocking off todos in PaymentAdjuster and some refactoring --- node/src/accountant/payment_adjuster.rs | 249 ++++++++++++++++-------- 1 file changed, 168 insertions(+), 81 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 241549edf..7fd333d4c 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -1,11 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::gwei_to_wei; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; +use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; @@ -25,6 +25,8 @@ const REFILL_RECOMMENDATION: &str = "\ In order to continue using services of other Nodes and avoid delinquency \ bans you will need to put more funds into your consuming wallet."; +const EMPTY_STR: &str = ""; + pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, @@ -66,11 +68,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(None) => (), Ok(Some(limited_count_from_gas)) => { - return Ok(Some(Adjustment::GasFirstAndThenMaybeMasq { + return Ok(Some(Adjustment::TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas, })) } - Err(e) => todo!(), + Err(e) => return Err(e), }; match Self::check_need_of_masq_balances_adjustment( @@ -102,7 +104,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let qualified_payables: Vec = msg.qualified_payables; let gas_limitation_opt = match setup.adjustment { - Adjustment::GasFirstAndThenMaybeMasq { + Adjustment::TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas, } => Some(limited_count_from_gas), Adjustment::MasqToken => None, @@ -186,16 +188,20 @@ impl PaymentAdjusterReal { required_transactions_count: usize, logger: &Logger, ) -> Result, AnalysisError> { - let gas_required_per_transaction_gwei = + let transaction_fee_required_per_transaction_in_major = u128::try_from(tech_info.estimated_gas_limit_per_transaction) .expectv("small number for gas limit") * u128::try_from(tech_info.desired_gas_price_gwei) .expectv("small number for gas price"); - let grpt_in_wei: U256 = gwei_to_wei(gas_required_per_transaction_gwei); - let available_wei = tech_info.consuming_wallet_balances.gas_currency_wei; - let limiting_max_possible_count = (available_wei / grpt_in_wei).as_u128(); + let tfrpt_in_minor: U256 = gwei_to_wei(transaction_fee_required_per_transaction_in_major); + let available_balance_in_minor = tech_info.consuming_wallet_balances.gas_currency_wei; + let limiting_max_possible_count = (available_balance_in_minor / tfrpt_in_minor).as_u128(); if limiting_max_possible_count == 0 { - todo!() + Err(AnalysisError::TransactionFeeBalanceBelowOneTransaction { + one_transaction_requirement: transaction_fee_required_per_transaction_in_major + as u64, + cw_balance: wei_to_gwei(available_balance_in_minor), + }) } else if limiting_max_possible_count >= required_transactions_count as u128 { Ok(None) } else { @@ -211,16 +217,16 @@ impl PaymentAdjusterReal { } } - //TODO write a test that tries really big balances...if we can kill the ceiling fn find_decent_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - const EMPIRIC_PRECISION_LEVERAGING_COEFFICIENT: usize = 6; - let num_len_criteria = log_10(criteria_sum); //TODO make the log_10 fn generic - let num_len_cw_balance = log_10(cw_masq_balance); - let smallest_mul_coeff_between = num_len_criteria - .checked_sub(num_len_cw_balance) - .unwrap_or(1); - let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_LEVERAGING_COEFFICIENT; - 1_u128 * 10_u128.pow(safe_mul_coeff as u32) + const EMPIRIC_PRECISION_COEFFICIENT: usize = 6; + + let criteria_sum_digits_count = log_10(criteria_sum); + let cw_balance_digits_count = log_10(cw_masq_balance); + let smallest_mul_coeff_between = criteria_sum_digits_count + .checked_sub(cw_balance_digits_count) + .unwrap_or(0); + let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_COEFFICIENT; + 10_u128.pow(safe_mul_coeff as u32) } fn initialize_zero_criteria( @@ -305,6 +311,7 @@ impl PaymentAdjusterReal { return result; }; + //TODO starting here...wrap this up into a separate method let (decided_accounts, disqualified_accounts): ( Vec, Vec, @@ -313,8 +320,8 @@ impl PaymentAdjusterReal { cw_masq_balance, criteria_total, ); + if disqualified_accounts.is_empty() { - //TODO this part and the one almost identical below can be refactored let decided_accounts = Self::finalize_decided_accounts( decided_accounts, DecidedPayableAccountResolution::Finalize, @@ -337,6 +344,7 @@ impl PaymentAdjusterReal { remaining_accounts, disqualified_accounts, } + //TODO ending here } } @@ -490,56 +498,65 @@ impl PaymentAdjusterReal { original_account_balances_mapped: HashMap, adjusted_accounts: &[PayableAccount], ) -> String { - let adjusted_accounts_enumerated: Vec<&Wallet> = adjusted_accounts + fn format_summary_for_included_accounts( + original_account_balances_mapped: &HashMap, + adjusted_accounts: &[PayableAccount], + ) -> String { + adjusted_accounts + .into_iter() + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + }) + .map(|account| { + format!( + "{} {}\n{:^length$} {}", + account.wallet, + original_account_balances_mapped + .get(&account.wallet) + .expectv("initial balance"), + EMPTY_STR, + account.balance_wei, + length = WALLET_ADDRESS_LENGTH + ) + }) + .join("\n") + } + fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { + let title = once(format!( + "\n{: = adjusted_accounts .iter() .map(|account| &account.wallet) .collect(); - let excluded: Vec<(&Wallet, &u128)> = original_account_balances_mapped.iter().fold( + let excluded: Vec<(&Wallet, u128)> = original_account_balances_mapped.iter().fold( vec![], |mut acc, (wallet, original_balance)| { - if !adjusted_accounts_enumerated.contains(&wallet) { - acc.push((wallet, original_balance)); + if !adjusted_accounts_wallets.contains(&wallet) { + acc.push((wallet, *original_balance)); } acc }, ); - - let adjusted_accounts_summary = adjusted_accounts - .into_iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - }) - .map(|account| { - format!( - "{} {}\n{:^length$} {}", - account.wallet, - original_account_balances_mapped - .get(&account.wallet) - .expectv("initial balance"), - "", - account.balance_wei, - length = WALLET_ADDRESS_LENGTH - ) - }) - .join("\n"); - - let excluded_accounts_summary_opt = excluded.is_empty().not().then(|| { - once(format!( - "\n{: u128 { gwei_to_wei(gwei) } @@ -1036,7 +1066,7 @@ bans you will need to put more funds into your consuming wallet." let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( result, - Ok(Some(Adjustment::GasFirstAndThenMaybeMasq { + Ok(Some(Adjustment::TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas: expected_limiting_count })) ); @@ -1050,6 +1080,32 @@ bans you will need to put more funds into your consuming wallet." of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); } + #[test] + fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_gas( + ) { + let subject = PaymentAdjusterReal::new(); + let number_of_payments = 3; + let msg = make_payable_setup_msg_coming_from_blockchain_bridge( + None, + Some(GasTestConditions { + desired_gas_price_gwei: 100, + number_of_payments, + estimated_gas_limit_per_transaction: 55_000, + consuming_wallet_gas_gwei: 54_000 * 100, + }), + ); + + let result = subject.search_for_indispensable_adjustment(&msg, &Logger::new("test")); + + assert_eq!( + result, + Err(AnalysisError::TransactionFeeBalanceBelowOneTransaction { + one_transaction_requirement: 55_000 * 100, + cw_balance: 54_000 * 100 + }) + ); + } + #[test] fn find_smallest_debt_works() { let mut payable_1 = make_payable_account(111); @@ -1097,7 +1153,9 @@ bans you will need to put more funds into your consuming wallet." 222_222_222_222_u128, 100_000, 123_456_789, - 5_555_000_000_000, //the only one bigger than the criteria sum + 5_555_000_000_000, + 50_555_000_000_000, + 500_555_000_000_000, ]; let result = consuming_wallet_balances @@ -1113,10 +1171,35 @@ bans you will need to put more funds into your consuming wallet." assert_eq!( result, - vec![10_000_000, 10_000_000_000_000, 10_000_000_000, 1_000_000] + vec![ + 10_000_000, + 10_000_000_000_000, + 10_000_000_000, + 1_000_000, + 1_000_000, + 1_000_000 + ] ) } + #[test] + fn multiplication_coeff_testing_upper_extreme() { + let final_criteria = get_extreme_criteria(1); + let final_criteria_total = final_criteria[0].0; + let cw_balance_in_minor = 1; + let result = PaymentAdjusterReal::find_decent_multiplication_coeff( + cw_balance_in_minor, + final_criteria_total, + ); + + assert_eq!(result, 100_000_000_000_000_000_000_000_000_000_000_000) + // enough space for our counts; mostly we use it for division and multiplication and + // in both cases the coefficient is picked carefully to handle it (we near the extremes + // either by increasing the criteria sum and decreasing the cw balance or vica versa) + // + // it allows operating without the use of point floating numbers + } + #[test] fn recreate_accounts_with_proportioned_balances_accepts_exact_adjustment_by_half_but_not_by_more( ) { @@ -1157,23 +1240,25 @@ bans you will need to put more funds into your consuming wallet." assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) } - #[test] - fn testing_criteria_on_overflow_safeness() { + fn get_extreme_criteria(number_of_accounts: usize) -> Vec<(u128, PayableAccount)> { let now = SystemTime::now(); - let extremely_big_balance_wei = MASQ_TOTAL_SUPPLY as u128 * 10u128.pow(18); - let very_long_period_of_time_sec = 5_u64 * 365 * 24 * 60 * 60; //five years let account = PayableAccount { wallet: make_wallet("blah"), - balance_wei: extremely_big_balance_wei, + balance_wei: *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, last_paid_timestamp: now - .checked_sub(Duration::from_secs(very_long_period_of_time_sec)) + .checked_sub(Duration::from_secs(*FIVE_YEAR_LONG_DEBT_SEC)) .unwrap(), pending_payable_opt: None, }; - let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(vec![account]); - let criteria_and_accounts = - PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); - assert_eq!(criteria_and_accounts.len(), 1); + let accounts = once(account).cycle().take(number_of_accounts).collect(); + let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); + PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now) + } + + #[test] + fn testing_criteria_on_overflow_safeness() { + let criteria_and_accounts = get_extreme_criteria(3); + assert_eq!(criteria_and_accounts.len(), 3); //operands in apply_criteria have to be their checked version therefore we passed through without a panic and so no overflow occurred } @@ -1543,7 +1628,7 @@ bans you will need to put more funds into your consuming wallet." }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::GasFirstAndThenMaybeMasq { + adjustment: Adjustment::TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas: 2, }, }; @@ -1671,6 +1756,7 @@ bans you will need to put more funds into your consuming wallet." least half of the debt")); } + //TODO do I really want to delete this test? Why? // #[test] // fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( // ) { @@ -1737,7 +1823,8 @@ bans you will need to put more funds into your consuming wallet." // } // ); // } - // + + //TODO do I really want to delete this test? Why? // #[test] // fn adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut( // ) { @@ -1845,7 +1932,7 @@ bans you will need to put more funds into your consuming wallet." }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::GasFirstAndThenMaybeMasq { + adjustment: Adjustment::TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas: 2, }, }; From 199bb8066b9a0dac9ca21e0160f2629f579b9e23 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 13 Jul 2023 11:49:15 +0200 Subject: [PATCH 038/250] GH-711: interim commit --- node/src/accountant/payment_adjuster.rs | 217 ++++++++++++++++-------- 1 file changed, 145 insertions(+), 72 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 7fd333d4c..9c28e8f67 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -83,11 +83,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { .masq_tokens_wei .as_u128(), ) { - Ok(required) => match required { - true => Ok(Some(Adjustment::MasqToken)), - false => Ok(None), - }, - Err(e) => todo!(), + true => Ok(Some(Adjustment::MasqToken)), + false => Ok(None), } } @@ -150,6 +147,8 @@ impl Default for PaymentAdjusterReal { } } +//TODO think about splitting this file into a folder of files + impl PaymentAdjusterReal { pub fn new() -> Self { Self {} @@ -171,17 +170,17 @@ impl PaymentAdjusterReal { .into() } - fn find_smallest_debt(qualified_accounts: &[&PayableAccount]) -> u128 { - qualified_accounts - .iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - }) - .last() - .expect("at least one qualified payable must have been sent here") - .balance_wei - .into() - } + // fn find_smallest_debt(qualified_accounts: &[&PayableAccount]) -> u128 { + // qualified_accounts + // .iter() + // .sorted_by(|account_a, account_b| { + // Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + // }) + // .last() + // .expect("at least one qualified payable must have been sent here") + // .balance_wei + // .into() + // } fn determine_transactions_count_limit_by_gas( tech_info: &FinancialAndTechDetails, @@ -461,15 +460,9 @@ impl PaymentAdjusterReal { fn adjust_cw_balance_in_setup_data( current_data: FinancialAndTechDetails, processed_prioritized: &[PayableAccount], - disqualified_accounts: &[DisqualifiedPayableAccount], ) -> FinancialAndTechDetails { - let subtrahend_total: u128 = if !disqualified_accounts.is_empty() { - Self::sum_as(disqualified_accounts, |disq_account| { - disq_account.original_balance - }) - } else { - Self::sum_as(processed_prioritized, |account| account.balance_wei) - }; + let subtrahend_total: u128 = + Self::sum_as(processed_prioritized, |account| account.balance_wei); let consuming_wallet_balances = ConsumingWalletBalances { gas_currency_wei: current_data.consuming_wallet_balances.gas_currency_wei, masq_tokens_wei: U256::from( @@ -682,7 +675,6 @@ impl PaymentAdjusterReal { let adjusted_setup_data = Self::adjust_cw_balance_in_setup_data( collected_setup_data, &adjustment_result.decided_accounts, - &adjustment_result.disqualified_accounts, ); //TODO what happens if we choose one that will get us into negative when subtracted return Self::run_recursively( @@ -717,16 +709,13 @@ impl PaymentAdjusterReal { Either::Right(&weighted_accounts_cut_by_gas), cw_masq_balance_wei, ) { - Ok(is_needed) => match is_needed { - true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - weighted_accounts_cut_by_gas, - )), - false => AdjustmentCompletion::Finished(Self::rebuild_accounts( - weighted_accounts_cut_by_gas, - )), - }, - Err(e) => todo!(), + true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( + cw_masq_balance_wei, + weighted_accounts_cut_by_gas, + )), + false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + weighted_accounts_cut_by_gas, + )), } } None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( @@ -740,7 +729,7 @@ impl PaymentAdjusterReal { logger: &Logger, qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, consuming_wallet_balance_wei: u128, - ) -> Result { + ) -> bool { let qualified_payables: Vec<&PayableAccount> = match qualified_payables { Either::Left(accounts) => accounts.iter().collect(), Either::Right(criteria_and_accounts) => criteria_and_accounts @@ -754,16 +743,14 @@ impl PaymentAdjusterReal { }); if required_masq_sum <= consuming_wallet_balance_wei { - Ok(false) - } else if Self::find_smallest_debt(&qualified_payables) > consuming_wallet_balance_wei { - todo!() + false } else { Self::log_adjustment_by_masq_required( logger, required_masq_sum, consuming_wallet_balance_wei, ); - Ok(true) + true } } } @@ -1106,32 +1093,32 @@ bans you will need to put more funds into your consuming wallet." ); } - #[test] - fn find_smallest_debt_works() { - let mut payable_1 = make_payable_account(111); - payable_1.balance_wei = 111_111; - let mut payable_3 = make_payable_account(333); - payable_3.balance_wei = 111_110; - let mut payable_2 = make_payable_account(222); - payable_2.balance_wei = 3_000_000; - let qualified_payables = vec![payable_1, payable_2, payable_3]; - let referenced_qualified_payables = qualified_payables.iter().collect::>(); - - let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - - assert_eq!(min, 111_110) - } - - #[test] - fn find_smallest_debt_handles_just_one_account() { - let payable = make_payable_account(111); - let qualified_payables = vec![payable]; - let referenced_qualified_payables = qualified_payables.iter().collect::>(); - - let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - - assert_eq!(min, 111_000_000_000) - } + // #[test] + // fn find_smallest_debt_works() { + // let mut payable_1 = make_payable_account(111); + // payable_1.balance_wei = 111_111; + // let mut payable_3 = make_payable_account(333); + // payable_3.balance_wei = 111_110; + // let mut payable_2 = make_payable_account(222); + // payable_2.balance_wei = 3_000_000; + // let qualified_payables = vec![payable_1, payable_2, payable_3]; + // let referenced_qualified_payables = qualified_payables.iter().collect::>(); + // + // let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); + // + // assert_eq!(min, 111_110) + // } + // + // #[test] + // fn find_smallest_debt_handles_just_one_account() { + // let payable = make_payable_account(111); + // let qualified_payables = vec![payable]; + // let referenced_qualified_payables = qualified_payables.iter().collect::>(); + // + // let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); + // + // assert_eq!(min, 111_000_000_000) + // } #[test] fn log_10_works() { @@ -1728,18 +1715,17 @@ bans you will need to put more funds into your consuming wallet." .balance_wei; assert!( account_3_adjusted_balance < (balance_3 / 2), - "balance for account 3 after \ - adjustment from the first iteration is {} but we need it smaller than {}", + "balance for account 3 after adjustment from the first iteration is {} but we need it \ + smaller than {} to exercise what happens if the proposed balance is smaller than half \ + the original one", account_3_adjusted_balance.separate_with_commas(), (balance_3 / 2).separate_with_commas() ); - let adjusted_cw_balance_after_prioritizing_one_account = - consuming_wallet_masq_balance_wei.as_u128() - balance_3; let expected_accounts = emulation_of_the_actual_adjustment_algorithm( account_1, account_2, None, - adjusted_cw_balance_after_prioritizing_one_account, + consuming_wallet_masq_balance_wei.as_u128(), now, ); assert_eq!(result.accounts, expected_accounts); @@ -1756,6 +1742,93 @@ bans you will need to put more funds into your consuming wallet." least half of the debt")); } + #[test] + fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { + // accounts in this test are evenly significant and so one cannot win over another, + // yet there is not enough balance to pay the minimum required which is a half of each + // thus we conclude none can be paid + + init_test_logging(); + let test_name = "adjust_payments_when_not_enough_masq_to_pay_at_least_halves_of_accounts"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abcd"), + balance_wei: 100_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), + pending_payable_opt: None, + }; + let mut account_2 = PayableAccount { + wallet: make_wallet("cdef"), + ..account_1.clone() + }; + account_2.balance_wei = 90_000_000_000_000; + let qualified_payables = vec![account_1.clone(), account_2.clone()]; + let subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 + 1); + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(u128::MAX), + masq_tokens_wei: consuming_wallet_masq_balance_wei, + }, + estimated_gas_limit_per_transaction: 55_000, + desired_gas_price_gwei: 150, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::MasqToken, + }; + + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + + eprintln!("accounts adjusted: {:?}", result.accounts); + // let expected_accounts_first_iteration = emulation_of_the_actual_adjustment_algorithm( + // account_1.clone(), + // account_2.clone(), + // Some(account_3), + // consuming_wallet_masq_balance_wei.as_u128(), + // now, + // ); + // let account_3_adjusted_balance = expected_accounts_first_iteration + // .iter() + // .find(|account| account.wallet == wallet_3) + // .unwrap() + // .balance_wei; + // assert!( + // account_3_adjusted_balance < (balance_3 / 2), + // "balance for account 3 after \ + // adjustment from the first iteration is {} but we need it smaller than {}", + // account_3_adjusted_balance.separate_with_commas(), + // (balance_3 / 2).separate_with_commas() + // ); + // let adjusted_cw_balance_after_prioritizing_one_account = + // consuming_wallet_masq_balance_wei.as_u128() - balance_3; + // let expected_accounts = emulation_of_the_actual_adjustment_algorithm( + // account_1, + // account_2, + // None, + // adjusted_cw_balance_after_prioritizing_one_account, + // now, + // ); + // assert_eq!(result.accounts, expected_accounts); + // assert_eq!( + // result.response_skeleton_opt, + // Some(ResponseSkeleton { + // client_id: 111, + // context_id: 234 + // }) + // ); + // TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ + // payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ + // consuming balance implied adjustment of its balance down to 22,572,576 wei, which is not at \ + // least half of the debt")); + } + //TODO do I really want to delete this test? Why? // #[test] // fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( @@ -1957,7 +2030,7 @@ bans you will need to put more funds into your consuming wallet." | Adjusted | |0x000000000000000000000000000000000067686b 333000000000000 -| 299944910012241 +| 299999980172486 | |Ignored minor payables Original | From 579996f24aae0cf0ed4b3e34df79918b26003876 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 14 Jul 2023 02:04:10 +0200 Subject: [PATCH 039/250] GH-711: added sofisticated diagnostics tools; tuning tests and the engine to get results corresponding to the behaviour the users might expect --- node/src/accountant/payment_adjuster.rs | 592 ++++++++++++++++-------- 1 file changed, 392 insertions(+), 200 deletions(-) diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster.rs index 9c28e8f67..6682c8ad3 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster.rs @@ -15,9 +15,11 @@ use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; use std::collections::HashMap; +use std::fmt::Display; use std::iter::{once, successors}; use std::ops::Not; -use std::time::SystemTime; +use std::sync::Once; +use std::time::{Duration, SystemTime}; use thousands::Separable; use web3::types::U256; @@ -25,7 +27,7 @@ const REFILL_RECOMMENDATION: &str = "\ In order to continue using services of other Nodes and avoid delinquency \ bans you will need to put more funds into your consuming wallet."; -const EMPTY_STR: &str = ""; +const NO_CHARS: &str = ""; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( @@ -116,9 +118,9 @@ impl PaymentAdjuster for PaymentAdjusterReal { let adjusted_accounts = Self::run_recursively( vec![], + qualified_payables, current_stage_data, gas_limitation_opt, - qualified_payables, now, logger, ); @@ -147,8 +149,121 @@ impl Default for PaymentAdjusterReal { } } +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; +const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = false; +static AGE_SINGLETON: Once = Once::new(); +static BALANCE_SINGLETON: Once = Once::new(); + +fn diagnostics(account: &Wallet, description: &str, value_renderer: F) +where + F: Fn() -> String, +{ + if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { + eprintln!( + "{} {:( + label: &str, + formula: F, + singleton: &Once, + examples_count: usize, +) where + F: Fn(u128) -> u128, +{ + if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { + singleton.call_once(|| { + let different_values_for_chief_parameter: Vec<(u128, u32)> = vec![ + (10, 1), + (10, 3), + (10, 6), + (10, 9), + (10, 12), + (10, 15), + (10, 18), + ]; + + eprintln!("{}", label); + different_values_for_chief_parameter + .into_iter() + .take(examples_count) + .map(|(base, factor)| base.pow(factor)) + .for_each(|num| { + let value = formula(num); + eprintln!("x: {: +where + F: Fn(u128) -> u128, +{ + account: PayableAccount, + criterion: u128, + criteria_sum_so_far: u128, + //below only diagnostics purposes + label: &'static str, + diagnostics_adapted_formula: F, + singleton_ref: &'a Once, + //max 9 + example_count_to_print: usize, +} + +impl FinalizationAndDiagnostics<'_, F> +where + F: Fn(u128) -> u128, +{ + fn perform(self) -> (u128, PayableAccount) { + diagnostics( + &self.account.wallet, + &format!("COMPUTED {} CRITERIA", self.label), + || self.criterion.separate_with_commas(), + ); + compute_progressive_characteristics( + &format!("CHARACTERISTICS FOR {} FORMULA", self.label), + self.diagnostics_adapted_formula, + self.singleton_ref, + self.example_count_to_print, + ); + + ( + self.criteria_sum_so_far + .checked_add(self.criterion) + .expect("add overflow"), + self.account, + ) + } +} + +const AGE_POWER: u32 = 4; +const AGE_MULTIPLIER: u128 = 10; +//this parameter affects the steepness (sensitivity on increase in balance) +const BALANCE_LOG_2_BASE_MULTIPLIER: u128 = 9; +//this parameter affects proportion against the different criteria class +const BALANCE_OUTER_MULTIPLIER: u128 = 2; + //TODO think about splitting this file into a folder of files +type CriterionFormula<'a> = Box (u128, PayableAccount) + 'a>; + impl PaymentAdjusterReal { pub fn new() -> Self { Self {} @@ -217,8 +332,7 @@ impl PaymentAdjusterReal { } fn find_decent_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - const EMPIRIC_PRECISION_COEFFICIENT: usize = 6; - + const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; let criteria_sum_digits_count = log_10(criteria_sum); let cw_balance_digits_count = log_10(cw_masq_balance); let smallest_mul_coeff_between = criteria_sum_digits_count @@ -265,6 +379,11 @@ impl PaymentAdjusterReal { let original_balance = account.balance_wei; let proposed_adjusted_balance = criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + + diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { + proposed_adjusted_balance.separate_with_commas() + }); + if ((original_balance * 10) / 2) <= (proposed_adjusted_balance * 10) { account.balance_wei = proposed_adjusted_balance; let decided_account = ReversiblePayableAccount::new(account, original_balance); @@ -288,7 +407,7 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { let (required_balance_total, criteria_total) = - Self::compute_totals(&accounts_with_individual_criteria); + Self::compute_balance_and_criteria_totals(&accounts_with_individual_criteria); if let Some(prioritized_wallets) = Self::check_for_prioritized_accounts_that_qualify_without_prolongation( @@ -347,7 +466,7 @@ impl PaymentAdjusterReal { } } - fn compute_totals( + fn compute_balance_and_criteria_totals( accounts_with_individual_criteria: &[(u128, PayableAccount)], ) -> (u128, u128) { let required_balance_total: u128 = @@ -376,44 +495,65 @@ impl PaymentAdjusterReal { accounts_with_zero_criteria: impl Iterator, now: SystemTime, ) -> Vec<(u128, PayableAccount)> { - type CriteriaClosure<'a> = - Box (u128, PayableAccount) + 'a>; //define individual criteria as closures to be used in a map() //caution: always remember to use checked math operations! - let time_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let elapsed_sec: u64 = now - .duration_since(account.last_paid_timestamp) - .expect("time traveller") - .as_secs(); - let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; - let criterion = (elapsed_sec as u128) - .pow(4) - .checked_div(divisor) - .expect("div overflow"); - ( - criteria_sum.checked_add(criterion).expect("add overflow"), - account, - ) - }); - let balance_criteria_closure: CriteriaClosure = Box::new(|(criteria_sum, account)| { - let digits_weight = log_10(account.balance_wei); - let multiplier = (digits_weight as u128) - .checked_pow(3) - .expect("pow overflow"); - let criterion = account - .balance_wei - .checked_mul(multiplier) - .expect("mul overflow"); - ( - criteria_sum.checked_add(criterion).expect("add overflow"), + let age_criteria_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { + let formula = |last_paid_timestamp: SystemTime| { + let elapsed_sec: u64 = now + .duration_since(last_paid_timestamp) + .expect("time traveller") + .as_secs(); + let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; + (elapsed_sec as u128) + .pow(AGE_POWER) + .checked_div(divisor) + .expect("div overflow") + * AGE_MULTIPLIER + }; + let criterion = formula(account.last_paid_timestamp); + + FinalizationAndDiagnostics { account, - ) + criterion, + criteria_sum_so_far, + label: "AGE", + diagnostics_adapted_formula: |x: u128| { + let approx_before = SystemTime::now() + .checked_sub(Duration::from_secs(x as u64)) + .expect("characteristics for age blew up"); + formula(approx_before) + }, + singleton_ref: &AGE_SINGLETON, + example_count_to_print: 4, + } + .perform() }); + let balance_criteria_closure: CriterionFormula = + Box::new(|(criteria_sum_so_far, account)| { + // constants used to keep the weights of balance and time balanced + let formula = |balance_wei: u128| { + let binary_weight = log_2(balance_wei * BALANCE_LOG_2_BASE_MULTIPLIER); + let multiplier = (binary_weight as u128) * BALANCE_OUTER_MULTIPLIER; + balance_wei.checked_mul(multiplier).expect("mul overflow") + }; + let criterion = formula(account.balance_wei); + + FinalizationAndDiagnostics { + account, + criterion, + criteria_sum_so_far, + label: "BALANCE", + diagnostics_adapted_formula: |x: u128| formula(x), + singleton_ref: &BALANCE_SINGLETON, + example_count_to_print: 9, + } + .perform() + }); let weights_and_accounts = accounts_with_zero_criteria - .map(time_criteria_closure) + .map(age_criteria_closure) .map(balance_criteria_closure); Self::sort_in_descendant_order_by_weights(weights_and_accounts) @@ -446,7 +586,20 @@ impl PaymentAdjusterReal { // true means we would pay more than we were asked to pay at the beginning, // this happens when the debt size is quite small but its age is large and // plays the main factor - balance_ratio > criterion_ratio + let is_highly_significant = balance_ratio > criterion_ratio; + if is_highly_significant { + diagnostics( + &account.wallet, + "ACCOUNT PROPOSED WITH BALANCE HIGHER THAN THE ORIGIN ONE", + || { + format!( + "balance_ration ({}) > criterion_ration ({})", + balance_ratio, criterion_ratio + ) + }, + ) + } + is_highly_significant }) .map(|(_, account)| account.wallet.clone()) .collect::>(); @@ -507,7 +660,7 @@ impl PaymentAdjusterReal { original_account_balances_mapped .get(&account.wallet) .expectv("initial balance"), - EMPTY_STR, + NO_CHARS, account.balance_wei, length = WALLET_ADDRESS_LENGTH ) @@ -575,7 +728,7 @@ impl PaymentAdjusterReal { "Balance wei", "Adjusted payables", "Original", - EMPTY_STR, + NO_CHARS, "Adjusted", Self::format_brief_adjustment_summary( original_account_balances_mapped, @@ -592,9 +745,9 @@ impl PaymentAdjusterReal { disqualified_accounts.iter().for_each(|account| { info!( logger, - "Recently qualified payable for wallet {} is being ignored as the limited \ - consuming balance implied adjustment of its balance down to {} wei, which is not at least \ - half of the debt", + "Recently qualified payable for wallet {} is being ignored as the limited consuming \ + balance implied adjustment of its balance down to {} wei, which is not at least half \ + of the debt", account.wallet, account.proposed_adjusted_balance.separate_with_commas() ) @@ -621,8 +774,8 @@ impl PaymentAdjusterReal { warning!( logger, "Gas amount {} wei cannot cover anticipated fees from sending {} \ - transactions. Maximum is {}. The payments need to be adjusted in \ - their count.", + transactions. Maximum is {}. The payments need to be adjusted in \ + their count.", this_stage_data .consuming_wallet_balances .masq_tokens_wei @@ -641,14 +794,20 @@ impl PaymentAdjusterReal { } fn run_recursively( - already_fully_qualified_accounts: Vec, + resolved_qualified_accounts: Vec, + unresolved_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, gas_limitation_opt: Option, - qualified_payables: Vec, now: SystemTime, logger: &Logger, ) -> Vec { - let accounts_with_zero_criteria = Self::initialize_zero_criteria(qualified_payables); + diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); + diagnostics_collective( + "UNRESOLVED QUALIFIED ACCOUNTS:", + &unresolved_qualified_accounts, + ); + let accounts_with_zero_criteria = + Self::initialize_zero_criteria(unresolved_qualified_accounts); let sorted_accounts_with_individual_criteria = Self::apply_criteria(accounts_with_zero_criteria, now); @@ -679,19 +838,21 @@ impl PaymentAdjusterReal { //TODO what happens if we choose one that will get us into negative when subtracted return Self::run_recursively( adjustment_result.decided_accounts, + adjustment_result.remaining_accounts, adjusted_setup_data, None, - adjustment_result.remaining_accounts, now, logger, ); }; let adjusted_accounts_iter = adjusted_accounts.into_iter(); - already_fully_qualified_accounts + let result: Vec = resolved_qualified_accounts .into_iter() .chain(adjusted_accounts_iter) - .collect() + .collect(); + diagnostics_collective("FINAL ADJUSTED ACCOUNTS:", &result); + result } fn give_job_to_adjustment_workers( @@ -761,6 +922,17 @@ fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } +const fn num_bits() -> usize { + std::mem::size_of::() * 8 +} + +fn log_2(x: u128) -> u32 { + if x < 1 { + panic!("log2 of 0 not supported") + } + num_bits::() as u32 - x.leading_zeros() - 1 +} + #[derive(Debug)] struct AdjustmentIterationResult { decided_accounts: Vec, @@ -850,8 +1022,9 @@ pub enum AnalysisError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ - log_10, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, - PaymentAdjusterReal, ReversiblePayableAccount, REFILL_RECOMMENDATION, + log_10, log_2, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, + PaymentAdjusterReal, ReversiblePayableAccount, AGE_MULTIPLIER, AGE_POWER, + BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, REFILL_RECOMMENDATION, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -1133,6 +1306,27 @@ bans you will need to put more funds into your consuming wallet." .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) } + #[test] + fn log_2_works() { + [ + (1, 0), + (2, 1), + (4, 2), + (8192, 13), + (18446744073709551616, 64), + (1267650600228229401496703205376, 100), + (170141183460469231731687303715884105728, 127), + ] + .into_iter() + .for_each(|(num, expected_result)| assert_eq!(log_2(num), expected_result)) + } + + #[test] + #[should_panic(expected = "log2 of 0 not supported")] + fn log_2_dislikes_0() { + let _ = log_2(0); + } + #[test] fn multiplication_coeff_for_integers_to_be_above_one_instead_of_fractional_numbers() { let final_criteria_sum = 5_000_000_000_000_u128; @@ -1141,8 +1335,8 @@ bans you will need to put more funds into your consuming wallet." 100_000, 123_456_789, 5_555_000_000_000, - 50_555_000_000_000, - 500_555_000_000_000, + 5_000_555_000_000_000, + 1_000_000_000_000_000_000, //1 MASQ ]; let result = consuming_wallet_balances @@ -1159,12 +1353,12 @@ bans you will need to put more funds into your consuming wallet." assert_eq!( result, vec![ - 10_000_000, - 10_000_000_000_000, - 10_000_000_000, - 1_000_000, - 1_000_000, - 1_000_000 + 1_000_000_000, + 1_000_000_000_000_000, + 1_000_000_000_000, + 100_000_000, + 100_000_000, + 100_000_000 ] ) } @@ -1179,7 +1373,7 @@ bans you will need to put more funds into your consuming wallet." final_criteria_total, ); - assert_eq!(result, 100_000_000_000_000_000_000_000_000_000_000_000) + assert_eq!(result, 10_000_000_000_000_000_000_000_000_000_000_000_000) // enough space for our counts; mostly we use it for division and multiplication and // in both cases the coefficient is picked carefully to handle it (we near the extremes // either by increasing the criteria sum and decreasing the cw balance or vica versa) @@ -1348,9 +1542,9 @@ bans you will need to put more funds into your consuming wallet." let result = PaymentAdjusterReal::run_recursively( vec![], + qualified_payables.clone(), collected_setup_data, None, - qualified_payables.clone(), now, &logger, ); @@ -1378,7 +1572,7 @@ bans you will need to put more funds into your consuming wallet." account_2, //prioritized accounts take the first places PayableAccount { wallet: make_wallet("blah"), - balance_wei: 1_499_949_712_293, + balance_wei: 1_499_949_995_483, last_paid_timestamp: last_paid_timestamp_1, pending_payable_opt: None, }, @@ -1387,33 +1581,33 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn adjust_payments_when_number_of_accounts_evens_the_final_transaction_count() { + fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { init_test_logging(); - let test_name = "adjust_payments_when_number_of_accounts_evens_the_final_transaction_count"; + let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 444_444_444_444_444_444, + balance_wei: 4_444_444_444_444_444_444, last_paid_timestamp: now.checked_sub(Duration::from_secs(1_234)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 666_666_666_666_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100)).unwrap(), + balance_wei: 6_666_666_666_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghk"), - balance_wei: 22_000_000_000_000, + balance_wei: 60_000_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let subject = PaymentAdjusterReal::new(); let accounts_sum: u128 = - 444_444_444_444_444_444 + 666_666_666_666_000_000 + 22_000_000_000_000; //= 1_000_022_000_000_444_444 - let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 6_000_000_000_000_000); + 4_444_444_444_444_444_444 + 6_666_666_666_000_000_000 + 60_000_000_000_000_000; //= 1_000_022_000_000_444_444 + let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 70_000_000_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1456,122 +1650,16 @@ bans you will need to put more funds into your consuming wallet." |Adjusted payables Original | Adjusted | -|0x0000000000000000000000000000000000646566 666666666666000000 -| 663067295999338638 -|0x0000000000000000000000000000000000616263 444444444444444444 -| 442044864010984732 -|0x000000000000000000000000000000000067686b 22000000000000 -| 15053705795285" +|0x0000000000000000000000000000000000646566 6666666666000000000 +| 6627452261726961117 +|0x0000000000000000000000000000000000616263 4444444444444444444 +| 4418301511592193084 +|0x000000000000000000000000000000000067686b 60000000000000000 +| 55357206066348214" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } - fn emulation_of_the_actual_adjustment_algorithm( - account_1: PayableAccount, - account_2: PayableAccount, - account_3_opt: Option, - consuming_wallet_masq_balance_wei: u128, - now: SystemTime, - ) -> Vec { - let accounts = vec![ - Some(account_1.clone()), - Some(account_2.clone()), - account_3_opt.clone(), - ] - .into_iter() - .flatten() - .collect::>(); - let time_criteria = accounts - .iter() - .map(|account| { - let elapsed = secs_elapsed(account.last_paid_timestamp, now); - let criterion = elapsed.pow(4) / (((elapsed as f64).sqrt().ceil()) as u128); - eprintln!("time criterion: {}", criterion.separate_with_commas()); - criterion - }) - .collect(); - let amount_criteria = accounts - .iter() - .map(|account| { - let significance = log_10(account.balance_wei) as u128; - account.balance_wei * significance.pow(3) - } as u128) - .collect(); - - let final_criteria = vec![time_criteria, amount_criteria].into_iter().fold( - vec![0, 0, 0], - |acc: Vec, current: Vec| { - acc.into_iter() - .zip(current.into_iter()) - .map(|(partial_acc, partial_current)| partial_acc + partial_current) - .collect() - }, - ); - - eprintln!("final criteria {:?}", final_criteria); - let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = PaymentAdjusterReal::find_decent_multiplication_coeff( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); - eprintln!( - "emul: consuming balance for fragment computation: {}", - consuming_wallet_masq_balance_wei - ); - let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei - .checked_mul(multiplication_coeff) - .unwrap() - .checked_div(final_criteria_sum) - .unwrap(); - - eprintln!( - "emulated in ration fragment: {}", - in_ratio_fragment_of_available_balance - ); - let balanced_portions = final_criteria - .iter() - .map(|criterion| { - in_ratio_fragment_of_available_balance * criterion / multiplication_coeff - }) - .collect::>(); - eprintln!("balanced portions: {:?}", balanced_portions); - let new_total_amount_to_pay = balanced_portions.iter().sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); - assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, - "new total amount to pay: {}, consuming wallet masq balance: {}", - new_total_amount_to_pay, - consuming_wallet_masq_balance_wei - ); - let mut account_1_adjusted = account_1; - account_1_adjusted.balance_wei = balanced_portions[0]; - let mut account_2_adjusted = account_2; - account_2_adjusted.balance_wei = balanced_portions[1]; - let account_3_adjusted_opt = { - match account_3_opt { - Some(mut account) => Some({ - account.balance_wei = balanced_portions[2]; - account - }), - None => None, - } - }; - - vec![ - Some((final_criteria[0], account_1_adjusted)), - Some((final_criteria[1], account_2_adjusted)), - match account_3_adjusted_opt { - Some(account) => Some((final_criteria[2], account)), - None => None, - }, - ] - .into_iter() - .flatten() - .sorted_by(|(criterion_a, _), (criterion_b, _)| Ord::cmp(&criterion_b, &criterion_a)) - .map(|(_, account)| account) - .collect() - } - #[test] fn adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut( ) { @@ -1653,20 +1741,22 @@ bans you will need to put more funds into your consuming wallet." init_test_logging(); let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count"; let now = SystemTime::now(); + let wallet_1 = make_wallet("def"); let account_1 = PayableAccount { - wallet: make_wallet("def"), + wallet: wallet_1.clone(), balance_wei: 333_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }; + let wallet_2 = make_wallet("abc"); let account_2 = PayableAccount { - wallet: make_wallet("abc"), + wallet: wallet_2.clone(), balance_wei: 111_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghk"); - let balance_3 = 50_000_000; + let balance_3 = 600_000_000; let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: balance_3, @@ -1728,6 +1818,12 @@ bans you will need to put more funds into your consuming wallet." consuming_wallet_masq_balance_wei.as_u128(), now, ); + let wallets_of_final_accounts = result + .accounts + .iter() + .map(|account| account.wallet.clone()) + .collect::>(); + assert_eq!(wallets_of_final_accounts, vec![wallet_1, wallet_2]); assert_eq!(result.accounts, expected_accounts); assert_eq!( result.response_skeleton_opt, @@ -1738,7 +1834,7 @@ bans you will need to put more funds into your consuming wallet." ); TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ - consuming balance implied adjustment of its balance down to 22,572,576 wei, which is not at \ + consuming balance implied adjustment of its balance down to 56,551,122 wei, which is not at \ least half of the debt")); } @@ -1761,10 +1857,10 @@ bans you will need to put more funds into your consuming wallet." wallet: make_wallet("cdef"), ..account_1.clone() }; - account_2.balance_wei = 90_000_000_000_000; + account_2.balance_wei = 100_000_000_000_000; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 + 1); + let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 - 1); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1787,6 +1883,7 @@ bans you will need to put more funds into your consuming wallet." let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); eprintln!("accounts adjusted: {:?}", result.accounts); + todo!("finish me") // let expected_accounts_first_iteration = emulation_of_the_actual_adjustment_algorithm( // account_1.clone(), // account_2.clone(), @@ -1963,22 +2060,22 @@ bans you will need to put more funds into your consuming wallet." init_test_logging(); let test_name = "adjust_payments_when_masq_as_well_as_gas_will_limit_the_count"; let now = SystemTime::now(); - //thrown away by gas + //thrown away as the second one because of its insignificance (proposed adjusted balance is smaller than half the original) let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 44_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + balance_wei: 10_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - //thrown away because not enough significant + //thrown away as the first one because of gas let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 55_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghk"); - let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(4444)).unwrap(); + let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: 333_000_000_000_000, @@ -2022,6 +2119,8 @@ bans you will need to put more funds into your consuming wallet." ); assert_eq!(only_account.last_paid_timestamp, last_paid_timestamp_3); assert_eq!(only_account.pending_payable_opt, None); + + //TODO if there is the only account remaining why don't we use the exact value...just the original balance..we would fit easily let log_msg = format!( "DEBUG: {test_name}: \n\ |Account wallet Balance wei @@ -2030,12 +2129,12 @@ bans you will need to put more funds into your consuming wallet." | Adjusted | |0x000000000000000000000000000000000067686b 333000000000000 -| 299999980172486 +| 299999993782194 | |Ignored minor payables Original | -|0x0000000000000000000000000000000000646566 55000000000 -|0x0000000000000000000000000000000000616263 44000000000" +|0x0000000000000000000000000000000000616263 10000000000000 +|0x0000000000000000000000000000000000646566 55000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -2043,4 +2142,97 @@ bans you will need to put more funds into your consuming wallet." fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { now.duration_since(timestamp).unwrap().as_secs() as u128 } + + fn emulation_of_the_actual_adjustment_algorithm( + account_1: PayableAccount, + account_2: PayableAccount, + account_3_opt: Option, + consuming_wallet_masq_balance_wei: u128, + now: SystemTime, + ) -> Vec { + let accounts = vec![ + Some(account_1.clone()), + Some(account_2.clone()), + account_3_opt.clone(), + ] + .into_iter() + .flatten() + .collect::>(); + let age_criteria = accounts + .iter() + .map(|account| { + let elapsed = secs_elapsed(account.last_paid_timestamp, now); + elapsed.pow(AGE_POWER) / (((elapsed as f64).sqrt().ceil()) as u128) * AGE_MULTIPLIER + }) + .collect(); + let balance_criteria = accounts + .iter() + .map(|account| { + let significance = + log_2(account.balance_wei * BALANCE_LOG_2_BASE_MULTIPLIER) as u128; + account.balance_wei * (significance * BALANCE_OUTER_MULTIPLIER) + } as u128) + .collect(); + + let final_criteria = vec![age_criteria, balance_criteria].into_iter().fold( + vec![0, 0, 0], + |acc: Vec, current: Vec| { + acc.into_iter() + .zip(current.into_iter()) + .map(|(partial_acc, partial_current)| partial_acc + partial_current) + .collect() + }, + ); + let final_criteria_sum = final_criteria.iter().sum::(); + let multiplication_coeff = PaymentAdjusterReal::find_decent_multiplication_coeff( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); + let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei + .checked_mul(multiplication_coeff) + .unwrap() + .checked_div(final_criteria_sum) + .unwrap(); + let balanced_portions = final_criteria + .iter() + .map(|criterion| { + in_ratio_fragment_of_available_balance * criterion / multiplication_coeff + }) + .collect::>(); + let new_total_amount_to_pay = balanced_portions.iter().sum::(); + assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); + assert!( + new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, + "new total amount to pay: {}, consuming wallet masq balance: {}", + new_total_amount_to_pay, + consuming_wallet_masq_balance_wei + ); + let mut account_1_adjusted = account_1; + account_1_adjusted.balance_wei = balanced_portions[0]; + let mut account_2_adjusted = account_2; + account_2_adjusted.balance_wei = balanced_portions[1]; + let account_3_adjusted_opt = { + match account_3_opt { + Some(mut account) => Some({ + account.balance_wei = balanced_portions[2]; + account + }), + None => None, + } + }; + + vec![ + Some((final_criteria[0], account_1_adjusted)), + Some((final_criteria[1], account_2_adjusted)), + match account_3_adjusted_opt { + Some(account) => Some((final_criteria[2], account)), + None => None, + }, + ] + .into_iter() + .flatten() + .sorted_by(|(criterion_a, _), (criterion_b, _)| Ord::cmp(&criterion_b, &criterion_a)) + .map(|(_, account)| account) + .collect() + } } From f4ed38a6b3cf26d5e1b6663b0329178b34211fe3 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 15 Jul 2023 00:05:29 +0200 Subject: [PATCH 040/250] GH-711: reorganized by separate files; also working the concept of 'tail weight' and advanced const parameters to twiggle with the setting --- node/src/accountant/mod.rs | 2 +- .../payment_adjuster/diagnostics.rs | 111 ++ .../payment_adjuster/log_functions.rs | 217 +++ .../mod.rs} | 1316 ++++++++--------- 4 files changed, 939 insertions(+), 707 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/diagnostics.rs create mode 100644 node/src/accountant/payment_adjuster/log_functions.rs rename node/src/accountant/{payment_adjuster.rs => payment_adjuster/mod.rs} (80%) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index e0ba6c567..7db4e2821 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -3,7 +3,7 @@ pub mod big_int_processing; pub mod database_access_objects; pub mod financials; -pub mod payment_adjuster; +mod payment_adjuster; pub mod scanners; #[cfg(test)] diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs new file mode 100644 index 000000000..5aca37fe9 --- /dev/null +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::{ + COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, +}; +use crate::sub_lib::wallet::Wallet; +use std::sync::Once; +use thousands::Separable; + +pub static AGE_SINGLETON: Once = Once::new(); +pub static BALANCE_SINGLETON: Once = Once::new(); + +pub fn diagnostics(account: &Wallet, description: &str, value_renderer: F) +where + F: Fn() -> String, +{ + if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { + eprintln!( + "{} {:( + label: &str, + formula: F, + singleton: &Once, + examples_count: usize, +) where + F: Fn(u128) -> u128, +{ + if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { + singleton.call_once(|| { + let different_values_for_chief_parameter: Vec<(u128, u32)> = vec![ + (10, 1), + (10, 3), + (10, 6), + (10, 9), + (10, 12), + (10, 15), + (10, 18), + ]; + + eprintln!("{}", label); + different_values_for_chief_parameter + .into_iter() + .take(examples_count) + .map(|(base, factor)| base.pow(factor)) + .for_each(|num| { + let value = formula(num); + eprintln!("x: {: +where + F: Fn(u128) -> u128, +{ + pub account: PayableAccount, + pub criterion: u128, + pub criteria_sum_so_far: u128, + // below only diagnostics purposes + pub label: &'static str, + pub diagnostics_adapted_formula: F, + pub singleton_ref: &'a Once, + // max 9 + pub safe_count_of_examples_to_print: usize, +} + +impl FinalizationAndDiagnostics<'_, F> +where + F: Fn(u128) -> u128, +{ + pub fn perform(self) -> (u128, PayableAccount) { + diagnostics( + &self.account.wallet, + &format!("COMPUTED {} CRITERIA", self.label), + || self.criterion.separate_with_commas(), + ); + compute_progressive_characteristics( + &format!("CHARACTERISTICS FOR {} FORMULA", self.label), + self.diagnostics_adapted_formula, + self.singleton_ref, + self.safe_count_of_examples_to_print, + ); + + ( + self.criteria_sum_so_far + .checked_add(self.criterion) + .expect("add overflow"), + self.account, + ) + } +} diff --git a/node/src/accountant/payment_adjuster/log_functions.rs b/node/src/accountant/payment_adjuster/log_functions.rs new file mode 100644 index 000000000..5b0aa80f8 --- /dev/null +++ b/node/src/accountant/payment_adjuster/log_functions.rs @@ -0,0 +1,217 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::DisqualifiedPayableAccount; +use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; +use crate::masq_lib::utils::ExpectValue; +use crate::sub_lib::wallet::Wallet; +use itertools::Itertools; +use masq_lib::constants::WALLET_ADDRESS_LENGTH; +use masq_lib::logger::Logger; +use std::collections::HashMap; +use std::iter::once; +use std::ops::Not; +use thousands::Separable; + +const REFILL_RECOMMENDATION: &str = "\ +In order to continue using services of other Nodes and avoid delinquency \ +bans you will need to put more funds into your consuming wallet."; + +const NO_CHARS: &str = ""; + +pub fn format_brief_adjustment_summary( + original_account_balances_mapped: HashMap, + adjusted_accounts: &[PayableAccount], +) -> String { + fn format_summary_for_included_accounts( + original_account_balances_mapped: &HashMap, + adjusted_accounts: &[PayableAccount], + ) -> String { + adjusted_accounts + .into_iter() + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + }) + .map(|account| { + format!( + "{} {}\n{:^length$} {}", + account.wallet, + original_account_balances_mapped + .get(&account.wallet) + .expectv("initial balance"), + NO_CHARS, + account.balance_wei, + length = WALLET_ADDRESS_LENGTH + ) + }) + .join("\n") + } + fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { + let title = once(format!( + "\n{: = adjusted_accounts + .iter() + .map(|account| &account.wallet) + .collect(); + let excluded: Vec<(&Wallet, u128)> = original_account_balances_mapped.iter().fold( + vec![], + |mut acc, (wallet, original_balance)| { + if !adjusted_accounts_wallets.contains(&wallet) { + acc.push((wallet, *original_balance)); + } + acc + }, + ); + let adjusted_accounts_summary = + format_summary_for_included_accounts(&original_account_balances_mapped, adjusted_accounts); + let excluded_accounts_summary_opt = excluded + .is_empty() + .not() + .then(|| format_summary_for_excluded_accounts(&excluded)); + vec![ + Some(adjusted_accounts_summary), + excluded_accounts_summary_opt, + ] + .into_iter() + .flatten() + .join("\n") +} + +pub fn before_and_after_debug_msg( + original_account_balances_mapped: HashMap, + adjusted_accounts: &[PayableAccount], +) -> String { + format!( + "\n\ + {: String { + format!("Recently qualified payable for wallet {wallet} is being ignored as the limited consuming \ + balance implied adjustment of its balance down to {} wei, which is not at least half of the debt", balance.separate_with_commas()) + }; + TestLogHandler::new().assert_logs_contain_in_order(vec![ + &make_expected_msg(&wallet_1, balance_1), + &make_expected_msg(&wallet_2, balance_2), + ]); + } +} diff --git a/node/src/accountant/payment_adjuster.rs b/node/src/accountant/payment_adjuster/mod.rs similarity index 80% rename from node/src/accountant/payment_adjuster.rs rename to node/src/accountant/payment_adjuster/mod.rs index 6682c8ad3..e920504ba 100644 --- a/node/src/accountant/payment_adjuster.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,6 +1,17 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +mod diagnostics; +mod log_functions; + use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics::{ + diagnostics, diagnostics_collective, FinalizationAndDiagnostics, AGE_SINGLETON, + BALANCE_SINGLETON, +}; +use crate::accountant::payment_adjuster::log_functions::{ + before_and_after_debug_msg, log_adjustment_by_masq_required, + log_info_for_disqualified_accounts, log_insufficient_transaction_fee_balance, +}; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; @@ -10,25 +21,15 @@ use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; -use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; use std::collections::HashMap; -use std::fmt::Display; use std::iter::{once, successors}; -use std::ops::Not; -use std::sync::Once; use std::time::{Duration, SystemTime}; use thousands::Separable; use web3::types::U256; -const REFILL_RECOMMENDATION: &str = "\ -In order to continue using services of other Nodes and avoid delinquency \ -bans you will need to put more funds into your consuming wallet."; - -const NO_CHARS: &str = ""; - pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, @@ -128,10 +129,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { debug!( logger, "{}", - Self::before_and_after_debug_msg( - debug_info_opt.expectv("debug info"), - &adjusted_accounts - ) + before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); OutcomingPaymentsInstructions { @@ -151,114 +149,32 @@ impl Default for PaymentAdjusterReal { const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = false; -static AGE_SINGLETON: Once = Once::new(); -static BALANCE_SINGLETON: Once = Once::new(); - -fn diagnostics(account: &Wallet, description: &str, value_renderer: F) -where - F: Fn() -> String, -{ - if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { - eprintln!( - "{} {:( - label: &str, - formula: F, - singleton: &Once, - examples_count: usize, -) where - F: Fn(u128) -> u128, -{ - if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { - singleton.call_once(|| { - let different_values_for_chief_parameter: Vec<(u128, u32)> = vec![ - (10, 1), - (10, 3), - (10, 6), - (10, 9), - (10, 12), - (10, 15), - (10, 18), - ]; - - eprintln!("{}", label); - different_values_for_chief_parameter - .into_iter() - .take(examples_count) - .map(|(base, factor)| base.pow(factor)) - .for_each(|num| { - let value = formula(num); - eprintln!("x: {: -where - F: Fn(u128) -> u128, -{ - account: PayableAccount, - criterion: u128, - criteria_sum_so_far: u128, - //below only diagnostics purposes - label: &'static str, - diagnostics_adapted_formula: F, - singleton_ref: &'a Once, - //max 9 - example_count_to_print: usize, -} - -impl FinalizationAndDiagnostics<'_, F> -where - F: Fn(u128) -> u128, -{ - fn perform(self) -> (u128, PayableAccount) { - diagnostics( - &self.account.wallet, - &format!("COMPUTED {} CRITERIA", self.label), - || self.criterion.separate_with_commas(), - ); - compute_progressive_characteristics( - &format!("CHARACTERISTICS FOR {} FORMULA", self.label), - self.diagnostics_adapted_formula, - self.singleton_ref, - self.example_count_to_print, - ); - - ( - self.criteria_sum_so_far - .checked_add(self.criterion) - .expect("add overflow"), - self.account, - ) - } -} -const AGE_POWER: u32 = 4; +const AGE_EXPONENT: u32 = 4; const AGE_MULTIPLIER: u128 = 10; -//this parameter affects the steepness (sensitivity on increase in balance) +// this parameter affects the steepness (sensitivity on increase in balance) const BALANCE_LOG_2_BASE_MULTIPLIER: u128 = 9; -//this parameter affects proportion against the different criteria class +// this parameter affects proportion against the different criteria class const BALANCE_OUTER_MULTIPLIER: u128 = 2; +const BALANCE_TAIL_WEIGHT_MASK: u128 = 0xFFF; +const BALANCE_TAIL_WEIGHT_EXPONENT: u32 = 2; +const BALANCE_TAIL_WEIGHT_MULTIPLIER: u128 = 3; +// represents 50% +const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = + PercentageAccountInsignificance { + multiplier: 1, + divisor: 2, + }; + +// sets the minimal percentage of the original balance that must be +// proposed after the adjustment or the account will be eliminated for insignificance +#[derive(Debug, PartialEq, Eq)] +struct PercentageAccountInsignificance { + // using integers means we have to represent accurate percentage + // as set of two constants + multiplier: u128, + divisor: u128, +} //TODO think about splitting this file into a folder of files @@ -277,14 +193,6 @@ impl PaymentAdjusterReal { collection.iter().map(arranger).sum::().into() } - fn sum_payable_balances(qualified_accounts: &[PayableAccount]) -> U256 { - qualified_accounts - .iter() - .map(|account| account.balance_wei) - .sum::() - .into() - } - // fn find_smallest_debt(qualified_accounts: &[&PayableAccount]) -> u128 { // qualified_accounts // .iter() @@ -321,7 +229,7 @@ impl PaymentAdjusterReal { } else { let limiting_count = u16::try_from(limiting_max_possible_count) .expectv("small number for possible tx count"); - Self::log_insufficient_transaction_fee_balance( + log_insufficient_transaction_fee_balance( logger, required_transactions_count, tech_info, @@ -331,15 +239,95 @@ impl PaymentAdjusterReal { } } - fn find_decent_multiplication_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; - let criteria_sum_digits_count = log_10(criteria_sum); - let cw_balance_digits_count = log_10(cw_masq_balance); - let smallest_mul_coeff_between = criteria_sum_digits_count - .checked_sub(cw_balance_digits_count) - .unwrap_or(0); - let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_COEFFICIENT; - 10_u128.pow(safe_mul_coeff as u32) + fn check_need_of_masq_balances_adjustment( + logger: &Logger, + qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, + consuming_wallet_balance_wei: u128, + ) -> bool { + let qualified_payables: Vec<&PayableAccount> = match qualified_payables { + Either::Left(accounts) => accounts.iter().collect(), + Either::Right(criteria_and_accounts) => criteria_and_accounts + .iter() + .map(|(_, account)| account) + .collect(), + }; + let required_masq_sum: u128 = + Self::sum_as(&qualified_payables, |account: &&PayableAccount| { + account.balance_wei + }); + + if required_masq_sum <= consuming_wallet_balance_wei { + false + } else { + log_adjustment_by_masq_required( + logger, + required_masq_sum, + consuming_wallet_balance_wei, + ); + true + } + } + + fn run_recursively( + resolved_qualified_accounts: Vec, + unresolved_qualified_accounts: Vec, + collected_setup_data: FinancialAndTechDetails, + gas_limitation_opt: Option, + now: SystemTime, + logger: &Logger, + ) -> Vec { + diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); + diagnostics_collective( + "UNRESOLVED QUALIFIED ACCOUNTS:", + &unresolved_qualified_accounts, + ); + let accounts_with_zero_criteria = + Self::initialize_zero_criteria(unresolved_qualified_accounts); + let sorted_accounts_with_individual_criteria = + Self::apply_criteria(accounts_with_zero_criteria, now); + + let cw_masq_balance_wei = collected_setup_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128(); + let adjustment_result: AdjustmentIterationResult = + match Self::give_job_to_adjustment_workers( + gas_limitation_opt, + sorted_accounts_with_individual_criteria, + cw_masq_balance_wei, + logger, + ) { + AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, + AdjustmentCompletion::Continue(iteration_result) => iteration_result, + }; + + log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); + + let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { + adjustment_result.decided_accounts + } else { + let adjusted_setup_data = Self::adjust_cw_balance_in_setup_data( + collected_setup_data, + &adjustment_result.decided_accounts, + ); + //TODO what happens if we choose one that will get us into negative when subtracted + return Self::run_recursively( + adjustment_result.decided_accounts, + adjustment_result.remaining_accounts, + adjusted_setup_data, + None, + now, + logger, + ); + }; + + let adjusted_accounts_iter = adjusted_accounts.into_iter(); + let result: Vec = resolved_qualified_accounts + .into_iter() + .chain(adjusted_accounts_iter) + .collect(); + diagnostics_collective("FINAL ADJUSTED ACCOUNTS:", &result); + result } fn initialize_zero_criteria( @@ -356,141 +344,6 @@ impl PaymentAdjusterReal { criteria_iterator.zip(qualified_payables.into_iter()) } - fn recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - cw_masq_balance: u128, - criteria_total: u128, - ) -> ( - Vec, - Vec, - ) { - let multiplication_coeff = - PaymentAdjusterReal::find_decent_multiplication_coeff(cw_masq_balance, criteria_total); - - let proportional_fragment_of_cw_balance = cw_masq_balance - .checked_mul(multiplication_coeff) - .expect("mul overflow") - .checked_div(criteria_total) - .expect("div overflow"); - - accounts_with_individual_criteria.into_iter().fold( - (vec![], vec![]), - |(mut decided, mut disqualified), (criteria_sum, mut account)| { - let original_balance = account.balance_wei; - let proposed_adjusted_balance = - criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; - - diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { - proposed_adjusted_balance.separate_with_commas() - }); - - if ((original_balance * 10) / 2) <= (proposed_adjusted_balance * 10) { - account.balance_wei = proposed_adjusted_balance; - let decided_account = ReversiblePayableAccount::new(account, original_balance); - decided.push(decided_account); - (decided, disqualified) - } else { - let disqualified_account = DisqualifiedPayableAccount::new( - account.wallet, - original_balance, - proposed_adjusted_balance, - ); - disqualified.push(disqualified_account); - (decided, disqualified) - } - }, - ) - } - - fn handle_masq_token_adjustment( - cw_masq_balance: u128, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> AdjustmentIterationResult { - let (required_balance_total, criteria_total) = - Self::compute_balance_and_criteria_totals(&accounts_with_individual_criteria); - - if let Some(prioritized_wallets) = - Self::check_for_prioritized_accounts_that_qualify_without_prolongation( - &accounts_with_individual_criteria, - required_balance_total, - criteria_total, - ) - { - let (prioritized, remaining): (Vec, Vec) = - accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .partition(|account| prioritized_wallets.contains(&account.wallet)); - let result = AdjustmentIterationResult { - decided_accounts: prioritized, - remaining_accounts: remaining, - disqualified_accounts: vec![], - }; - return result; - }; - - //TODO starting here...wrap this up into a separate method - let (decided_accounts, disqualified_accounts): ( - Vec, - Vec, - ) = Self::recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria, - cw_masq_balance, - criteria_total, - ); - - if disqualified_accounts.is_empty() { - let decided_accounts = Self::finalize_decided_accounts( - decided_accounts, - DecidedPayableAccountResolution::Finalize, - ); - AdjustmentIterationResult { - decided_accounts, - remaining_accounts: vec![], - disqualified_accounts: vec![], - } - } else { - // reverting decided accounts because after we lose the disqualified ones from - // the compilation it may be that the remaining accounts could be now paid - // in more favorable proportions or even in the full size - let remaining_accounts = Self::finalize_decided_accounts( - decided_accounts, - DecidedPayableAccountResolution::Revert, - ); - AdjustmentIterationResult { - decided_accounts: vec![], - remaining_accounts, - disqualified_accounts, - } - //TODO ending here - } - } - - fn compute_balance_and_criteria_totals( - accounts_with_individual_criteria: &[(u128, PayableAccount)], - ) -> (u128, u128) { - let required_balance_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { - account.balance_wei - }); - - let criteria_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }); - (required_balance_total, criteria_total) - } - - fn finalize_decided_accounts( - decided_accounts: Vec, - resolution: DecidedPayableAccountResolution, - ) -> Vec { - decided_accounts - .into_iter() - .map(|decided_account| PayableAccount::from((decided_account, resolution))) - .collect() - } - fn apply_criteria( accounts_with_zero_criteria: impl Iterator, now: SystemTime, @@ -507,7 +360,7 @@ impl PaymentAdjusterReal { .as_secs(); let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; (elapsed_sec as u128) - .pow(AGE_POWER) + .pow(AGE_EXPONENT) .checked_div(divisor) .expect("div overflow") * AGE_MULTIPLIER @@ -526,7 +379,7 @@ impl PaymentAdjusterReal { formula(approx_before) }, singleton_ref: &AGE_SINGLETON, - example_count_to_print: 4, + safe_count_of_examples_to_print: 4, } .perform() }); @@ -536,7 +389,13 @@ impl PaymentAdjusterReal { let formula = |balance_wei: u128| { let binary_weight = log_2(balance_wei * BALANCE_LOG_2_BASE_MULTIPLIER); let multiplier = (binary_weight as u128) * BALANCE_OUTER_MULTIPLIER; - balance_wei.checked_mul(multiplier).expect("mul overflow") + let multiplied = balance_wei.checked_mul(multiplier).expect("mul overflow"); + let tail_weight = Self::balance_tail_weight(balance_wei); + eprintln!("tail weight {}", tail_weight); + let tail_weight_stressed = tail_weight.pow(BALANCE_TAIL_WEIGHT_EXPONENT) + * BALANCE_TAIL_WEIGHT_MULTIPLIER; + eprintln!("tail weight stressed {}", tail_weight_stressed); + multiplied + tail_weight_stressed }; let criterion = formula(account.balance_wei); @@ -547,7 +406,7 @@ impl PaymentAdjusterReal { label: "BALANCE", diagnostics_adapted_formula: |x: u128| formula(x), singleton_ref: &BALANCE_SINGLETON, - example_count_to_print: 9, + safe_count_of_examples_to_print: 9, } .perform() }); @@ -559,24 +418,215 @@ impl PaymentAdjusterReal { Self::sort_in_descendant_order_by_weights(weights_and_accounts) } - fn cut_back_by_gas_count_limit( - weights_and_accounts: Vec<(u128, PayableAccount)>, - limit: u16, - ) -> Vec<(u128, PayableAccount)> { - weights_and_accounts - .into_iter() - .take(limit as usize) - .collect() + //this enables to sense also small differences + fn balance_tail_weight(balance_wei: u128) -> u128 { + BALANCE_TAIL_WEIGHT_MASK - (balance_wei & BALANCE_TAIL_WEIGHT_MASK) + } + + fn give_job_to_adjustment_workers( + gas_limitation_opt: Option, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + cw_masq_balance_wei: u128, + logger: &Logger, + ) -> AdjustmentCompletion { + match gas_limitation_opt { + Some(gas_limit) => { + let weighted_accounts_cut_by_gas = + Self::cut_back_by_gas_count_limit(accounts_with_individual_criteria, gas_limit); + match Self::check_need_of_masq_balances_adjustment( + logger, + Either::Right(&weighted_accounts_cut_by_gas), + cw_masq_balance_wei, + ) { + true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( + cw_masq_balance_wei, + weighted_accounts_cut_by_gas, + )), + false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + weighted_accounts_cut_by_gas, + )), + } + } + None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( + cw_masq_balance_wei, + accounts_with_individual_criteria, + )), + } } - fn check_for_prioritized_accounts_that_qualify_without_prolongation( + fn handle_masq_token_adjustment( + cw_masq_balance: u128, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + ) -> AdjustmentIterationResult { + let (required_balance_total, criteria_total) = + Self::compute_balance_and_criteria_totals(&accounts_with_individual_criteria); + + match Self::try_separating_outweighed_accounts( + accounts_with_individual_criteria, + required_balance_total, + criteria_total, + cw_masq_balance, + ) { + Either::Left(accounts_with_individual_criteria) => { + Self::perform_adjustment_and_determine_adjustment_iteration_result( + accounts_with_individual_criteria, + cw_masq_balance, + criteria_total, + ) + } + Either::Right((outweighed, remaining)) => { + AdjustmentIterationResult { + decided_accounts: outweighed, + remaining_accounts: remaining, + disqualified_accounts: vec![], + } + } + } + } + + fn perform_adjustment_and_determine_adjustment_iteration_result( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + cw_masq_balance: u128, + criteria_total: u128, + ) -> AdjustmentIterationResult { + let (decided_accounts, disqualified_accounts): ( + Vec, + Vec, + ) = Self::recreate_accounts_with_proportioned_balances( + accounts_with_individual_criteria, + cw_masq_balance, + criteria_total, + ); + + if disqualified_accounts.is_empty() { + let decided_accounts = Self::finalize_decided_accounts( + decided_accounts, + DecidedPayableAccountResolution::Finalize, + ); + AdjustmentIterationResult { + decided_accounts, + remaining_accounts: vec![], + disqualified_accounts: vec![], + } + } else { + // reverting decided accounts because after we lose the disqualified ones from + // the compilation it may be that the remaining accounts could be now paid + // in more favorable proportions or even in the full size + let remaining_accounts = Self::finalize_decided_accounts( + decided_accounts, + DecidedPayableAccountResolution::Revert, + ); + AdjustmentIterationResult { + decided_accounts: vec![], + remaining_accounts, + disqualified_accounts, + } + } + } + + fn recreate_accounts_with_proportioned_balances( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + cw_masq_balance: u128, + criteria_total: u128, + ) -> ( + Vec, + Vec, + ) { + let multiplication_coeff = + PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( + cw_masq_balance, + criteria_total, + ); + + let proportional_fragment_of_cw_balance = cw_masq_balance + .checked_mul(multiplication_coeff) + .expect("mul overflow") + .checked_div(criteria_total) + .expect("div overflow"); + + accounts_with_individual_criteria.into_iter().fold( + (vec![], vec![]), + |(decided, disqualified), (criteria_sum, account)| { + let original_balance = account.balance_wei; + let proposed_adjusted_balance = + criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + + diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { + proposed_adjusted_balance.separate_with_commas() + }); + + Self::consider_account_disqualification_from_percentage_insignificance( + decided, + disqualified, + account, + original_balance, + proposed_adjusted_balance, + ) + }, + ) + } + + fn consider_account_disqualification_from_percentage_insignificance( + mut decided: Vec, + mut disqualified: Vec, + mut account: PayableAccount, + original_balance: u128, + proposed_adjusted_balance: u128, + ) -> ( + Vec, + Vec, + ) { + let balance_at_the_edge = + ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor); + if balance_at_the_edge <= (proposed_adjusted_balance * 10) { + account.balance_wei = proposed_adjusted_balance; + let decided_account = ReversiblePayableAccount::new(account, original_balance); + decided.push(decided_account); + (decided, disqualified) + } else { + let disqualified_account = DisqualifiedPayableAccount::new( + account.wallet, + original_balance, + proposed_adjusted_balance, + ); + disqualified.push(disqualified_account); + (decided, disqualified) + } + } + + fn try_separating_outweighed_accounts( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + required_balance_total: u128, + criteria_total: u128, + cw_masq_balance: u128 + ) -> Either, (Vec, Vec)> { + match Self::check_for_outweighed_accounts( + &accounts_with_individual_criteria, + required_balance_total, + criteria_total, + ) { + None => Either::Left(accounts_with_individual_criteria), + Some(wallets_of_outweighed) => Either::Right({ + let (outweighed, remaining) = + accounts_with_individual_criteria + .into_iter() + // TODO use the criteria later, if you have more than one outweighed accounts but you don't have enough balance for them... + .map(|(_, account)| account) + .partition(|account| wallets_of_outweighed.contains(&account.wallet)); + (outweighed, remaining) + }) + } + } + + fn check_for_outweighed_accounts( accounts_with_individual_criteria: &[(u128, PayableAccount)], required_balance_total: u128, criteria_total: u128, ) -> Option> { let required_balance_total_for_safe_math = required_balance_total * 10_000; let criteria_total_for_safe_math = criteria_total * 10_000; - let accounts_to_be_prioritized = accounts_with_individual_criteria + let accounts_to_be_outweighed = accounts_with_individual_criteria .iter() .filter(|(criterion, account)| { //account.balance_wei is still the original balance @@ -587,6 +637,7 @@ impl PaymentAdjusterReal { // this happens when the debt size is quite small but its age is large and // plays the main factor let is_highly_significant = balance_ratio > criterion_ratio; + if is_highly_significant { diagnostics( &account.wallet, @@ -599,12 +650,13 @@ impl PaymentAdjusterReal { }, ) } + is_highly_significant }) .map(|(_, account)| account.wallet.clone()) .collect::>(); - if !accounts_to_be_prioritized.is_empty() { - Some(accounts_to_be_prioritized) + if !accounts_to_be_outweighed.is_empty() { + Some(accounts_to_be_outweighed) } else { None } @@ -612,10 +664,18 @@ impl PaymentAdjusterReal { fn adjust_cw_balance_in_setup_data( current_data: FinancialAndTechDetails, - processed_prioritized: &[PayableAccount], + processed_outweighed: &[PayableAccount], ) -> FinancialAndTechDetails { let subtrahend_total: u128 = - Self::sum_as(processed_prioritized, |account| account.balance_wei); + Self::sum_as(processed_outweighed, |account| account.balance_wei); + eprintln!( + "cw masq balance {} to subtract {}", + current_data + .consuming_wallet_balances + .masq_tokens_wei + .as_u128(), + subtrahend_total + ); let consuming_wallet_balances = ConsumingWalletBalances { gas_currency_wei: current_data.consuming_wallet_balances.gas_currency_wei, masq_tokens_wei: U256::from( @@ -632,287 +692,68 @@ impl PaymentAdjusterReal { } } - fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, - ) -> Vec<(u128, PayableAccount)> { - unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) - .collect() - } - - fn format_brief_adjustment_summary( - original_account_balances_mapped: HashMap, - adjusted_accounts: &[PayableAccount], - ) -> String { - fn format_summary_for_included_accounts( - original_account_balances_mapped: &HashMap, - adjusted_accounts: &[PayableAccount], - ) -> String { - adjusted_accounts - .into_iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - }) - .map(|account| { - format!( - "{} {}\n{:^length$} {}", - account.wallet, - original_account_balances_mapped - .get(&account.wallet) - .expectv("initial balance"), - NO_CHARS, - account.balance_wei, - length = WALLET_ADDRESS_LENGTH - ) - }) - .join("\n") - } - fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { - let title = once(format!( - "\n{: = adjusted_accounts - .iter() - .map(|account| &account.wallet) - .collect(); - let excluded: Vec<(&Wallet, u128)> = original_account_balances_mapped.iter().fold( - vec![], - |mut acc, (wallet, original_balance)| { - if !adjusted_accounts_wallets.contains(&wallet) { - acc.push((wallet, *original_balance)); - } - acc - }, - ); - let adjusted_accounts_summary = format_summary_for_included_accounts( - &original_account_balances_mapped, - adjusted_accounts, - ); - let excluded_accounts_summary_opt = excluded - .is_empty() - .not() - .then(|| format_summary_for_excluded_accounts(&excluded)); - vec![ - Some(adjusted_accounts_summary), - excluded_accounts_summary_opt, - ] - .into_iter() - .flatten() - .join("\n") - } - - fn before_and_after_debug_msg( - original_account_balances_mapped: HashMap, - adjusted_accounts: &[PayableAccount], - ) -> String { - format!( - "\n\ - {: (u128, u128) { + let required_balance_total: u128 = + Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { + account.balance_wei + }); - fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_masq_balance: u128) { - warning!( - logger, - "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ - the MASQ token. Adjustment in their count or the amounts is required.", - payables_sum.separate_with_commas(), - cw_masq_balance.separate_with_commas() - ); - info!(logger, "{}", REFILL_RECOMMENDATION) + let criteria_total: u128 = + Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { + *criteria + }); + (required_balance_total, criteria_total) } - fn log_insufficient_transaction_fee_balance( - logger: &Logger, - required_transactions_count: usize, - this_stage_data: &FinancialAndTechDetails, - limiting_count: u16, - ) { - warning!( - logger, - "Gas amount {} wei cannot cover anticipated fees from sending {} \ - transactions. Maximum is {}. The payments need to be adjusted in \ - their count.", - this_stage_data - .consuming_wallet_balances - .masq_tokens_wei - .separate_with_commas(), - required_transactions_count, - limiting_count - ); - info!(logger, "{}", REFILL_RECOMMENDATION) + fn pick_mul_coeff_for_non_fractional_computation( + cw_masq_balance: u128, + criteria_sum: u128, + ) -> u128 { + const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; + let criteria_sum_digits_count = log_10(criteria_sum); + let cw_balance_digits_count = log_10(cw_masq_balance); + let smallest_mul_coeff_between = criteria_sum_digits_count + .checked_sub(cw_balance_digits_count) + .unwrap_or(0); + let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_COEFFICIENT; + 10_u128.pow(safe_mul_coeff as u32) } - fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { - criteria_and_accounts + fn finalize_decided_accounts( + decided_accounts: Vec, + resolution: DecidedPayableAccountResolution, + ) -> Vec { + decided_accounts .into_iter() - .map(|(_, account)| account) + .map(|decided_account| PayableAccount::from((decided_account, resolution))) .collect() } - fn run_recursively( - resolved_qualified_accounts: Vec, - unresolved_qualified_accounts: Vec, - collected_setup_data: FinancialAndTechDetails, - gas_limitation_opt: Option, - now: SystemTime, - logger: &Logger, - ) -> Vec { - diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); - diagnostics_collective( - "UNRESOLVED QUALIFIED ACCOUNTS:", - &unresolved_qualified_accounts, - ); - let accounts_with_zero_criteria = - Self::initialize_zero_criteria(unresolved_qualified_accounts); - let sorted_accounts_with_individual_criteria = - Self::apply_criteria(accounts_with_zero_criteria, now); - - let cw_masq_balance_wei = collected_setup_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(); - let adjustment_result: AdjustmentIterationResult = - match Self::give_job_to_adjustment_workers( - gas_limitation_opt, - sorted_accounts_with_individual_criteria, - cw_masq_balance_wei, - logger, - ) { - AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, - AdjustmentCompletion::Continue(iteration_result) => iteration_result, - }; - - Self::log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); - - let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { - adjustment_result.decided_accounts - } else { - let adjusted_setup_data = Self::adjust_cw_balance_in_setup_data( - collected_setup_data, - &adjustment_result.decided_accounts, - ); - //TODO what happens if we choose one that will get us into negative when subtracted - return Self::run_recursively( - adjustment_result.decided_accounts, - adjustment_result.remaining_accounts, - adjusted_setup_data, - None, - now, - logger, - ); - }; - - let adjusted_accounts_iter = adjusted_accounts.into_iter(); - let result: Vec = resolved_qualified_accounts + fn cut_back_by_gas_count_limit( + weights_and_accounts: Vec<(u128, PayableAccount)>, + limit: u16, + ) -> Vec<(u128, PayableAccount)> { + weights_and_accounts .into_iter() - .chain(adjusted_accounts_iter) - .collect(); - diagnostics_collective("FINAL ADJUSTED ACCOUNTS:", &result); - result + .take(limit as usize) + .collect() } - fn give_job_to_adjustment_workers( - gas_limitation_opt: Option, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - cw_masq_balance_wei: u128, - logger: &Logger, - ) -> AdjustmentCompletion { - match gas_limitation_opt { - Some(gas_limit) => { - let weighted_accounts_cut_by_gas = - Self::cut_back_by_gas_count_limit(accounts_with_individual_criteria, gas_limit); - match Self::check_need_of_masq_balances_adjustment( - logger, - Either::Right(&weighted_accounts_cut_by_gas), - cw_masq_balance_wei, - ) { - true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - weighted_accounts_cut_by_gas, - )), - false => AdjustmentCompletion::Finished(Self::rebuild_accounts( - weighted_accounts_cut_by_gas, - )), - } - } - None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - accounts_with_individual_criteria, - )), - } + fn sort_in_descendant_order_by_weights( + unsorted: impl Iterator, + ) -> Vec<(u128, PayableAccount)> { + unsorted + .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .collect() } - fn check_need_of_masq_balances_adjustment( - logger: &Logger, - qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, - consuming_wallet_balance_wei: u128, - ) -> bool { - let qualified_payables: Vec<&PayableAccount> = match qualified_payables { - Either::Left(accounts) => accounts.iter().collect(), - Either::Right(criteria_and_accounts) => criteria_and_accounts - .iter() - .map(|(_, account)| account) - .collect(), - }; - let required_masq_sum: u128 = - Self::sum_as(&qualified_payables, |account: &&PayableAccount| { - account.balance_wei - }); - - if required_masq_sum <= consuming_wallet_balance_wei { - false - } else { - Self::log_adjustment_by_masq_required( - logger, - required_masq_sum, - consuming_wallet_balance_wei, - ); - true - } + fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { + criteria_and_accounts + .into_iter() + .map(|(_, account)| account) + .collect() } } @@ -982,7 +823,7 @@ impl From<(ReversiblePayableAccount, DecidedPayableAccountResolution)> for Payab } #[derive(Debug, PartialEq, Eq)] -struct DisqualifiedPayableAccount { +pub struct DisqualifiedPayableAccount { wallet: Wallet, proposed_adjusted_balance: u128, original_balance: u128, @@ -1023,15 +864,18 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ log_10, log_2, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, - PaymentAdjusterReal, ReversiblePayableAccount, AGE_MULTIPLIER, AGE_POWER, - BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, REFILL_RECOMMENDATION, + PaymentAdjusterReal, PercentageAccountInsignificance, ReversiblePayableAccount, + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_EXPONENT, AGE_MULTIPLIER, + BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, BALANCE_TAIL_WEIGHT_EXPONENT, + BALANCE_TAIL_WEIGHT_MASK, BALANCE_TAIL_WEIGHT_MULTIPLIER, + COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::test_utils::make_payable_account; - use crate::accountant::{gwei_to_wei, ResponseSkeleton}; + use crate::accountant::{gwei_to_wei, wei_to_gwei, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; @@ -1054,36 +898,6 @@ mod tests { static ref FIVE_YEAR_LONG_DEBT_SEC: u64 = 5_u64 * 365 * 24 * 60 * 60; } - fn type_definite_conversion(gwei: u64) -> u128 { - gwei_to_wei(gwei) - } - - #[test] - fn constants_are_correct() { - assert_eq!( - REFILL_RECOMMENDATION, - "\ -In order to continue using services of other Nodes and avoid delinquency \ -bans you will need to put more funds into your consuming wallet." - ) - } - - #[test] - fn sum_payable_balances_works() { - let qualified_payables = vec![ - make_payable_account(456), - make_payable_account(1111), - make_payable_account(7800), - ]; - - let result = PaymentAdjusterReal::sum_payable_balances(&qualified_payables); - - let expected_result = type_definite_conversion(456) - + type_definite_conversion(1111) - + type_definite_conversion(7800); - assert_eq!(result, U256::from(expected_result)) - } - fn make_payable_setup_msg_coming_from_blockchain_bridge( q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, gas_price_opt: Option, @@ -1139,6 +953,23 @@ bans you will need to put more funds into your consuming wallet." consuming_wallet_gas_gwei: u64, } + #[test] + fn constants_are_correct() { + assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); + assert_eq!(COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, false); + assert_eq!(AGE_EXPONENT, 4); + assert_eq!(AGE_MULTIPLIER, 10); + assert_eq!(BALANCE_LOG_2_BASE_MULTIPLIER, 9); + assert_eq!(BALANCE_OUTER_MULTIPLIER, 2); + assert_eq!( + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + PercentageAccountInsignificance { + multiplier: 1, + divisor: 2, + } + ); + } + #[test] fn search_for_indispensable_adjustment_negative_answer() { init_test_logging(); @@ -1293,6 +1124,23 @@ bans you will need to put more funds into your consuming wallet." // assert_eq!(min, 111_000_000_000) // } + #[test] + fn balance_tail_weight_works() { + vec![ + (0xfff_u128 - 1, BALANCE_TAIL_WEIGHT_MASK - (0xfff - 1)), + (0xfff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), + (0x1fff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), + (0xfffff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), + ] + .into_iter() + .for_each(|(num, expected_result)| { + assert_eq!( + PaymentAdjusterReal::balance_tail_weight(num), + expected_result + ) + }) + } + #[test] fn log_10_works() { [ @@ -1343,7 +1191,7 @@ bans you will need to put more funds into your consuming wallet." .clone() .into_iter() .map(|cw_balance| { - PaymentAdjusterReal::find_decent_multiplication_coeff( + PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( cw_balance, final_criteria_sum, ) @@ -1368,7 +1216,7 @@ bans you will need to put more funds into your consuming wallet." let final_criteria = get_extreme_criteria(1); let final_criteria_total = final_criteria[0].0; let cw_balance_in_minor = 1; - let result = PaymentAdjusterReal::find_decent_multiplication_coeff( + let result = PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( cw_balance_in_minor, final_criteria_total, ); @@ -1382,7 +1230,7 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn recreate_accounts_with_proportioned_balances_accepts_exact_adjustment_by_half_but_not_by_more( + fn recreate_accounts_with_proportioned_balances_adheres_to_the_manifested_edge_of_insignificance( ) { let mut payable_account_1 = make_payable_account(1); payable_account_1.balance_wei = 1_000_000; @@ -1390,9 +1238,9 @@ bans you will need to put more funds into your consuming wallet." payable_account_2.balance_wei = 1_000_001; let proportional_fragment_of_cw_balance = 200_000; let multiplication_coeff = 10; - let expected_adjusted_balance = 500_000; + let proposed_adjusted_balance = 500_000; let criterion = - expected_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 + proposed_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 let weights_and_accounts = vec![ (criterion, payable_account_1.clone()), (criterion, payable_account_2.clone()), @@ -1405,17 +1253,22 @@ bans you will need to put more funds into your consuming wallet." multiplication_coeff, ); + // coefficients used reversely on purpose + let ok_former_balance = proposed_adjusted_balance + * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; let expected_decided_payable_account = ReversiblePayableAccount { adjusted_account: PayableAccount { balance_wei: 500_000, ..payable_account_1 }, - former_balance: 1_000_000, + former_balance: ok_former_balance, }; + let bad_former_balance = ok_former_balance + 1; let expected_disqualified_account = DisqualifiedPayableAccount { wallet: payable_account_2.wallet, proposed_adjusted_balance: 500_000, - original_balance: 1_000_001, + original_balance: bad_former_balance, }; assert_eq!(decided_accounts, vec![expected_decided_payable_account]); assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) @@ -1478,40 +1331,7 @@ bans you will need to put more funds into your consuming wallet." } #[test] - fn log_info_for_disqualified_accounts_can_log_multiple_accounts() { - init_test_logging(); - let wallet_1 = make_wallet("abc"); - let wallet_2 = make_wallet("efg"); - let balance_1 = 456_789_012_345; - let balance_2 = 222_444_777; - let disqualified_accounts = vec![ - DisqualifiedPayableAccount { - wallet: wallet_1.clone(), - original_balance: 500_000_000_000, - proposed_adjusted_balance: balance_1, - }, - DisqualifiedPayableAccount { - wallet: wallet_2.clone(), - original_balance: 300_000_000, - proposed_adjusted_balance: balance_2, - }, - ]; - let logger = Logger::new("log_info_for_disqualified_accounts_can_log_multiple_accounts"); - - PaymentAdjusterReal::log_info_for_disqualified_accounts(&logger, &disqualified_accounts); - - let make_expected_msg = |wallet: &Wallet, balance: u128| -> String { - format!("Recently qualified payable for wallet {wallet} is being ignored as the limited consuming \ - balance implied adjustment of its balance down to {} wei, which is not at least half of the debt", balance.separate_with_commas()) - }; - TestLogHandler::new().assert_logs_contain_in_order(vec![ - &make_expected_msg(&wallet_1, balance_1), - &make_expected_msg(&wallet_2, balance_2), - ]); - } - - #[test] - fn small_debt_with_extreme_age_is_paid_prioritized_but_not_with_more_money_than_required() { + fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { let now = SystemTime::now(); let collected_setup_data = FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { @@ -1569,10 +1389,10 @@ bans you will need to put more funds into your consuming wallet." assert_eq!( result, vec![ - account_2, //prioritized accounts take the first places + account_2, //outweighed accounts take the first places PayableAccount { wallet: make_wallet("blah"), - balance_wei: 1_499_949_995_483, + balance_wei: 1_499_949_995_299, last_paid_timestamp: last_paid_timestamp_1, pending_payable_opt: None, }, @@ -1580,6 +1400,49 @@ bans you will need to put more funds into your consuming wallet." ); } + #[test] + fn try_separating_outweighed_accounts_that_qualify_straight_cuts_back_the_outweighed_account_if_cw_balance_is_smaller( + ) { + let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; + let account_1_made_up_criteria = 18_000_000_000_000; + let account_1 = PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 1_000_000_000_000 + 333, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(200000)) + .unwrap(), + pending_payable_opt: None, + }; + let account_2_made_up_criteria = 5_000_000_000_000; + let account_2 = PayableAccount { + wallet: make_wallet("booga"), + balance_wei: 8_000_000_000_000_000, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(1000)) + .unwrap(), + pending_payable_opt: None, + }; + let required_balance_total = 1_000_000_000_000 + 333 + 8_000_000_000_000_000; + let criteria_total = account_1_made_up_criteria + account_2_made_up_criteria; + let accounts_with_individual_criteria = vec![ + (account_1_made_up_criteria, account_1.clone()), + (account_2_made_up_criteria, account_2.clone()), + ]; + + let (outweighed, _) = + PaymentAdjusterReal::try_separating_outweighed_accounts( + accounts_with_individual_criteria, + required_balance_total, + criteria_total, + consuming_wallet_balance + ).right() + .unwrap(); + + let mut expected_account = account_1; + expected_account.balance_wei = 1_000_000_000_000 - 1; + assert_eq!(outweighed, vec![expected_account]) + } + #[test] fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { init_test_logging(); @@ -1651,11 +1514,11 @@ bans you will need to put more funds into your consuming wallet." | Adjusted | |0x0000000000000000000000000000000000646566 6666666666000000000 -| 6627452261726961117 +| 6627452261727177476 |0x0000000000000000000000000000000000616263 4444444444444444444 -| 4418301511592193084 +| 4418301511592311819 |0x000000000000000000000000000000000067686b 60000000000000000 -| 55357206066348214" +| 55357206066732915" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1803,13 +1666,15 @@ bans you will need to put more funds into your consuming wallet." .find(|account| account.wallet == wallet_3) .unwrap() .balance_wei; + let minimum_allowed = ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * balance_3 * 10 + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; assert!( - account_3_adjusted_balance < (balance_3 / 2), + account_3_adjusted_balance * 10 < minimum_allowed, "balance for account 3 after adjustment from the first iteration is {} but we need it \ smaller than {} to exercise what happens if the proposed balance is smaller than half \ the original one", account_3_adjusted_balance.separate_with_commas(), - (balance_3 / 2).separate_with_commas() + minimum_allowed.separate_with_commas() ); let expected_accounts = emulation_of_the_actual_adjustment_algorithm( account_1, @@ -1834,30 +1699,36 @@ bans you will need to put more funds into your consuming wallet." ); TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ - consuming balance implied adjustment of its balance down to 56,551,122 wei, which is not at \ + consuming balance implied adjustment of its balance down to 56,554,286 wei, which is not at \ least half of the debt")); } - #[test] - fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { - // accounts in this test are evenly significant and so one cannot win over another, - // yet there is not enough balance to pay the minimum required which is a half of each - // thus we conclude none can be paid - - init_test_logging(); - let test_name = "adjust_payments_when_not_enough_masq_to_pay_at_least_halves_of_accounts"; + fn test_competitive_accounts( + test_name_with_unique_description: &str, + wallet_1: &Wallet, + wallet_2: &Wallet, + balance_account_1: u128, + balance_account_2: u128, + age_secs_account_1: u64, + age_secs_account_2: u64, + ) -> Vec { let now = SystemTime::now(); let account_1 = PayableAccount { - wallet: make_wallet("abcd"), - balance_wei: 100_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), + wallet: wallet_1.clone(), + balance_wei: balance_account_1, + last_paid_timestamp: now + .checked_sub(Duration::from_secs(age_secs_account_1)) + .unwrap(), pending_payable_opt: None, }; - let mut account_2 = PayableAccount { - wallet: make_wallet("cdef"), - ..account_1.clone() + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: balance_account_2, + last_paid_timestamp: now + .checked_sub(Duration::from_secs(age_secs_account_2)) + .unwrap(), + pending_payable_opt: None, }; - account_2.balance_wei = 100_000_000_000_000; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let subject = PaymentAdjusterReal::new(); let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 - 1); @@ -1880,50 +1751,78 @@ bans you will need to put more funds into your consuming wallet." adjustment: Adjustment::MasqToken, }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + subject + .adjust_payments( + adjustment_setup, + now, + &Logger::new(test_name_with_unique_description), + ) + .accounts + } + + #[test] + fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { + // accounts in this test are evenly significant and so one cannot compete another, + // yet there is not enough balance to pay the minimum required which is a half of each + // thus we conclude none can be paid + fn merge_test_name_and_study_description(test_name: &str, description: &str) -> String { + format!("{}/{}", test_name, description) + } + let test_name = "adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account"; + let wallet_1 = make_wallet("abcd"); + let wallet_2 = make_wallet("cdef"); + let balance_account_1 = 100_000_000_000_000; + let balance_account_2 = 100_000_000_000_000; + let age_account_1 = 12000; + let age_account_2 = 12000; + let first_scenario_name = merge_test_name_and_study_description(test_name, "when_equal"); + + // scenario A + let result = test_competitive_accounts( + &first_scenario_name, + &wallet_1, + &wallet_2, + balance_account_1, + balance_account_2, + age_account_1, + age_account_2, + ); - eprintln!("accounts adjusted: {:?}", result.accounts); - todo!("finish me") - // let expected_accounts_first_iteration = emulation_of_the_actual_adjustment_algorithm( - // account_1.clone(), - // account_2.clone(), - // Some(account_3), - // consuming_wallet_masq_balance_wei.as_u128(), - // now, - // ); - // let account_3_adjusted_balance = expected_accounts_first_iteration - // .iter() - // .find(|account| account.wallet == wallet_3) - // .unwrap() - // .balance_wei; - // assert!( - // account_3_adjusted_balance < (balance_3 / 2), - // "balance for account 3 after \ - // adjustment from the first iteration is {} but we need it smaller than {}", - // account_3_adjusted_balance.separate_with_commas(), - // (balance_3 / 2).separate_with_commas() - // ); - // let adjusted_cw_balance_after_prioritizing_one_account = - // consuming_wallet_masq_balance_wei.as_u128() - balance_3; - // let expected_accounts = emulation_of_the_actual_adjustment_algorithm( - // account_1, - // account_2, - // None, - // adjusted_cw_balance_after_prioritizing_one_account, - // now, - // ); - // assert_eq!(result.accounts, expected_accounts); - // assert_eq!( - // result.response_skeleton_opt, - // Some(ResponseSkeleton { - // client_id: 111, - // context_id: 234 - // }) - // ); - // TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ - // payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ - // consuming balance implied adjustment of its balance down to 22,572,576 wei, which is not at \ - // least half of the debt")); + assert_eq!(result, vec![]); + // scenario B + const TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED: u128 = 500; + let second_scenario_name = + merge_test_name_and_study_description(test_name, "first_heavier_by_balance"); + + let result = test_competitive_accounts( + &second_scenario_name, + &wallet_1, + &wallet_2, + balance_account_1 + TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED, + balance_account_2 - TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED, + age_account_1, + age_account_2, + ); + + assert_eq!(result[0].wallet, wallet_1); + assert_eq!(result.len(), 1); + // scenario C + const TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED: u64 = 30; + let third_scenario_name = + merge_test_name_and_study_description(test_name, "second_heavier_by_age"); + + let result = test_competitive_accounts( + &third_scenario_name, + &wallet_1, + &wallet_2, + balance_account_1, + balance_account_2, + age_account_1 - TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED, + age_account_2 + TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED, + ); + + assert_eq!(result[0].wallet, wallet_2); + assert_eq!(result.len(), 1) } //TODO do I really want to delete this test? Why? @@ -2129,7 +2028,7 @@ bans you will need to put more funds into your consuming wallet." | Adjusted | |0x000000000000000000000000000000000067686b 333000000000000 -| 299999993782194 +| 299999993982547 | |Ignored minor payables Original | @@ -2162,15 +2061,19 @@ bans you will need to put more funds into your consuming wallet." .iter() .map(|account| { let elapsed = secs_elapsed(account.last_paid_timestamp, now); - elapsed.pow(AGE_POWER) / (((elapsed as f64).sqrt().ceil()) as u128) * AGE_MULTIPLIER + elapsed.pow(AGE_EXPONENT) / (((elapsed as f64).sqrt().ceil()) as u128) + * AGE_MULTIPLIER }) .collect(); let balance_criteria = accounts .iter() .map(|account| { - let significance = - log_2(account.balance_wei * BALANCE_LOG_2_BASE_MULTIPLIER) as u128; - account.balance_wei * (significance * BALANCE_OUTER_MULTIPLIER) + let balance = account.balance_wei; + let significance = log_2(balance * BALANCE_LOG_2_BASE_MULTIPLIER) as u128; + let tail_weight = PaymentAdjusterReal::balance_tail_weight(balance); + balance * (significance * BALANCE_OUTER_MULTIPLIER) + + (tail_weight.pow(BALANCE_TAIL_WEIGHT_EXPONENT) + * BALANCE_TAIL_WEIGHT_MULTIPLIER) } as u128) .collect(); @@ -2184,10 +2087,11 @@ bans you will need to put more funds into your consuming wallet." }, ); let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = PaymentAdjusterReal::find_decent_multiplication_coeff( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); + let multiplication_coeff = + PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei .checked_mul(multiplication_coeff) .unwrap() From 37fe54c2f4d0660292a565980eebd2929f97dab4 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 16 Jul 2023 23:46:46 +0200 Subject: [PATCH 041/250] GH-711: interim commit --- node/src/accountant/payment_adjuster/mod.rs | 188 ++++++++++++++++---- 1 file changed, 158 insertions(+), 30 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e920504ba..253e85a31 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -27,6 +27,7 @@ use std::any::Any; use std::collections::HashMap; use std::iter::{once, successors}; use std::time::{Duration, SystemTime}; +use log::logger; use thousands::Separable; use web3::types::U256; @@ -271,7 +272,7 @@ impl PaymentAdjusterReal { fn run_recursively( resolved_qualified_accounts: Vec, unresolved_qualified_accounts: Vec, - collected_setup_data: FinancialAndTechDetails, + collected_setup_data: FinancialAndTechDetails, //TODO this maybe should be just cw_balance gas_limitation_opt: Option, now: SystemTime, logger: &Logger, @@ -306,7 +307,7 @@ impl PaymentAdjusterReal { let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts } else { - let adjusted_setup_data = Self::adjust_cw_balance_in_setup_data( + let adjusted_setup_data = Self::adjust_next_round_cw_balance_in_setup_data( collected_setup_data, &adjustment_result.decided_accounts, ); @@ -441,6 +442,7 @@ impl PaymentAdjusterReal { true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( cw_masq_balance_wei, weighted_accounts_cut_by_gas, + logger )), false => AdjustmentCompletion::Finished(Self::rebuild_accounts( weighted_accounts_cut_by_gas, @@ -450,6 +452,7 @@ impl PaymentAdjusterReal { None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( cw_masq_balance_wei, accounts_with_individual_criteria, + logger )), } } @@ -457,15 +460,17 @@ impl PaymentAdjusterReal { fn handle_masq_token_adjustment( cw_masq_balance: u128, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + logger: &Logger ) -> AdjustmentIterationResult { - let (required_balance_total, criteria_total) = - Self::compute_balance_and_criteria_totals(&accounts_with_individual_criteria); + let required_balance_total= Self::balance_total(&accounts_with_individual_criteria); + let criteria_total = Self::criteria_total(&accounts_with_individual_criteria); match Self::try_separating_outweighed_accounts( accounts_with_individual_criteria, required_balance_total, criteria_total, cw_masq_balance, + logger ) { Either::Left(accounts_with_individual_criteria) => { Self::perform_adjustment_and_determine_adjustment_iteration_result( @@ -599,7 +604,8 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, required_balance_total: u128, criteria_total: u128, - cw_masq_balance: u128 + cw_masq_balance: u128, + logger: &Logger ) -> Either, (Vec, Vec)> { match Self::check_for_outweighed_accounts( &accounts_with_individual_criteria, @@ -608,13 +614,16 @@ impl PaymentAdjusterReal { ) { None => Either::Left(accounts_with_individual_criteria), Some(wallets_of_outweighed) => Either::Right({ - let (outweighed, remaining) = + eprintln!("wallets: {:?}", wallets_of_outweighed); + debug!(logger, "Found outweighed accounts that will have to be readjusted ({:?}).", wallets_of_outweighed); + let (outweighed_with_criteria, remaining_with_criteria): (Vec<(u128, PayableAccount)>, Vec<(u128, PayableAccount)>) = accounts_with_individual_criteria .into_iter() - // TODO use the criteria later, if you have more than one outweighed accounts but you don't have enough balance for them... - .map(|(_, account)| account) - .partition(|account| wallets_of_outweighed.contains(&account.wallet)); - (outweighed, remaining) + .map(|(_,account)|account) + .partition(|(_, account)| wallets_of_outweighed.contains(&account.wallet)); + let remaining= Self::drop_criteria_and_leave_accounts(remaining_with_criteria); + let outweighed = Self::adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary(outweighed_with_criteria,cw_masq_balance,logger); + (outweighed,remaining) }) } } @@ -629,7 +638,6 @@ impl PaymentAdjusterReal { let accounts_to_be_outweighed = accounts_with_individual_criteria .iter() .filter(|(criterion, account)| { - //account.balance_wei is still the original balance let balance_ratio = required_balance_total_for_safe_math / (account.balance_wei * 10_000); let criterion_ratio = criteria_total_for_safe_math / (criterion * 10_000); @@ -662,7 +670,30 @@ impl PaymentAdjusterReal { } } - fn adjust_cw_balance_in_setup_data( + //TODO we probably want to drop the criteria before here where we've got no use for them + fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary(mut outweighed_with_criteria: Vec<(u128, PayableAccount)>, cw_masq_balance: u128, logger: &Logger) ->Vec{ + if outweighed_with_criteria.len() == 1 { + let (_, account) = &outweighed_with_criteria[0]; + if account.balance_wei > cw_masq_balance { + let (_, account) = outweighed_with_criteria.remove(0); + vec![PayableAccount {balance_wei: cw_masq_balance, ..account}] + } else {Self::drop_criteria_and_leave_accounts(outweighed_with_criteria)} + } else { + Self::run_recursively(vec![], outweighed, logger) + // + // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { + // todo!() + // } else { + // todo!("{:?}", adjustment_result) + // } + } + } + + fn drop_criteria_and_leave_accounts(accounts_with_individual_criteria: Vec<(u128, PayableAccount)>)->Vec{ + accounts_with_individual_criteria.into_iter().map(|(_,account)|account).collect() + } + + fn adjust_next_round_cw_balance_in_setup_data( current_data: FinancialAndTechDetails, processed_outweighed: &[PayableAccount], ) -> FinancialAndTechDetails { @@ -692,19 +723,16 @@ impl PaymentAdjusterReal { } } - fn compute_balance_and_criteria_totals( - accounts_with_individual_criteria: &[(u128, PayableAccount)], - ) -> (u128, u128) { - let required_balance_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { - account.balance_wei - }); + fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)])->u128{ + Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { + account.balance_wei + }) + } - let criteria_total: u128 = - Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }); - (required_balance_total, criteria_total) + fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)])->u128{ + Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { + *criteria + }) } fn pick_mul_coeff_for_non_fractional_computation( @@ -1401,13 +1429,14 @@ mod tests { } #[test] - fn try_separating_outweighed_accounts_that_qualify_straight_cuts_back_the_outweighed_account_if_cw_balance_is_smaller( + fn try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less( ) { + let test_name = "try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less"; let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; let account_1_made_up_criteria = 18_000_000_000_000; let account_1 = PayableAccount { wallet: make_wallet("blah"), - balance_wei: 1_000_000_000_000 + 333, + balance_wei: 1_000_000_000_000, last_paid_timestamp: SystemTime::now() .checked_sub(Duration::from_secs(200000)) .unwrap(), @@ -1415,7 +1444,7 @@ mod tests { }; let account_2_made_up_criteria = 5_000_000_000_000; let account_2 = PayableAccount { - wallet: make_wallet("booga"), + wallet:make_wallet("booga"), balance_wei: 8_000_000_000_000_000, last_paid_timestamp: SystemTime::now() .checked_sub(Duration::from_secs(1000)) @@ -1429,18 +1458,88 @@ mod tests { (account_2_made_up_criteria, account_2.clone()), ]; - let (outweighed, _) = + let (outweighed, remaining) = PaymentAdjusterReal::try_separating_outweighed_accounts( accounts_with_individual_criteria, required_balance_total, criteria_total, - consuming_wallet_balance + consuming_wallet_balance, + &Logger::new(test_name) ).right() .unwrap(); let mut expected_account = account_1; expected_account.balance_wei = 1_000_000_000_000 - 1; - assert_eq!(outweighed, vec![expected_account]) + assert_eq!(outweighed, vec![expected_account]); + assert_eq!(remaining, vec![account_2]) + } + + #[test] + fn try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum( + ) { + let test_name = "try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum"; + let now = SystemTime::now(); + let consuming_wallet_balance = 2_000_000_000_000; + let balance_1 = 1_500_000_000_000; + let account_1 = PayableAccount { + wallet: make_wallet("blah"), + balance_wei: balance_1, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(200000)) + .unwrap(), + pending_payable_opt: None, + }; + let balance_2 = 3_000_000_000_000; + let account_2 = PayableAccount { + wallet: make_wallet("booga"), + balance_wei: balance_2, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(150000)) + .unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("giraffe"), + balance_wei: 8_000_000_000_000_000, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(1000)) + .unwrap(), + pending_payable_opt: None, + }; + let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![account_1.clone(),account_2.clone(),account_3.clone()]); + let accounts_with_individual_criteria = PaymentAdjusterReal::apply_criteria(accounts_with_zero_criteria, now); + let required_balance_total = PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); + let criteria_total = PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); + + let (outweighed, remaining) = + PaymentAdjusterReal::try_separating_outweighed_accounts( + accounts_with_individual_criteria.clone(), + required_balance_total, + criteria_total, + consuming_wallet_balance, + &Logger::new(test_name) + ).right() + .unwrap(); + + // the algorithm first isolates the two first outweighed accounts and because + // they are more than one it will apply the standard adjustment strategy according to its criteria, + // with one difference though, now the third account will stay out + let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria.iter().take(2).map(|(criteria_sum, _)|*criteria_sum).collect(); + let expected_balances = compute_expected_balanced_portions_from_criteria(individual_criteria_for_just_two_accounts, consuming_wallet_balance); + //making sure the proposed balances are non zero + assert!(expected_balances[0] > (balance_1 / 4)); + assert!(expected_balances[1] > (balance_1 / 4)); + let check_sum = expected_balances.iter().sum(); + assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); + let expected_outweighed_accounts = { + let mut account_1 = account_1; + account_1.balance_wei = expected_balances[0]; + let mut account_2 = account_2; + account_2.balance_wei =expected_balances[1]; + vec![account_1,account_2] + }; + assert_eq!(outweighed, expected_outweighed_accounts); + assert_eq!(remaining, vec![account_3]) } #[test] @@ -2139,4 +2238,33 @@ mod tests { .map(|(_, account)| account) .collect() } + + fn compute_expected_balanced_portions_from_criteria(final_criteria: Vec, consuming_wallet_masq_balance_wei: u128)->Vec{ + let final_criteria_sum = final_criteria.iter().sum::(); + let multiplication_coeff = + PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); + let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei + .checked_mul(multiplication_coeff) + .unwrap() + .checked_div(final_criteria_sum) + .unwrap(); + let new_balanced_portions = final_criteria + .iter() + .map(|criterion| { + in_ratio_fragment_of_available_balance * criterion / multiplication_coeff + }) + .collect::>(); + let new_total_amount_to_pay = new_balanced_portions.iter().sum::(); + assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); + assert!( + new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, + "new total amount to pay: {}, consuming wallet masq balance: {}", + new_total_amount_to_pay, + consuming_wallet_masq_balance_wei + ); + new_balanced_portions + } } From f79adf70f3a91a404387297ae6f72103e0b1ecda Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 17 Jul 2023 11:10:25 +0200 Subject: [PATCH 042/250] GH-711: interim commit --- node/src/accountant/payment_adjuster/mod.rs | 37 +++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 253e85a31..9febb5b78 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -72,7 +72,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(None) => (), Ok(Some(limited_count_from_gas)) => { - return Ok(Some(Adjustment::TransactionFeeFirstLaterMaybeMasq { + return Ok(Some(Adjustment::TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas, })) } @@ -105,7 +105,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let qualified_payables: Vec = msg.qualified_payables; let gas_limitation_opt = match setup.adjustment { - Adjustment::TransactionFeeFirstLaterMaybeMasq { + Adjustment::TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas, } => Some(limited_count_from_gas), Adjustment::MasqToken => None, @@ -118,7 +118,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = Self::run_recursively( + let adjusted_accounts = Self::run_adjustment_in_recursion( vec![], qualified_payables, current_stage_data, @@ -220,7 +220,7 @@ impl PaymentAdjusterReal { let available_balance_in_minor = tech_info.consuming_wallet_balances.gas_currency_wei; let limiting_max_possible_count = (available_balance_in_minor / tfrpt_in_minor).as_u128(); if limiting_max_possible_count == 0 { - Err(AnalysisError::TransactionFeeBalanceBelowOneTransaction { + Err(AnalysisError::BalanceBelowSingleTxFee { one_transaction_requirement: transaction_fee_required_per_transaction_in_major as u64, cw_balance: wei_to_gwei(available_balance_in_minor), @@ -269,7 +269,7 @@ impl PaymentAdjusterReal { } } - fn run_recursively( + fn run_adjustment_in_recursion( resolved_qualified_accounts: Vec, unresolved_qualified_accounts: Vec, collected_setup_data: FinancialAndTechDetails, //TODO this maybe should be just cw_balance @@ -312,7 +312,7 @@ impl PaymentAdjusterReal { &adjustment_result.decided_accounts, ); //TODO what happens if we choose one that will get us into negative when subtracted - return Self::run_recursively( + return Self::run_adjustment_in_recursion( adjustment_result.decided_accounts, adjustment_result.remaining_accounts, adjusted_setup_data, @@ -621,6 +621,7 @@ impl PaymentAdjusterReal { .into_iter() .map(|(_,account)|account) .partition(|(_, account)| wallets_of_outweighed.contains(&account.wallet)); + // TODO you probably will want to supply the accounts naked to adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary let remaining= Self::drop_criteria_and_leave_accounts(remaining_with_criteria); let outweighed = Self::adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary(outweighed_with_criteria,cw_masq_balance,logger); (outweighed,remaining) @@ -679,7 +680,7 @@ impl PaymentAdjusterReal { vec![PayableAccount {balance_wei: cw_masq_balance, ..account}] } else {Self::drop_criteria_and_leave_accounts(outweighed_with_criteria)} } else { - Self::run_recursively(vec![], outweighed, logger) + Self::run_adjustment_in_recursion(vec![], outweighed, logger) // // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { // todo!() @@ -870,7 +871,7 @@ impl DisqualifiedPayableAccount { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - TransactionFeeFirstLaterMaybeMasq { limited_count_from_gas: u16 }, + TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas: u16 }, } #[derive(Clone, Copy)] @@ -881,12 +882,20 @@ struct GasLimitationContext { #[derive(Debug, PartialEq, Eq)] pub enum AnalysisError { - TransactionFeeBalanceBelowOneTransaction { + BalanceBelowSingleTxFee { one_transaction_requirement: u64, cw_balance: u64, }, } +// struct AdjustmentCircumstances { +// now: SystemTime, +// cw_masq_balance: u128, +// gas_limitation_opt: Option, +// // TODO information about accounts +// // from Neighborhood should later go here +// } + #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; @@ -1085,7 +1094,7 @@ mod tests { let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( result, - Ok(Some(Adjustment::TransactionFeeFirstLaterMaybeMasq { + Ok(Some(Adjustment::TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas: expected_limiting_count })) ); @@ -1118,7 +1127,7 @@ mod tests { assert_eq!( result, - Err(AnalysisError::TransactionFeeBalanceBelowOneTransaction { + Err(AnalysisError::BalanceBelowSingleTxFee { one_transaction_requirement: 55_000 * 100, cw_balance: 54_000 * 100 }) @@ -1388,7 +1397,7 @@ mod tests { let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; - let result = PaymentAdjusterReal::run_recursively( + let result = PaymentAdjusterReal::run_adjustment_in_recursion( vec![], qualified_payables.clone(), collected_setup_data, @@ -1665,7 +1674,7 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::TransactionFeeFirstLaterMaybeMasq { + adjustment: Adjustment::TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas: 2, }, }; @@ -2100,7 +2109,7 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::TransactionFeeFirstLaterMaybeMasq { + adjustment: Adjustment::TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas: 2, }, }; From 12d58f2242902727b6df3d2a6678ccfc26016933 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 19 Jul 2023 00:15:29 +0200 Subject: [PATCH 043/250] GH-711: hardening against dangerious too big number computations; trying to figure out how to solve the outweighed accounts incomplete alg --- node/src/accountant/payment_adjuster/inner.rs | 111 +++ .../payment_adjuster/log_functions.rs | 9 +- node/src/accountant/payment_adjuster/mod.rs | 766 ++++++++++-------- node/src/accountant/scanners/mod.rs | 13 +- node/src/accountant/test_utils.rs | 2 +- 5 files changed, 561 insertions(+), 340 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/inner.rs diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs new file mode 100644 index 000000000..58a12e49c --- /dev/null +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use std::time::SystemTime; + +pub trait PaymentAdjusterInner { + fn now(&self) -> SystemTime { + PaymentAdjusterInnerNull::panicking_operation("now()") + } + fn gas_limitation_opt(&self) -> Option { + PaymentAdjusterInnerNull::panicking_operation("gas_limitation_opt()") + } + fn cw_masq_balance(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("cw_masq_balance()") + } + + fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation("lower_remaining_cw_balance()") + } +} + +pub struct PaymentAdjusterInnerReal { + now: SystemTime, + gas_limitation_opt: Option, + cw_masq_balance: u128, +} + +impl PaymentAdjusterInnerReal { + pub fn new(now: SystemTime, gas_limitation_opt: Option, cw_masq_balance: u128) -> Self { + Self { + now, + gas_limitation_opt, + cw_masq_balance, + } + } +} + +impl PaymentAdjusterInner for PaymentAdjusterInnerReal { + fn now(&self) -> SystemTime { + self.now + } + fn gas_limitation_opt(&self) -> Option { + self.gas_limitation_opt + } + fn cw_masq_balance(&self) -> u128 { + self.cw_masq_balance + } + + fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { + self.cw_masq_balance -= subtrahend + } +} + +pub struct PaymentAdjusterInnerNull {} + +impl PaymentAdjusterInnerNull { + fn panicking_operation(operation: &str) -> ! { + panic!( + "Called the null implementation of the {} method in PaymentAdjusterInner", + operation + ) + } +} + +impl PaymentAdjusterInner for PaymentAdjusterInnerNull {} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::inner::{ + PaymentAdjusterInner, PaymentAdjusterInnerNull, + }; + + #[test] + #[should_panic( + expected = "Called the null implementation of the now() method in PaymentAdjusterInner" + )] + fn inner_null_calling_now() { + let subject = PaymentAdjusterInnerNull {}; + + let _ = subject.now(); + } + + #[test] + #[should_panic( + expected = "Called the null implementation of the cw_masq_balance() method in PaymentAdjusterInner" + )] + fn inner_null_calling_cw_masq_balance() { + let subject = PaymentAdjusterInnerNull {}; + + let _ = subject.cw_masq_balance(); + } + + #[test] + #[should_panic( + expected = "Called the null implementation of the gas_limitation_opt() method in PaymentAdjusterInner" + )] + fn inner_null_calling_gas_limitation_opt() { + let subject = PaymentAdjusterInnerNull {}; + + let _ = subject.gas_limitation_opt(); + } + + #[test] + #[should_panic( + expected = "Called the null implementation of the lower_remaining_cw_balance() method in PaymentAdjusterInner" + )] + fn inner_null_calling_lower_remaining_cw_balance() { + let mut subject = PaymentAdjusterInnerNull {}; + + let _ = subject.lower_remaining_cw_balance(123); + } +} diff --git a/node/src/accountant/payment_adjuster/log_functions.rs b/node/src/accountant/payment_adjuster/log_functions.rs index 5b0aa80f8..d8b17cfdf 100644 --- a/node/src/accountant/payment_adjuster/log_functions.rs +++ b/node/src/accountant/payment_adjuster/log_functions.rs @@ -119,11 +119,12 @@ pub fn log_info_for_disqualified_accounts( disqualified_accounts.iter().for_each(|account| { info!( logger, - "Recently qualified payable for wallet {} is being ignored as the limited consuming \ - balance implied adjustment of its balance down to {} wei, which is not at least half \ - of the debt", + "Consuming wallet low in MASQ balance. Recently qualified \ + payable for wallet {} will not be paid as the consuming wallet handles to provide only {} wei \ + which is not at least more than a half of the original debt {}", account.wallet, - account.proposed_adjusted_balance.separate_with_commas() + account.proposed_adjusted_balance.separate_with_commas(), + account.original_balance.separate_with_commas() ) }); } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 9febb5b78..6a412bd5a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. mod diagnostics; +mod inner; mod log_functions; use crate::accountant::database_access_objects::payable_dao::PayableAccount; @@ -8,6 +9,9 @@ use crate::accountant::payment_adjuster::diagnostics::{ diagnostics, diagnostics_collective, FinalizationAndDiagnostics, AGE_SINGLETON, BALANCE_SINGLETON, }; +use crate::accountant::payment_adjuster::inner::{ + PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, +}; use crate::accountant::payment_adjuster::log_functions::{ before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_accounts, log_insufficient_transaction_fee_balance, @@ -21,13 +25,13 @@ use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; +use log::logger; use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; use std::collections::HashMap; use std::iter::{once, successors}; use std::time::{Duration, SystemTime}; -use log::logger; use thousands::Separable; use web3::types::U256; @@ -39,7 +43,7 @@ pub trait PaymentAdjuster { ) -> Result, AnalysisError>; fn adjust_payments( - &self, + &mut self, setup: AwaitedAdjustment, now: SystemTime, logger: &Logger, @@ -48,7 +52,10 @@ pub trait PaymentAdjuster { declare_as_any!(); } -pub struct PaymentAdjusterReal {} +pub struct PaymentAdjusterReal { + inner: Box, + logger: Logger, +} impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( @@ -93,42 +100,32 @@ impl PaymentAdjuster for PaymentAdjusterReal { } fn adjust_payments( - &self, + &mut self, setup: AwaitedAdjustment, now: SystemTime, - logger: &Logger, + logger: &Logger, //TODO fix this later ) -> OutcomingPaymentsInstructions { let msg = setup.original_setup_msg; + let qualified_payables: Vec = msg.qualified_payables; + let response_skeleton_opt = msg.response_skeleton_opt; let current_stage_data = match msg.this_stage_data_opt.expectv("complete setup data") { StageData::FinancialAndTechDetails(details) => details, }; - let qualified_payables: Vec = msg.qualified_payables; + let required_adjustment = setup.adjustment; - let gas_limitation_opt = match setup.adjustment { - Adjustment::TransactionFeeDefinitelyOtherMaybe { - limited_count_from_gas, - } => Some(limited_count_from_gas), - Adjustment::MasqToken => None, - }; + self.set_up_new_inner(current_stage_data, required_adjustment, now); - let debug_info_opt = logger.debug_enabled().then(|| { + let debug_info_opt = self.logger.debug_enabled().then(|| { qualified_payables .iter() .map(|account| (account.wallet.clone(), account.balance_wei)) .collect::>() }); - let adjusted_accounts = Self::run_adjustment_in_recursion( - vec![], - qualified_payables, - current_stage_data, - gas_limitation_opt, - now, - logger, - ); + let adjusted_accounts = self.run_adjustment_in_recursion(vec![], qualified_payables); debug!( - logger, + self.logger, "{}", before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); @@ -142,12 +139,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { implement_as_any!(); } -impl Default for PaymentAdjusterReal { - fn default() -> Self { - Self::new() - } -} - const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = false; @@ -166,6 +157,7 @@ const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = multiplier: 1, divisor: 2, }; +const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; // sets the minimal percentage of the original balance that must be // proposed after the adjustment or the account will be eliminated for insignificance @@ -177,13 +169,37 @@ struct PercentageAccountInsignificance { divisor: u128, } -//TODO think about splitting this file into a folder of files - type CriterionFormula<'a> = Box (u128, PayableAccount) + 'a>; +impl Default for PaymentAdjusterReal { + fn default() -> Self { + Self::new() + } +} + impl PaymentAdjusterReal { pub fn new() -> Self { - Self {} + Self { + inner: Box::new(PaymentAdjusterInnerNull {}), + logger: Logger::new("PaymentAdjuster"), + } + } + + fn set_up_new_inner( + &mut self, + setup: FinancialAndTechDetails, + required_adjustment: Adjustment, + now: SystemTime, + ) { + let gas_limitation_opt = match required_adjustment { + Adjustment::TransactionFeeDefinitelyOtherMaybe { + limited_count_from_gas, + } => Some(limited_count_from_gas), + Adjustment::MasqToken => None, + }; + let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_wei.as_u128(); + let inner = PaymentAdjusterInnerReal::new(now, gas_limitation_opt, cw_masq_balance); + self.inner = Box::new(inner); } fn sum_as(collection: &[T], arranger: F) -> N @@ -194,18 +210,6 @@ impl PaymentAdjusterReal { collection.iter().map(arranger).sum::().into() } - // fn find_smallest_debt(qualified_accounts: &[&PayableAccount]) -> u128 { - // qualified_accounts - // .iter() - // .sorted_by(|account_a, account_b| { - // Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - // }) - // .last() - // .expect("at least one qualified payable must have been sent here") - // .balance_wei - // .into() - // } - fn determine_transactions_count_limit_by_gas( tech_info: &FinancialAndTechDetails, required_transactions_count: usize, @@ -270,12 +274,9 @@ impl PaymentAdjusterReal { } fn run_adjustment_in_recursion( + &mut self, resolved_qualified_accounts: Vec, unresolved_qualified_accounts: Vec, - collected_setup_data: FinancialAndTechDetails, //TODO this maybe should be just cw_balance - gas_limitation_opt: Option, - now: SystemTime, - logger: &Logger, ) -> Vec { diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); diagnostics_collective( @@ -285,40 +286,23 @@ impl PaymentAdjusterReal { let accounts_with_zero_criteria = Self::initialize_zero_criteria(unresolved_qualified_accounts); let sorted_accounts_with_individual_criteria = - Self::apply_criteria(accounts_with_zero_criteria, now); + self.apply_criteria(accounts_with_zero_criteria); - let cw_masq_balance_wei = collected_setup_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(); let adjustment_result: AdjustmentIterationResult = - match Self::give_job_to_adjustment_workers( - gas_limitation_opt, - sorted_accounts_with_individual_criteria, - cw_masq_balance_wei, - logger, - ) { + match self.give_job_to_adjustment_workers(sorted_accounts_with_individual_criteria) { AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, AdjustmentCompletion::Continue(iteration_result) => iteration_result, }; - log_info_for_disqualified_accounts(logger, &adjustment_result.disqualified_accounts); + log_info_for_disqualified_accounts(&self.logger, &adjustment_result.disqualified_accounts); let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts } else { - let adjusted_setup_data = Self::adjust_next_round_cw_balance_in_setup_data( - collected_setup_data, - &adjustment_result.decided_accounts, - ); - //TODO what happens if we choose one that will get us into negative when subtracted - return Self::run_adjustment_in_recursion( + self.adjust_next_round_cw_balance_down(&adjustment_result.decided_accounts); + return self.run_adjustment_in_recursion( adjustment_result.decided_accounts, adjustment_result.remaining_accounts, - adjusted_setup_data, - None, - now, - logger, ); }; @@ -346,8 +330,8 @@ impl PaymentAdjusterReal { } fn apply_criteria( + &self, accounts_with_zero_criteria: impl Iterator, - now: SystemTime, ) -> Vec<(u128, PayableAccount)> { //define individual criteria as closures to be used in a map() @@ -355,13 +339,16 @@ impl PaymentAdjusterReal { let age_criteria_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { let formula = |last_paid_timestamp: SystemTime| { - let elapsed_sec: u64 = now + let elapsed_sec: u64 = self + .inner + .now() .duration_since(last_paid_timestamp) .expect("time traveller") .as_secs(); let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; (elapsed_sec as u128) - .pow(AGE_EXPONENT) + .checked_pow(AGE_EXPONENT) + .unwrap_or(u128::MAX) //TODO???? .checked_div(divisor) .expect("div overflow") * AGE_MULTIPLIER @@ -425,67 +412,58 @@ impl PaymentAdjusterReal { } fn give_job_to_adjustment_workers( - gas_limitation_opt: Option, + &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - cw_masq_balance_wei: u128, - logger: &Logger, ) -> AdjustmentCompletion { - match gas_limitation_opt { - Some(gas_limit) => { - let weighted_accounts_cut_by_gas = - Self::cut_back_by_gas_count_limit(accounts_with_individual_criteria, gas_limit); + match self.inner.gas_limitation_opt() { + Some(limitation_by_gas) => { + let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( + accounts_with_individual_criteria, + limitation_by_gas, + ); match Self::check_need_of_masq_balances_adjustment( - logger, + &self.logger, Either::Right(&weighted_accounts_cut_by_gas), - cw_masq_balance_wei, + self.inner.cw_masq_balance(), ) { - true => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - weighted_accounts_cut_by_gas, - logger - )), + true => AdjustmentCompletion::Continue( + self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas), + ), false => AdjustmentCompletion::Finished(Self::rebuild_accounts( weighted_accounts_cut_by_gas, )), } } - None => AdjustmentCompletion::Continue(Self::handle_masq_token_adjustment( - cw_masq_balance_wei, - accounts_with_individual_criteria, - logger - )), + None => AdjustmentCompletion::Continue( + self.handle_masq_token_adjustment(accounts_with_individual_criteria), + ), } } fn handle_masq_token_adjustment( - cw_masq_balance: u128, + &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - logger: &Logger ) -> AdjustmentIterationResult { - let required_balance_total= Self::balance_total(&accounts_with_individual_criteria); + let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); let criteria_total = Self::criteria_total(&accounts_with_individual_criteria); - match Self::try_separating_outweighed_accounts( + match self.try_separating_outweighed_accounts( accounts_with_individual_criteria, required_balance_total, criteria_total, - cw_masq_balance, - logger ) { Either::Left(accounts_with_individual_criteria) => { Self::perform_adjustment_and_determine_adjustment_iteration_result( accounts_with_individual_criteria, - cw_masq_balance, + self.inner.cw_masq_balance(), criteria_total, ) } - Either::Right((outweighed, remaining)) => { - AdjustmentIterationResult { - decided_accounts: outweighed, - remaining_accounts: remaining, - disqualified_accounts: vec![], - } - } + Either::Right((outweighed, remaining)) => AdjustmentIterationResult { + decided_accounts: outweighed, + remaining_accounts: remaining, + disqualified_accounts: vec![], + }, } } @@ -537,24 +515,28 @@ impl PaymentAdjusterReal { Vec, Vec, ) { - let multiplication_coeff = - PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( - cw_masq_balance, - criteria_total, - ); - - let proportional_fragment_of_cw_balance = cw_masq_balance - .checked_mul(multiplication_coeff) - .expect("mul overflow") - .checked_div(criteria_total) + let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + cw_masq_balance, + criteria_total, + ); + let cw_masq_balance_big_u256 = U256::from(cw_masq_balance); + let criteria_total_u256 = U256::from(criteria_total); + let multiplication_coeff_u256 = U256::from(multiplication_coeff); + let proportional_fragment_of_cw_balance = cw_masq_balance_big_u256 + .checked_mul(multiplication_coeff_u256) + .unwrap() //TODO try killing this unwrap() in a test + // TODO how to give the criteria some kind of ceiling? We don't want to exceed certain dangerous limit + .checked_div(criteria_total_u256) .expect("div overflow"); accounts_with_individual_criteria.into_iter().fold( (vec![], vec![]), |(decided, disqualified), (criteria_sum, account)| { let original_balance = account.balance_wei; - let proposed_adjusted_balance = - criteria_sum * proportional_fragment_of_cw_balance / multiplication_coeff; + let proposed_adjusted_balance = (U256::from(criteria_sum) + * proportional_fragment_of_cw_balance + / multiplication_coeff_u256) + .as_u128(); diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { proposed_adjusted_balance.separate_with_commas() @@ -601,31 +583,36 @@ impl PaymentAdjusterReal { } fn try_separating_outweighed_accounts( + &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, required_balance_total: u128, criteria_total: u128, - cw_masq_balance: u128, - logger: &Logger ) -> Either, (Vec, Vec)> { match Self::check_for_outweighed_accounts( &accounts_with_individual_criteria, required_balance_total, criteria_total, + self.inner.cw_masq_balance(), ) { None => Either::Left(accounts_with_individual_criteria), Some(wallets_of_outweighed) => Either::Right({ eprintln!("wallets: {:?}", wallets_of_outweighed); - debug!(logger, "Found outweighed accounts that will have to be readjusted ({:?}).", wallets_of_outweighed); - let (outweighed_with_criteria, remaining_with_criteria): (Vec<(u128, PayableAccount)>, Vec<(u128, PayableAccount)>) = + debug!( + self.logger, + "Found outweighed accounts that will have to be readjusted ({:?}).", + wallets_of_outweighed + ); + let (unchecked_outweighed, remaining): (Vec, Vec) = accounts_with_individual_criteria .into_iter() - .map(|(_,account)|account) - .partition(|(_, account)| wallets_of_outweighed.contains(&account.wallet)); - // TODO you probably will want to supply the accounts naked to adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary - let remaining= Self::drop_criteria_and_leave_accounts(remaining_with_criteria); - let outweighed = Self::adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary(outweighed_with_criteria,cw_masq_balance,logger); - (outweighed,remaining) - }) + .map(|(_, account)| account) + .partition(|account| wallets_of_outweighed.contains(&account.wallet)); + let outweighed = self + .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( + unchecked_outweighed, + ); + (outweighed, remaining) + }), } } @@ -633,20 +620,44 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: &[(u128, PayableAccount)], required_balance_total: u128, criteria_total: u128, + cw_masq_balance: u128, //TODO remove me after the experiment ) -> Option> { - let required_balance_total_for_safe_math = required_balance_total * 10_000; - let criteria_total_for_safe_math = criteria_total * 10_000; + let coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + cw_masq_balance, + criteria_total, + ); + eprintln!("required bala {} coeff: {}", required_balance_total, coeff); + let required_balance_total_for_safe_math = required_balance_total * 1000; + let criteria_total_for_safe_math = criteria_total * 1000; let accounts_to_be_outweighed = accounts_with_individual_criteria .iter() .filter(|(criterion, account)| { + //TODO maybe this part should have its own fn, where we could test closely the relation between the ration and the proposed balance let balance_ratio = - required_balance_total_for_safe_math / (account.balance_wei * 10_000); - let criterion_ratio = criteria_total_for_safe_math / (criterion * 10_000); - // true means we would pay more than we were asked to pay at the beginning, - // this happens when the debt size is quite small but its age is large and - // plays the main factor + required_balance_total_for_safe_math / (account.balance_wei * 1000); //TODO also try giving each parameter a diff const in order to mitigate the sensitiveness + let criterion_ratio = criteria_total_for_safe_math / (criterion * 1000); //TODO try moving with this constant and watch the impact on this check + // true means we would pay more than we were asked to pay at the beginning, + // this happens when the debt size is quite small but its age is large and + // plays the main factor + { + let balance_fragment = cw_masq_balance + .checked_mul(coeff) + .unwrap() + .checked_div(criteria_total) + .unwrap(); + eprintln!( + "account {} balance before {} and after {}", + account.wallet, + account.balance_wei.separate_with_commas(), + ((balance_fragment * criterion).checked_div(coeff).unwrap()) + .separate_with_commas() + ); + eprintln!( + "this is current balance ratio: {}, this is current criterion ratio {}", + balance_ratio, criterion_ratio + ) + }; let is_highly_significant = balance_ratio > criterion_ratio; - if is_highly_significant { diagnostics( &account.wallet, @@ -671,75 +682,68 @@ impl PaymentAdjusterReal { } } - //TODO we probably want to drop the criteria before here where we've got no use for them - fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary(mut outweighed_with_criteria: Vec<(u128, PayableAccount)>, cw_masq_balance: u128, logger: &Logger) ->Vec{ - if outweighed_with_criteria.len() == 1 { - let (_, account) = &outweighed_with_criteria[0]; - if account.balance_wei > cw_masq_balance { - let (_, account) = outweighed_with_criteria.remove(0); - vec![PayableAccount {balance_wei: cw_masq_balance, ..account}] - } else {Self::drop_criteria_and_leave_accounts(outweighed_with_criteria)} - } else { - Self::run_adjustment_in_recursion(vec![], outweighed, logger) - // - // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { - // todo!() - // } else { - // todo!("{:?}", adjustment_result) - // } + //TODO we probably want to drop the criteria before here where we've got no use for them........or maybe not...because the computation has always the same res + fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( + &mut self, + mut outweighed: Vec, + ) -> Vec { + if outweighed.len() == 1 { + let only_account = &outweighed[0]; + let cw_masq_balance = self.inner.cw_masq_balance(); + if only_account.balance_wei > cw_masq_balance { + let account = outweighed.remove(0); + vec![PayableAccount { + balance_wei: cw_masq_balance, + ..account + }] + } else { + outweighed } + } else { + self.run_adjustment_in_recursion(vec![], outweighed) + // + // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { + // todo!() + // } else { + // todo!("{:?}", adjustment_result) + // } + } } - fn drop_criteria_and_leave_accounts(accounts_with_individual_criteria: Vec<(u128, PayableAccount)>)->Vec{ - accounts_with_individual_criteria.into_iter().map(|(_,account)|account).collect() + fn drop_criteria_and_leave_accounts( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + ) -> Vec { + accounts_with_individual_criteria + .into_iter() + .map(|(_, account)| account) + .collect() } - fn adjust_next_round_cw_balance_in_setup_data( - current_data: FinancialAndTechDetails, - processed_outweighed: &[PayableAccount], - ) -> FinancialAndTechDetails { + fn adjust_next_round_cw_balance_down(&mut self, processed_outweighed: &[PayableAccount]) { let subtrahend_total: u128 = Self::sum_as(processed_outweighed, |account| account.balance_wei); eprintln!( "cw masq balance {} to subtract {}", - current_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(), + self.inner.cw_masq_balance(), subtrahend_total ); - let consuming_wallet_balances = ConsumingWalletBalances { - gas_currency_wei: current_data.consuming_wallet_balances.gas_currency_wei, - masq_tokens_wei: U256::from( - current_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128() - - subtrahend_total, - ), - }; - FinancialAndTechDetails { - consuming_wallet_balances, - ..current_data - } + //TODO what happens if we choose one that will get us into negative when subtracted + self.inner.lower_remaining_cw_balance(subtrahend_total) } - fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)])->u128{ + fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { account.balance_wei }) } - fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)])->u128{ + fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { *criteria }) } - fn pick_mul_coeff_for_non_fractional_computation( - cw_masq_balance: u128, - criteria_sum: u128, - ) -> u128 { + fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; let criteria_sum_digits_count = log_10(criteria_sum); let cw_balance_digits_count = log_10(cw_masq_balance); @@ -747,7 +751,9 @@ impl PaymentAdjusterReal { .checked_sub(cw_balance_digits_count) .unwrap_or(0); let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_COEFFICIENT; - 10_u128.pow(safe_mul_coeff as u32) + 10_u128 + .checked_pow(safe_mul_coeff as u32) + .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) } fn finalize_decided_accounts( @@ -888,17 +894,12 @@ pub enum AnalysisError { }, } -// struct AdjustmentCircumstances { -// now: SystemTime, -// cw_masq_balance: u128, -// gas_limitation_opt: Option, -// // TODO information about accounts -// // from Neighborhood should later go here -// } - #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::inner::{ + PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + }; use crate::accountant::payment_adjuster::{ log_10, log_2, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ReversiblePayableAccount, @@ -924,6 +925,7 @@ mod tests { use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::iter::once; + use std::thread::current; use std::time::{Duration, SystemTime}; use std::vec; use thousands::Separable; @@ -932,7 +934,7 @@ mod tests { lazy_static! { static ref MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR: u128 = MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18); - static ref FIVE_YEAR_LONG_DEBT_SEC: u64 = 5_u64 * 365 * 24 * 60 * 60; + static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; } fn make_payable_setup_msg_coming_from_blockchain_bridge( @@ -1007,6 +1009,16 @@ mod tests { ); } + #[test] + #[should_panic( + expected = "Called the null implementation of the cw_masq_balance() method in PaymentAdjusterInner" + )] + fn payment_adjuster_new_is_created_with_inner_null() { + let result = PaymentAdjusterReal::new(); + + let _ = result.inner.cw_masq_balance(); + } + #[test] fn search_for_indispensable_adjustment_negative_answer() { init_test_logging(); @@ -1134,33 +1146,6 @@ mod tests { ); } - // #[test] - // fn find_smallest_debt_works() { - // let mut payable_1 = make_payable_account(111); - // payable_1.balance_wei = 111_111; - // let mut payable_3 = make_payable_account(333); - // payable_3.balance_wei = 111_110; - // let mut payable_2 = make_payable_account(222); - // payable_2.balance_wei = 3_000_000; - // let qualified_payables = vec![payable_1, payable_2, payable_3]; - // let referenced_qualified_payables = qualified_payables.iter().collect::>(); - // - // let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - // - // assert_eq!(min, 111_110) - // } - // - // #[test] - // fn find_smallest_debt_handles_just_one_account() { - // let payable = make_payable_account(111); - // let qualified_payables = vec![payable]; - // let referenced_qualified_payables = qualified_payables.iter().collect::>(); - // - // let min = PaymentAdjusterReal::find_smallest_debt(&referenced_qualified_payables); - // - // assert_eq!(min, 111_000_000_000) - // } - #[test] fn balance_tail_weight_works() { vec![ @@ -1228,7 +1213,7 @@ mod tests { .clone() .into_iter() .map(|cw_balance| { - PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( + PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( cw_balance, final_criteria_sum, ) @@ -1249,16 +1234,37 @@ mod tests { } #[test] - fn multiplication_coeff_testing_upper_extreme() { - let final_criteria = get_extreme_criteria(1); - let final_criteria_total = final_criteria[0].0; + fn multiplication_coeff_showing_extreme_inputs_the_produced_values_and_ceiling() { + let account_debt_ages_in_months = vec![1, 5, 12, 120, 600]; + let balance_wei_for_each_account = *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 1000; + let different_accounts_with_criteria = + get_extreme_criteria(account_debt_ages_in_months, balance_wei_for_each_account); + let final_criteria_total = + PaymentAdjusterReal::criteria_total(&different_accounts_with_criteria); let cw_balance_in_minor = 1; - let result = PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( - cw_balance_in_minor, - final_criteria_total, - ); - assert_eq!(result, 10_000_000_000_000_000_000_000_000_000_000_000_000) + let results = different_accounts_with_criteria + .into_iter() + .map(|(criteria, account)| { + // scenario simplification: we asume there is always just one account in a time + let final_criteria_total = criteria; + PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + cw_balance_in_minor, + final_criteria_total, + ) + }) + .collect::>(); + + assert_eq!( + results, + vec![ + 100000000000000000000000000000000000000, + 100000000000000000000000000000000000000, + 100000000000000000000000000000000000, + 1000000000000000000000000000000000, + 100000000000000000000000000000000 + ] + ) // enough space for our counts; mostly we use it for division and multiplication and // in both cases the coefficient is picked carefully to handle it (we near the extremes // either by increasing the criteria sum and decreasing the cw balance or vica versa) @@ -1311,31 +1317,10 @@ mod tests { assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) } - fn get_extreme_criteria(number_of_accounts: usize) -> Vec<(u128, PayableAccount)> { - let now = SystemTime::now(); - let account = PayableAccount { - wallet: make_wallet("blah"), - balance_wei: *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, - last_paid_timestamp: now - .checked_sub(Duration::from_secs(*FIVE_YEAR_LONG_DEBT_SEC)) - .unwrap(), - pending_payable_opt: None, - }; - let accounts = once(account).cycle().take(number_of_accounts).collect(); - let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); - PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now) - } - - #[test] - fn testing_criteria_on_overflow_safeness() { - let criteria_and_accounts = get_extreme_criteria(3); - assert_eq!(criteria_and_accounts.len(), 3); - //operands in apply_criteria have to be their checked version therefore we passed through without a panic and so no overflow occurred - } - #[test] fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { let now = SystemTime::now(); + let subject = make_initialized_subject(now, None, None); let account_1 = PayableAccount { wallet: make_wallet("def"), balance_wei: 333_000_000_000_000, @@ -1358,7 +1343,7 @@ mod tests { let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - let weights_and_accounts = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + let weights_and_accounts = subject.apply_criteria(zero_criteria_accounts); let only_accounts = weights_and_accounts .iter() @@ -1370,14 +1355,16 @@ mod tests { #[test] fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { let now = SystemTime::now(); - let collected_setup_data = FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u128::MAX), - masq_tokens_wei: U256::from(1_500_000_000_000_u64 - 25_000_000), - }, - desired_gas_price_gwei: 50, - estimated_gas_limit_per_transaction: 55_000, - }; + let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000; + let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); + // let collected_setup_data = FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // gas_currency_wei: U256::from(u128::MAX), + // masq_tokens_wei: U256::from(1_500_000_000_000_u64 - 25_000_000), + // }, + // desired_gas_price_gwei: 50, + // estimated_gas_limit_per_transaction: 55_000, + // }; let balance_1 = 1_500_000_000_000; let balance_2 = 25_000_000; let wallet_1 = make_wallet("blah"); @@ -1397,14 +1384,7 @@ mod tests { let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; - let result = PaymentAdjusterReal::run_adjustment_in_recursion( - vec![], - qualified_payables.clone(), - collected_setup_data, - None, - now, - &logger, - ); + let result = subject.run_adjustment_in_recursion(vec![], qualified_payables.clone()); //first a presentation of why this test is important let total = balance_1 * 10_000 + balance_2 * 10_000; @@ -1412,7 +1392,7 @@ mod tests { let ratio_2 = ratio_2_u128 as f64 / 10_000.0; let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - let criteria = PaymentAdjusterReal::apply_criteria(zero_criteria_accounts, now); + let criteria = subject.apply_criteria(zero_criteria_accounts); let account_1_criterion = criteria[0].0 * 10_000; let account_2_criterion = criteria[1].0 * 10_000; let criteria_total = account_1_criterion + account_2_criterion; @@ -1441,23 +1421,25 @@ mod tests { fn try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less( ) { let test_name = "try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less"; + let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; + let mut subject = make_initialized_subject( + now, + Some(consuming_wallet_balance), + Some(Logger::new(test_name)), + ); let account_1_made_up_criteria = 18_000_000_000_000; let account_1 = PayableAccount { wallet: make_wallet("blah"), balance_wei: 1_000_000_000_000, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(200000)) - .unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(200000)).unwrap(), pending_payable_opt: None, }; let account_2_made_up_criteria = 5_000_000_000_000; let account_2 = PayableAccount { - wallet:make_wallet("booga"), + wallet: make_wallet("booga"), balance_wei: 8_000_000_000_000_000, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(1000)) - .unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; let required_balance_total = 1_000_000_000_000 + 333 + 8_000_000_000_000_000; @@ -1467,14 +1449,13 @@ mod tests { (account_2_made_up_criteria, account_2.clone()), ]; - let (outweighed, remaining) = - PaymentAdjusterReal::try_separating_outweighed_accounts( + let (outweighed, remaining) = subject + .try_separating_outweighed_accounts( accounts_with_individual_criteria, required_balance_total, criteria_total, - consuming_wallet_balance, - &Logger::new(test_name) - ).right() + ) + .right() .unwrap(); let mut expected_account = account_1; @@ -1488,69 +1469,146 @@ mod tests { ) { let test_name = "try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum"; let now = SystemTime::now(); - let consuming_wallet_balance = 2_000_000_000_000; - let balance_1 = 1_500_000_000_000; + let consuming_wallet_balance = 90_000_000_000_000; + let mut subject = make_initialized_subject( + now, + Some(consuming_wallet_balance), + Some(Logger::new(test_name)), + ); + let balance_1 = 150_000_000_000_000; let account_1 = PayableAccount { wallet: make_wallet("blah"), balance_wei: balance_1, last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(200000)) + .checked_sub(Duration::from_secs(170000)) .unwrap(), pending_payable_opt: None, }; - let balance_2 = 3_000_000_000_000; + let balance_2 = 300_000_000_000_000; let account_2 = PayableAccount { wallet: make_wallet("booga"), balance_wei: balance_2, last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(150000)) + .checked_sub(Duration::from_secs(170000)) .unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("giraffe"), - balance_wei: 8_000_000_000_000_000, + balance_wei: 800_000_000_000_000_000, last_paid_timestamp: SystemTime::now() .checked_sub(Duration::from_secs(1000)) .unwrap(), pending_payable_opt: None, }; - let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![account_1.clone(),account_2.clone(),account_3.clone()]); - let accounts_with_individual_criteria = PaymentAdjusterReal::apply_criteria(accounts_with_zero_criteria, now); - let required_balance_total = PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); - let criteria_total = PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); - - let (outweighed, remaining) = - PaymentAdjusterReal::try_separating_outweighed_accounts( + let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ + account_1.clone(), + account_2.clone(), + account_3.clone(), + ]); + let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); + let required_balance_total = + PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); + let criteria_total = + PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); + + let (outweighed, remaining) = subject + .try_separating_outweighed_accounts( accounts_with_individual_criteria.clone(), required_balance_total, criteria_total, - consuming_wallet_balance, - &Logger::new(test_name) - ).right() - .unwrap(); + ) + .right() + .unwrap(); // the algorithm first isolates the two first outweighed accounts and because - // they are more than one it will apply the standard adjustment strategy according to its criteria, - // with one difference though, now the third account will stay out - let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria.iter().take(2).map(|(criteria_sum, _)|*criteria_sum).collect(); - let expected_balances = compute_expected_balanced_portions_from_criteria(individual_criteria_for_just_two_accounts, consuming_wallet_balance); - //making sure the proposed balances are non zero - assert!(expected_balances[0] > (balance_1 / 4)); - assert!(expected_balances[1] > (balance_1 / 4)); - let check_sum = expected_balances.iter().sum(); - assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); + // they are more than one it will apply the standard adjustment strategy according to their criteria, + // with one big difference, now the third account will stay out + let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria + .iter() + .take(2) + .map(|(criteria_sum, _)| *criteria_sum) + .collect(); + let expected_balances = compute_expected_balanced_portions_from_criteria( + individual_criteria_for_just_two_accounts, + consuming_wallet_balance, + ); + // //making sure the proposed balances are non zero + // assert!(expected_balances[0] > (balance_1 / 4)); + // assert!(expected_balances[1] > (balance_2 / 4)); + // let check_sum = expected_balances.iter().sum(); + // assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); let expected_outweighed_accounts = { let mut account_1 = account_1; account_1.balance_wei = expected_balances[0]; let mut account_2 = account_2; - account_2.balance_wei =expected_balances[1]; - vec![account_1,account_2] + account_2.balance_wei = expected_balances[1]; + vec![account_1, account_2] }; assert_eq!(outweighed, expected_outweighed_accounts); assert_eq!(remaining, vec![account_3]) } + #[test] + fn trying_to_go_through_the_complete_process_under_the_extremest_debt_conditions_without_getting_killed( + ) { + init_test_logging(); + let test_name = "trying_to_go_through_the_complete_process_under_the_extremest_debt_conditions_without_getting_killed"; + let now = SystemTime::now(); + //each of 3 accounts contains half of the full supply and a 10-years-old debt which generates extremely big numbers in the criteria + let qualified_payables = { + let mut accounts = get_extreme_accounts( + vec![120, 120, 120], + now, + *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + ); + accounts + .iter_mut() + .for_each(|account| account.balance_wei = account.balance_wei / 2); + accounts + }; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + //for change extremely small cw balance + let cw_masq_balance = 1_000; + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(u32::MAX), + masq_tokens_wei: U256::from(cw_masq_balance), + }, + estimated_gas_limit_per_transaction: 70_000, + desired_gas_price_gwei: 120, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::MasqToken, + }; + + let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + + //because the proposed final balances all all way lower than (at least) the half of the original balances + assert_eq!(result.accounts, vec![]); + let expected_log = |wallet: &str| { + format!("INFO: {test_name}: Consuming wallet low in MASQ balance. Recently qualified \ + payable for wallet {} will not be paid as the consuming wallet handles to provide only 333 wei \ + which is not at least more than a half of the original debt {}", wallet, + (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 2).separate_with_commas()) + }; + let log_handler = TestLogHandler::new(); + log_handler + .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616830")); + log_handler + .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616831")); + log_handler + .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616832")); + } + #[test] fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { init_test_logging(); @@ -1575,7 +1633,8 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); let accounts_sum: u128 = 4_444_444_444_444_444_444 + 6_666_666_666_000_000_000 + 60_000_000_000_000_000; //= 1_000_022_000_000_444_444 let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 70_000_000_000_000_000); @@ -1656,7 +1715,8 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1735,7 +1795,8 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); let consuming_wallet_masq_balance_wei = U256::from(333_000_000_000_u64 + 50_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, @@ -1805,10 +1866,10 @@ mod tests { context_id: 234 }) ); - TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Recently qualified \ - payable for wallet 0x000000000000000000000000000000000067686b is being ignored as the limited \ - consuming balance implied adjustment of its balance down to 56,554,286 wei, which is not at \ - least half of the debt")); + TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Consuming wallet \ + low in MASQ balance. Recently qualified payable for wallet 0x00000000000000000000000000000\ + 0000067686b will not be paid as the consuming wallet handles to provide only 56,554,286 wei \ + which is not at least more than a half of the original debt 600,000,000")); } fn test_competitive_accounts( @@ -1838,7 +1899,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone()]; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 - 1); let setup_msg = PayablePaymentSetup { qualified_payables, @@ -2090,7 +2151,8 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); let consuming_wallet_masq_balance = 300_000_000_000_000_u128; let setup_msg = PayablePaymentSetup { qualified_payables, @@ -2195,11 +2257,10 @@ mod tests { }, ); let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = - PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); + let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei .checked_mul(multiplication_coeff) .unwrap() @@ -2248,13 +2309,15 @@ mod tests { .collect() } - fn compute_expected_balanced_portions_from_criteria(final_criteria: Vec, consuming_wallet_masq_balance_wei: u128)->Vec{ + fn compute_expected_balanced_portions_from_criteria( + final_criteria: Vec, + consuming_wallet_masq_balance_wei: u128, + ) -> Vec { let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = - PaymentAdjusterReal::pick_mul_coeff_for_non_fractional_computation( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); + let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + consuming_wallet_masq_balance_wei, + final_criteria_sum, + ); let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei .checked_mul(multiplication_coeff) .unwrap() @@ -2276,4 +2339,51 @@ mod tests { ); new_balanced_portions } + + fn get_extreme_criteria( + months_of_debt_matrix: Vec, + common_balance_wei: u128, + ) -> Vec<(u128, PayableAccount)> { + let now = SystemTime::now(); + let accounts = get_extreme_accounts(months_of_debt_matrix, now, common_balance_wei); + let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); + let subject = make_initialized_subject(now, None, None); + subject.apply_criteria(zero_criteria_accounts) + } + + fn get_extreme_accounts( + months_of_debt_matrix: Vec, + now: SystemTime, + common_balance_wei: u128, + ) -> Vec { + months_of_debt_matrix + .into_iter() + .enumerate() + .map(|(idx, number_of_months)| PayableAccount { + wallet: make_wallet(&format!("blah{}", idx)), + balance_wei: common_balance_wei, + last_paid_timestamp: now + .checked_sub(Duration::from_secs( + number_of_months as u64 * (*ONE_MONTH_LONG_DEBT_SEC), + )) + .unwrap(), + pending_payable_opt: None, + }) + .collect() + } + + fn make_initialized_subject( + now: SystemTime, + cw_masq_balance_opt: Option, + logger_opt: Option, + ) -> PaymentAdjusterReal { + PaymentAdjusterReal { + inner: Box::new(PaymentAdjusterInnerReal::new( + now, + None, + cw_masq_balance_opt.unwrap_or(0), + )), + logger: logger_opt.unwrap_or(Logger::new("test")), + } + } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index e45cabd00..4bb53547b 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -180,7 +180,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_threshold_gauge: Box, - pub payment_adjuster: Box, + pub payment_adjuster: RefCell>, } impl Scanner for PayableScanner { @@ -262,6 +262,7 @@ impl PayableScannerMiddleProcedures for PayableScanner { ) -> Result, String> { match self .payment_adjuster + .borrow() .search_for_indispensable_adjustment(&msg, logger) { Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { @@ -279,7 +280,9 @@ impl PayableScannerMiddleProcedures for PayableScanner { logger: &Logger, ) -> OutcomingPaymentsInstructions { let now = SystemTime::now(); - self.payment_adjuster.adjust_payments(setup, now, logger) + self.payment_adjuster + .borrow_mut() + .adjust_payments(setup, now, logger) } } @@ -297,7 +300,7 @@ impl PayableScanner { payable_dao, pending_payable_dao, payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), - payment_adjuster, + payment_adjuster: RefCell::new(payment_adjuster), } } @@ -1112,10 +1115,6 @@ mod tests { .as_any() .downcast_ref::() .unwrap(); - payable_scanner - .payment_adjuster - .as_any() - .downcast_ref::(); assert_eq!( pending_payable_scanner.when_pending_too_long_sec, when_pending_too_long_sec diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 1e66604c5..311402fb2 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1414,7 +1414,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { } fn adjust_payments( - &self, + &mut self, setup: AwaitedAdjustment, now: SystemTime, logger: &Logger, From 9bfdb93ba2551d73625fa7a8bddf3964c85f4a32 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 23 Jul 2023 00:41:38 +0200 Subject: [PATCH 044/250] GH-711: advancing towards more straightened design; affecting mostly the outweighed accounts computation --- node/src/accountant/payment_adjuster/inner.rs | 1 + node/src/accountant/payment_adjuster/mod.rs | 917 ++++++++++-------- 2 files changed, 521 insertions(+), 397 deletions(-) diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 58a12e49c..62eee7165 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -13,6 +13,7 @@ pub trait PaymentAdjusterInner { PaymentAdjusterInnerNull::panicking_operation("cw_masq_balance()") } + //TODO this method should use RefCell internally...and we could have PaymentAdjuster with &self instead of &mut self fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { PaymentAdjusterInnerNull::panicking_operation("lower_remaining_cw_balance()") } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 6a412bd5a..e12ae3ee1 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -24,6 +24,7 @@ use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; +use itertools::Either::{Left, Right}; use itertools::{Either, Itertools}; use log::logger; use masq_lib::logger::Logger; @@ -122,7 +123,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = self.run_adjustment_in_recursion(vec![], qualified_payables); + let adjusted_accounts = self.run_full_adjustment_procedure(qualified_payables, vec![]); debug!( self.logger, @@ -273,10 +274,10 @@ impl PaymentAdjusterReal { } } - fn run_adjustment_in_recursion( + fn run_full_adjustment_procedure( &mut self, - resolved_qualified_accounts: Vec, unresolved_qualified_accounts: Vec, + resolved_qualified_accounts: Vec, ) -> Vec { diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); diagnostics_collective( @@ -288,7 +289,18 @@ impl PaymentAdjusterReal { let sorted_accounts_with_individual_criteria = self.apply_criteria(accounts_with_zero_criteria); - let adjustment_result: AdjustmentIterationResult = + self.run_adjustment_by_criteria_recursively( + sorted_accounts_with_individual_criteria, + resolved_qualified_accounts, + ) + } + + fn run_adjustment_by_criteria_recursively( + &mut self, + sorted_accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + resolved_qualified_accounts: Vec, + ) -> Vec { + let adjustment_result: AdjustmentIterationSummary = match self.give_job_to_adjustment_workers(sorted_accounts_with_individual_criteria) { AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, AdjustmentCompletion::Continue(iteration_result) => iteration_result, @@ -300,9 +312,9 @@ impl PaymentAdjusterReal { adjustment_result.decided_accounts } else { self.adjust_next_round_cw_balance_down(&adjustment_result.decided_accounts); - return self.run_adjustment_in_recursion( - adjustment_result.decided_accounts, + return self.run_full_adjustment_procedure( adjustment_result.remaining_accounts, + adjustment_result.decided_accounts, ); }; @@ -348,7 +360,7 @@ impl PaymentAdjusterReal { let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; (elapsed_sec as u128) .checked_pow(AGE_EXPONENT) - .unwrap_or(u128::MAX) //TODO???? + .unwrap_or(u128::MAX) //TODO sensible and tested ???? .checked_div(divisor) .expect("div overflow") * AGE_MULTIPLIER @@ -443,78 +455,71 @@ impl PaymentAdjusterReal { fn handle_masq_token_adjustment( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> AdjustmentIterationResult { - let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); + ) -> AdjustmentIterationSummary { + // let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); let criteria_total = Self::criteria_total(&accounts_with_individual_criteria); - match self.try_separating_outweighed_accounts( + // match self.handle_possibly_outweighed_accounts( + // accounts_with_individual_criteria, + // required_balance_total, + // criteria_total, + // ) { + // Either::Left(accounts_with_individual_criteria) => { + self.perform_adjustment_and_determine_adjustment_iteration_result( accounts_with_individual_criteria, - required_balance_total, criteria_total, - ) { - Either::Left(accounts_with_individual_criteria) => { - Self::perform_adjustment_and_determine_adjustment_iteration_result( - accounts_with_individual_criteria, - self.inner.cw_masq_balance(), - criteria_total, - ) - } - Either::Right((outweighed, remaining)) => AdjustmentIterationResult { - decided_accounts: outweighed, - remaining_accounts: remaining, - disqualified_accounts: vec![], - }, - } + ) + // } + // Either::Right((outweighed, remaining)) => AdjustmentIterationSummary { + // decided_accounts: outweighed, + // remaining_accounts: remaining, + // disqualified_accounts: vec![], + // }, + // } } fn perform_adjustment_and_determine_adjustment_iteration_result( + &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - cw_masq_balance: u128, criteria_total: u128, - ) -> AdjustmentIterationResult { - let (decided_accounts, disqualified_accounts): ( - Vec, - Vec, - ) = Self::recreate_accounts_with_proportioned_balances( + ) -> AdjustmentIterationSummary { + //TODO simplify this...one of these enums is probably redundant + match self.recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, - cw_masq_balance, criteria_total, - ); - - if disqualified_accounts.is_empty() { - let decided_accounts = Self::finalize_decided_accounts( - decided_accounts, - DecidedPayableAccountResolution::Finalize, - ); - AdjustmentIterationResult { - decided_accounts, - remaining_accounts: vec![], - disqualified_accounts: vec![], + ) { + AccountsRecreationResult::AllAccountsCleanlyProcessed(decided_accounts) => { + AdjustmentIterationSummary { + decided_accounts, + remaining_accounts: vec![], + disqualified_accounts: vec![], + } } - } else { - // reverting decided accounts because after we lose the disqualified ones from - // the compilation it may be that the remaining accounts could be now paid - // in more favorable proportions or even in the full size - let remaining_accounts = Self::finalize_decided_accounts( - decided_accounts, - DecidedPayableAccountResolution::Revert, - ); - AdjustmentIterationResult { + AccountsRecreationResult::InsignificantAccounts { + disqualified, + remaining, + } => AdjustmentIterationSummary { decided_accounts: vec![], - remaining_accounts, - disqualified_accounts, - } + remaining_accounts: remaining, + disqualified_accounts: disqualified, + }, + AccountsRecreationResult::OutweighedAccounts { + outweighed, + remaining, + } => AdjustmentIterationSummary { + decided_accounts: outweighed, + remaining_accounts: remaining, + disqualified_accounts: vec![], + }, } } fn recreate_accounts_with_proportioned_balances( + &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - cw_masq_balance: u128, criteria_total: u128, - ) -> ( - Vec, - Vec, - ) { + ) -> AccountsRecreationResult { + let cw_masq_balance = self.inner.cw_masq_balance(); let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( cw_masq_balance, criteria_total, @@ -529,10 +534,9 @@ impl PaymentAdjusterReal { .checked_div(criteria_total_u256) .expect("div overflow"); - accounts_with_individual_criteria.into_iter().fold( - (vec![], vec![]), - |(decided, disqualified), (criteria_sum, account)| { - let original_balance = account.balance_wei; + let accounts_with_unchecked_adjustment: Vec<_> = accounts_with_individual_criteria + .into_iter() + .map(|(criteria_sum, account)| { let proposed_adjusted_balance = (U256::from(criteria_sum) * proportional_fragment_of_cw_balance / multiplication_coeff_u256) @@ -541,172 +545,237 @@ impl PaymentAdjusterReal { diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { proposed_adjusted_balance.separate_with_commas() }); - - Self::consider_account_disqualification_from_percentage_insignificance( - decided, - disqualified, + AccountWithUncheckedAdjustment::new( account, - original_balance, proposed_adjusted_balance, + criteria_sum, ) - }, - ) + }) + .collect(); + + let unchecked_for_outweighed = + match Self::consider_account_disqualification_from_percentage_insignificance( + accounts_with_unchecked_adjustment, + ) { + Left(still_not_fully_checked) => still_not_fully_checked, + Right(with_some_disqualified) => return with_some_disqualified, + }; + + let finalized_accounts = + match self.handle_possibly_outweighed_account(unchecked_for_outweighed) { + Left(finalized_accounts) => finalized_accounts, + Right(with_some_outweighed) => return with_some_outweighed, + }; + + AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) } fn consider_account_disqualification_from_percentage_insignificance( - mut decided: Vec, - mut disqualified: Vec, - mut account: PayableAccount, - original_balance: u128, - proposed_adjusted_balance: u128, - ) -> ( - Vec, - Vec, - ) { - let balance_at_the_edge = - ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) + accounts_with_unchecked_adjustment: Vec, + ) -> Either, AccountsRecreationResult> { + let wallets_of_accounts_to_disqualify: Vec<&Wallet> = accounts_with_unchecked_adjustment + .iter() + .flat_map(|account_info| { + let original_balance = account_info.original_account.balance_wei; + let balance_at_the_edge = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor); - if balance_at_the_edge <= (proposed_adjusted_balance * 10) { - account.balance_wei = proposed_adjusted_balance; - let decided_account = ReversiblePayableAccount::new(account, original_balance); - decided.push(decided_account); - (decided, disqualified) + let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + if proposed_adjusted_balance <= balance_at_the_edge { + Some(&account_info.original_account.wallet) + } else { + None + // let disqualified_account = DisqualifiedPayableAccount::new( + // account.wallet, + // original_balance, + // proposed_adjusted_balance, + // ); + // disqualified.push(disqualified_account); + // (decided, disqualified) + } + }) + .collect(); + if wallets_of_accounts_to_disqualify.is_empty() { + Left(accounts_with_unchecked_adjustment) } else { - let disqualified_account = DisqualifiedPayableAccount::new( - account.wallet, - original_balance, - proposed_adjusted_balance, - ); - disqualified.push(disqualified_account); - (decided, disqualified) + //TODO add console diagnostics here + todo!("create the account creation result") } } - fn try_separating_outweighed_accounts( + fn handle_possibly_outweighed_account( &mut self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - required_balance_total: u128, - criteria_total: u128, - ) -> Either, (Vec, Vec)> { - match Self::check_for_outweighed_accounts( - &accounts_with_individual_criteria, - required_balance_total, - criteria_total, - self.inner.cw_masq_balance(), - ) { - None => Either::Left(accounts_with_individual_criteria), - Some(wallets_of_outweighed) => Either::Right({ - eprintln!("wallets: {:?}", wallets_of_outweighed); - debug!( - self.logger, - "Found outweighed accounts that will have to be readjusted ({:?}).", - wallets_of_outweighed - ); - let (unchecked_outweighed, remaining): (Vec, Vec) = - accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .partition(|account| wallets_of_outweighed.contains(&account.wallet)); - let outweighed = self - .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( - unchecked_outweighed, - ); - (outweighed, remaining) - }), - } - } - - fn check_for_outweighed_accounts( - accounts_with_individual_criteria: &[(u128, PayableAccount)], - required_balance_total: u128, - criteria_total: u128, - cw_masq_balance: u128, //TODO remove me after the experiment - ) -> Option> { - let coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( - cw_masq_balance, - criteria_total, - ); - eprintln!("required bala {} coeff: {}", required_balance_total, coeff); - let required_balance_total_for_safe_math = required_balance_total * 1000; - let criteria_total_for_safe_math = criteria_total * 1000; - let accounts_to_be_outweighed = accounts_with_individual_criteria - .iter() - .filter(|(criterion, account)| { - //TODO maybe this part should have its own fn, where we could test closely the relation between the ration and the proposed balance - let balance_ratio = - required_balance_total_for_safe_math / (account.balance_wei * 1000); //TODO also try giving each parameter a diff const in order to mitigate the sensitiveness - let criterion_ratio = criteria_total_for_safe_math / (criterion * 1000); //TODO try moving with this constant and watch the impact on this check - // true means we would pay more than we were asked to pay at the beginning, - // this happens when the debt size is quite small but its age is large and - // plays the main factor - { - let balance_fragment = cw_masq_balance - .checked_mul(coeff) - .unwrap() - .checked_div(criteria_total) - .unwrap(); - eprintln!( - "account {} balance before {} and after {}", - account.wallet, - account.balance_wei.separate_with_commas(), - ((balance_fragment * criterion).checked_div(coeff).unwrap()) - .separate_with_commas() - ); - eprintln!( - "this is current balance ratio: {}, this is current criterion ratio {}", - balance_ratio, criterion_ratio - ) - }; - let is_highly_significant = balance_ratio > criterion_ratio; - if is_highly_significant { - diagnostics( - &account.wallet, - "ACCOUNT PROPOSED WITH BALANCE HIGHER THAN THE ORIGIN ONE", - || { - format!( - "balance_ration ({}) > criterion_ration ({})", - balance_ratio, criterion_ratio - ) - }, - ) - } + accounts_with_unchecked_adjustment: Vec, + ) -> Either, AccountsRecreationResult> { + let init: ( + Vec<(u128, PayableAccount)>, + Vec, + ) = (vec![], vec![]); + let (outweighed_with_already_made_criteria, passing_through) = + accounts_with_unchecked_adjustment.into_iter().fold( + init, + |(mut outweighed, mut passing_through), account_info| { + if account_info.proposed_adjusted_balance + > account_info.original_account.balance_wei + //TODO test the operator against <= + { + diagnostics( + &account_info.original_account.wallet, + "OUTWEIGHED ACCOUNT FOUND", + || { + format!( + "original balance: {}, proposed balance {}", + account_info.original_account.balance_wei, + account_info.proposed_adjusted_balance + ) + }, + ); + + let outweighed_record = + (account_info.criteria_sum, account_info.original_account); + outweighed.push(outweighed_record); + (outweighed, passing_through) + } else { + passing_through.push(account_info); + (outweighed, passing_through) + } + }, + ); - is_highly_significant - }) - .map(|(_, account)| account.wallet.clone()) - .collect::>(); - if !accounts_to_be_outweighed.is_empty() { - Some(accounts_to_be_outweighed) + if outweighed_with_already_made_criteria.is_empty() { + Left(AccountWithUncheckedAdjustment::finalize_collection( + passing_through, + DecidedPayableAccountResolution::Finalize, + )) } else { - None + let clean_outweighed_accounts = self + .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( + outweighed_with_already_made_criteria, + ); + let remaining = AccountWithUncheckedAdjustment::finalize_collection( + passing_through, + DecidedPayableAccountResolution::Revert, + ); + Right(AccountsRecreationResult::OutweighedAccounts { + outweighed: clean_outweighed_accounts, + remaining, + }) } - } + + // None => Either::Left(account_with_individual_criteria), + // Some(wallets_of_outweighed) => Either::Right({ + // eprintln!("wallets: {:?}", wallets_of_outweighed); + // debug!( + // self.logger, + // "Found outweighed accounts that will have to be readjusted ({:?}).", + // wallets_of_outweighed + // ); + // let (unchecked_outweighed, remaining): (Vec, Vec) = + // account_with_individual_criteria + // .into_iter() + // .map(|(_, account)| account) + // .partition(|account| wallets_of_outweighed.contains(&account.wallet)); + // + // (outweighed, remaining) + // }), + // } + } + + // fn check_for_outweighed_accounts( + // accounts_with_individual_criteria: &[(u128, PayableAccount)], + // required_balance_total: u128, + // criteria_total: u128, + // cw_masq_balance: u128, //TODO remove me after the experiment + // ) -> Option> { + // let coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + // cw_masq_balance, + // criteria_total, + // ); + // eprintln!("required bala {} coeff: {}", required_balance_total, coeff); + // let required_balance_total_for_safe_math = required_balance_total * 1000; + // let criteria_total_for_safe_math = criteria_total * 1000; + // let accounts_to_be_outweighed = accounts_with_individual_criteria + // .iter() + // .filter(|(criterion, account)| { + // //TODO maybe this part should have its own fn, where we could test closely the relation between the ration and the proposed balance + // let balance_ratio = + // required_balance_total_for_safe_math / (account.balance_wei * 1000); //TODO also try giving each parameter a diff const in order to mitigate the sensitiveness + // let criterion_ratio = criteria_total_for_safe_math / (criterion * 1000); //TODO try moving with this constant and watch the impact on this check + // // true means we would pay more than we were asked to pay at the beginning, + // // this happens when the debt size is quite small but its age is large and + // // plays the main factor + // // { + // // let balance_fragment = cw_masq_balance + // // .checked_mul(coeff) + // // .unwrap() + // // .checked_div(criteria_total) + // // .unwrap(); + // // eprintln!( + // // "account {} balance before {} and after {}", + // // account.wallet, + // // account.balance_wei.separate_with_commas(), + // // ((balance_fragment * criterion).checked_div(coeff).unwrap()) + // // .separate_with_commas() + // // ); + // // eprintln!( + // // "this is current balance ratio: {}, this is current criterion ratio {}", + // // balance_ratio, criterion_ratio + // // ) + // // }; + // let is_highly_significant = balance_ratio > criterion_ratio; + // if is_highly_significant { + // diagnostics( + // &account.wallet, + // "ACCOUNT PROPOSED WITH BALANCE HIGHER THAN THE ORIGIN ONE", + // || { + // format!( + // "balance_ration ({}) > criterion_ration ({})", + // balance_ratio, criterion_ratio + // ) + // }, + // ) + // } + // + // is_highly_significant + // }) + // .map(|(_, account)| account.wallet.clone()) + // .collect::>(); + // if !accounts_to_be_outweighed.is_empty() { + // Some(accounts_to_be_outweighed) + // } else { + // None + // } + // } //TODO we probably want to drop the criteria before here where we've got no use for them........or maybe not...because the computation has always the same res fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( &mut self, - mut outweighed: Vec, + mut outweighed_with_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { - if outweighed.len() == 1 { - let only_account = &outweighed[0]; - let cw_masq_balance = self.inner.cw_masq_balance(); + //TODO is this special condition for the single guy necessary?? + let cw_masq_balance = self.inner.cw_masq_balance(); + if outweighed_with_criteria.len() == 1 { + let (_, only_account) = outweighed_with_criteria.remove(0); if only_account.balance_wei > cw_masq_balance { - let account = outweighed.remove(0); vec![PayableAccount { balance_wei: cw_masq_balance, - ..account + ..only_account }] } else { - outweighed + vec![only_account] } - } else { - self.run_adjustment_in_recursion(vec![], outweighed) + } else if Self::sum_as::(&outweighed_with_criteria, |(_, account)| { + account.balance_wei + }) > cw_masq_balance + { + self.run_adjustment_by_criteria_recursively(outweighed_with_criteria, vec![]) // // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { // todo!() // } else { // todo!("{:?}", adjustment_result) // } + } else { + todo!() } } @@ -756,16 +825,6 @@ impl PaymentAdjusterReal { .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) } - fn finalize_decided_accounts( - decided_accounts: Vec, - resolution: DecidedPayableAccountResolution, - ) -> Vec { - decided_accounts - .into_iter() - .map(|decided_account| PayableAccount::from((decided_account, resolution))) - .collect() - } - fn cut_back_by_gas_count_limit( weights_and_accounts: Vec<(u128, PayableAccount)>, limit: u16, @@ -809,19 +868,43 @@ fn log_2(x: u128) -> u32 { num_bits::() as u32 - x.leading_zeros() - 1 } +enum AccountsRecreationResult { + AllAccountsCleanlyProcessed(Vec), + InsignificantAccounts { + disqualified: Vec, + remaining: Vec, + }, + OutweighedAccounts { + outweighed: Vec, + remaining: Vec, + }, +} + +impl AccountsRecreationResult { + fn add_ok_adjusted_account(mut self, adjusted_account: PayableAccount) -> Self { + match self { + AccountsRecreationResult::AllAccountsCleanlyProcessed(_) => todo!(), + AccountsRecreationResult::InsignificantAccounts { .. } => todo!(), + AccountsRecreationResult::OutweighedAccounts { .. } => todo!(), + } + } +} + #[derive(Debug)] -struct AdjustmentIterationResult { +struct AdjustmentIterationSummary { decided_accounts: Vec, remaining_accounts: Vec, disqualified_accounts: Vec, } -#[derive(Debug, PartialEq, Eq)] -struct ReversiblePayableAccount { - adjusted_account: PayableAccount, - former_balance: u128, -} +// #[derive(Debug, PartialEq, Eq)] +// struct ReversiblePayableAccount { +// adjusted_account: PayableAccount, +// former_balance: u128, +// } +// +//TODO rename??? #[derive(Clone, Copy)] enum DecidedPayableAccountResolution { Finalize, @@ -830,31 +913,69 @@ enum DecidedPayableAccountResolution { enum AdjustmentCompletion { Finished(Vec), - Continue(AdjustmentIterationResult), + Continue(AdjustmentIterationSummary), } -impl ReversiblePayableAccount { - fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { - Self { - adjusted_account, - former_balance, +// impl ReversiblePayableAccount { +// fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { +// Self { +// adjusted_account, +// former_balance, +// } +// } +// } +// + +impl + From<( + AccountWithUncheckedAdjustment, + DecidedPayableAccountResolution, + )> for PayableAccount +{ + fn from( + (account_info, resolution): ( + AccountWithUncheckedAdjustment, + DecidedPayableAccountResolution, + ), + ) -> Self { + match resolution { + DecidedPayableAccountResolution::Finalize => PayableAccount { + balance_wei: account_info.proposed_adjusted_balance, + ..account_info.original_account + }, + DecidedPayableAccountResolution::Revert => account_info.original_account, } } } -impl From<(ReversiblePayableAccount, DecidedPayableAccountResolution)> for PayableAccount { - fn from( - (decided_account, resolution): (ReversiblePayableAccount, DecidedPayableAccountResolution), +pub struct AccountWithUncheckedAdjustment { + original_account: PayableAccount, + proposed_adjusted_balance: u128, + criteria_sum: u128, +} + +impl AccountWithUncheckedAdjustment { + fn new( + original_account: PayableAccount, + proposed_adjusted_balance: u128, + criteria_sum: u128, ) -> Self { - match resolution { - DecidedPayableAccountResolution::Finalize => decided_account.adjusted_account, - DecidedPayableAccountResolution::Revert => { - let mut reverted_account = decided_account.adjusted_account; - reverted_account.balance_wei = decided_account.former_balance; - reverted_account - } + Self { + original_account, + proposed_adjusted_balance, + criteria_sum, } } + + fn finalize_collection( + account_infos: Vec, + resolution: DecidedPayableAccountResolution, + ) -> Vec { + account_infos + .into_iter() + .map(|account_info| PayableAccount::from((account_info, resolution))) + .collect() + } } #[derive(Debug, PartialEq, Eq)] @@ -902,10 +1023,9 @@ mod tests { }; use crate::accountant::payment_adjuster::{ log_10, log_2, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, - PaymentAdjusterReal, PercentageAccountInsignificance, ReversiblePayableAccount, - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_EXPONENT, AGE_MULTIPLIER, - BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, BALANCE_TAIL_WEIGHT_EXPONENT, - BALANCE_TAIL_WEIGHT_MASK, BALANCE_TAIL_WEIGHT_MULTIPLIER, + PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + AGE_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, + BALANCE_TAIL_WEIGHT_EXPONENT, BALANCE_TAIL_WEIGHT_MASK, BALANCE_TAIL_WEIGHT_MULTIPLIER, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ @@ -1275,46 +1395,47 @@ mod tests { #[test] fn recreate_accounts_with_proportioned_balances_adheres_to_the_manifested_edge_of_insignificance( ) { - let mut payable_account_1 = make_payable_account(1); - payable_account_1.balance_wei = 1_000_000; - let mut payable_account_2 = make_payable_account(2); - payable_account_2.balance_wei = 1_000_001; - let proportional_fragment_of_cw_balance = 200_000; - let multiplication_coeff = 10; - let proposed_adjusted_balance = 500_000; - let criterion = - proposed_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 - let weights_and_accounts = vec![ - (criterion, payable_account_1.clone()), - (criterion, payable_account_2.clone()), - ]; - - let (decided_accounts, disqualified_accounts) = - PaymentAdjusterReal::recreate_accounts_with_proportioned_balances( - weights_and_accounts, - proportional_fragment_of_cw_balance, - multiplication_coeff, - ); - - // coefficients used reversely on purpose - let ok_former_balance = proposed_adjusted_balance - * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; - let expected_decided_payable_account = ReversiblePayableAccount { - adjusted_account: PayableAccount { - balance_wei: 500_000, - ..payable_account_1 - }, - former_balance: ok_former_balance, - }; - let bad_former_balance = ok_former_balance + 1; - let expected_disqualified_account = DisqualifiedPayableAccount { - wallet: payable_account_2.wallet, - proposed_adjusted_balance: 500_000, - original_balance: bad_former_balance, - }; - assert_eq!(decided_accounts, vec![expected_decided_payable_account]); - assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) + todo!("fix me") + // let mut payable_account_1 = make_payable_account(1); + // payable_account_1.balance_wei = 1_000_000; + // let mut payable_account_2 = make_payable_account(2); + // payable_account_2.balance_wei = 1_000_001; + // let proportional_fragment_of_cw_balance = 200_000; + // let multiplication_coeff = 10; + // let proposed_adjusted_balance = 500_000; + // let criterion = + // proposed_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 + // let weights_and_accounts = vec![ + // (criterion, payable_account_1.clone()), + // (criterion, payable_account_2.clone()), + // ]; + // + // let (decided_accounts, disqualified_accounts) = + // PaymentAdjusterReal::recreate_accounts_with_proportioned_balances( + // weights_and_accounts, + // proportional_fragment_of_cw_balance, + // multiplication_coeff, + // ); + // + // // coefficients used reversely on purpose + // let ok_former_balance = proposed_adjusted_balance + // * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + // / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + // let expected_decided_payable_account = ReversiblePayableAccount { + // adjusted_account: PayableAccount { + // balance_wei: 500_000, + // ..payable_account_1 + // }, + // former_balance: ok_former_balance, + // }; + // let bad_former_balance = ok_former_balance + 1; + // let expected_disqualified_account = DisqualifiedPayableAccount { + // wallet: payable_account_2.wallet, + // proposed_adjusted_balance: 500_000, + // original_balance: bad_former_balance, + // }; + // assert_eq!(decided_accounts, vec![expected_decided_payable_account]); + // assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) } #[test] @@ -1384,7 +1505,7 @@ mod tests { let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; - let result = subject.run_adjustment_in_recursion(vec![], qualified_payables.clone()); + let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important let total = balance_1 * 10_000 + balance_2 * 10_000; @@ -1420,133 +1541,135 @@ mod tests { #[test] fn try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less( ) { - let test_name = "try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less"; - let now = SystemTime::now(); - let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; - let mut subject = make_initialized_subject( - now, - Some(consuming_wallet_balance), - Some(Logger::new(test_name)), - ); - let account_1_made_up_criteria = 18_000_000_000_000; - let account_1 = PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 1_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(200000)).unwrap(), - pending_payable_opt: None, - }; - let account_2_made_up_criteria = 5_000_000_000_000; - let account_2 = PayableAccount { - wallet: make_wallet("booga"), - balance_wei: 8_000_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - let required_balance_total = 1_000_000_000_000 + 333 + 8_000_000_000_000_000; - let criteria_total = account_1_made_up_criteria + account_2_made_up_criteria; - let accounts_with_individual_criteria = vec![ - (account_1_made_up_criteria, account_1.clone()), - (account_2_made_up_criteria, account_2.clone()), - ]; - - let (outweighed, remaining) = subject - .try_separating_outweighed_accounts( - accounts_with_individual_criteria, - required_balance_total, - criteria_total, - ) - .right() - .unwrap(); - - let mut expected_account = account_1; - expected_account.balance_wei = 1_000_000_000_000 - 1; - assert_eq!(outweighed, vec![expected_account]); - assert_eq!(remaining, vec![account_2]) + todo!("fix me") + // let test_name = "try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less"; + // let now = SystemTime::now(); + // let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; + // let mut subject = make_initialized_subject( + // now, + // Some(consuming_wallet_balance), + // Some(Logger::new(test_name)), + // ); + // let account_1_made_up_criteria = 18_000_000_000_000; + // let account_1 = PayableAccount { + // wallet: make_wallet("blah"), + // balance_wei: 1_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(200000)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2_made_up_criteria = 5_000_000_000_000; + // let account_2 = PayableAccount { + // wallet: make_wallet("booga"), + // balance_wei: 8_000_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + // pending_payable_opt: None, + // }; + // let required_balance_total = 1_000_000_000_000 + 333 + 8_000_000_000_000_000; + // let criteria_total = account_1_made_up_criteria + account_2_made_up_criteria; + // let accounts_with_individual_criteria = vec![ + // (account_1_made_up_criteria, account_1.clone()), + // (account_2_made_up_criteria, account_2.clone()), + // ]; + // + // let (outweighed, remaining) = subject + // .handle_possibly_outweighed_account( + // accounts_with_individual_criteria, + // required_balance_total, + // criteria_total, + // ) + // .right() + // .unwrap(); + // + // let mut expected_account = account_1; + // expected_account.balance_wei = 1_000_000_000_000 - 1; + // assert_eq!(outweighed, vec![expected_account]); + // assert_eq!(remaining, vec![account_2]) } #[test] fn try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum( ) { - let test_name = "try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum"; - let now = SystemTime::now(); - let consuming_wallet_balance = 90_000_000_000_000; - let mut subject = make_initialized_subject( - now, - Some(consuming_wallet_balance), - Some(Logger::new(test_name)), - ); - let balance_1 = 150_000_000_000_000; - let account_1 = PayableAccount { - wallet: make_wallet("blah"), - balance_wei: balance_1, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(170000)) - .unwrap(), - pending_payable_opt: None, - }; - let balance_2 = 300_000_000_000_000; - let account_2 = PayableAccount { - wallet: make_wallet("booga"), - balance_wei: balance_2, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(170000)) - .unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("giraffe"), - balance_wei: 800_000_000_000_000_000, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(1000)) - .unwrap(), - pending_payable_opt: None, - }; - let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ - account_1.clone(), - account_2.clone(), - account_3.clone(), - ]); - let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); - let required_balance_total = - PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); - let criteria_total = - PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); - - let (outweighed, remaining) = subject - .try_separating_outweighed_accounts( - accounts_with_individual_criteria.clone(), - required_balance_total, - criteria_total, - ) - .right() - .unwrap(); - - // the algorithm first isolates the two first outweighed accounts and because - // they are more than one it will apply the standard adjustment strategy according to their criteria, - // with one big difference, now the third account will stay out - let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria - .iter() - .take(2) - .map(|(criteria_sum, _)| *criteria_sum) - .collect(); - let expected_balances = compute_expected_balanced_portions_from_criteria( - individual_criteria_for_just_two_accounts, - consuming_wallet_balance, - ); - // //making sure the proposed balances are non zero - // assert!(expected_balances[0] > (balance_1 / 4)); - // assert!(expected_balances[1] > (balance_2 / 4)); - // let check_sum = expected_balances.iter().sum(); - // assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); - let expected_outweighed_accounts = { - let mut account_1 = account_1; - account_1.balance_wei = expected_balances[0]; - let mut account_2 = account_2; - account_2.balance_wei = expected_balances[1]; - vec![account_1, account_2] - }; - assert_eq!(outweighed, expected_outweighed_accounts); - assert_eq!(remaining, vec![account_3]) + todo!("fix me") + // let test_name = "try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum"; + // let now = SystemTime::now(); + // let consuming_wallet_balance = 90_000_000_000_000; + // let mut subject = make_initialized_subject( + // now, + // Some(consuming_wallet_balance), + // Some(Logger::new(test_name)), + // ); + // let balance_1 = 150_000_000_000_000; + // let account_1 = PayableAccount { + // wallet: make_wallet("blah"), + // balance_wei: balance_1, + // last_paid_timestamp: SystemTime::now() + // .checked_sub(Duration::from_secs(170000)) + // .unwrap(), + // pending_payable_opt: None, + // }; + // let balance_2 = 300_000_000_000_000; + // let account_2 = PayableAccount { + // wallet: make_wallet("booga"), + // balance_wei: balance_2, + // last_paid_timestamp: SystemTime::now() + // .checked_sub(Duration::from_secs(170000)) + // .unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("giraffe"), + // balance_wei: 800_000_000_000_000_000, + // last_paid_timestamp: SystemTime::now() + // .checked_sub(Duration::from_secs(1000)) + // .unwrap(), + // pending_payable_opt: None, + // }; + // let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ + // account_1.clone(), + // account_2.clone(), + // account_3.clone(), + // ]); + // let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); + // let required_balance_total = + // PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); + // let criteria_total = + // PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); + // + // let (outweighed, remaining) = subject + // .handle_possibly_outweighed_account( + // accounts_with_individual_criteria.clone(), + // required_balance_total, + // criteria_total, + // ) + // .right() + // .unwrap(); + // + // // the algorithm first isolates the two first outweighed accounts and because + // // they are more than one it will apply the standard adjustment strategy according to their criteria, + // // with one big difference, now the third account will stay out + // let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria + // .iter() + // .take(2) + // .map(|(criteria_sum, _)| *criteria_sum) + // .collect(); + // let expected_balances = compute_expected_balanced_portions_from_criteria( + // individual_criteria_for_just_two_accounts, + // consuming_wallet_balance, + // ); + // // //making sure the proposed balances are non zero + // // assert!(expected_balances[0] > (balance_1 / 4)); + // // assert!(expected_balances[1] > (balance_2 / 4)); + // // let check_sum = expected_balances.iter().sum(); + // // assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); + // let expected_outweighed_accounts = { + // let mut account_1 = account_1; + // account_1.balance_wei = expected_balances[0]; + // let mut account_2 = account_2; + // account_2.balance_wei = expected_balances[1]; + // vec![account_1, account_2] + // }; + // assert_eq!(outweighed, expected_outweighed_accounts); + // assert_eq!(remaining, vec![account_3]) } #[test] From 8f034ee5e5f8a50a0ed5b7cd13cc73dd067f06cf Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 24 Jul 2023 10:47:23 +0200 Subject: [PATCH 045/250] GH-711: interim commit --- .../payment_adjuster/diagnostics.rs | 4 + node/src/accountant/payment_adjuster/mod.rs | 441 ++++++++++-------- 2 files changed, 246 insertions(+), 199 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 5aca37fe9..0b1473cfc 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -48,7 +48,11 @@ pub fn compute_progressive_characteristics( let different_values_for_chief_parameter: Vec<(u128, u32)> = vec![ (10, 1), (10, 3), + (10, 4), + (10, 5), (10, 6), + (10, 7), + (10, 8), (10, 9), (10, 12), (10, 15), diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e12ae3ee1..6dabae07f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -133,15 +133,15 @@ impl PaymentAdjuster for PaymentAdjusterReal { OutcomingPaymentsInstructions { accounts: adjusted_accounts, - response_skeleton_opt: msg.response_skeleton_opt, + response_skeleton_opt, } } implement_as_any!(); } -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; -const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = false; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; +const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = true; const AGE_EXPONENT: u32 = 4; const AGE_MULTIPLIER: u128 = 10; @@ -379,7 +379,7 @@ impl PaymentAdjusterReal { formula(approx_before) }, singleton_ref: &AGE_SINGLETON, - safe_count_of_examples_to_print: 4, + safe_count_of_examples_to_print: 8, } .perform() }); @@ -406,7 +406,7 @@ impl PaymentAdjusterReal { label: "BALANCE", diagnostics_adapted_formula: |x: u128| formula(x), singleton_ref: &BALANCE_SINGLETON, - safe_count_of_examples_to_print: 9, + safe_count_of_examples_to_print: 11, } .perform() }); @@ -458,31 +458,6 @@ impl PaymentAdjusterReal { ) -> AdjustmentIterationSummary { // let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); let criteria_total = Self::criteria_total(&accounts_with_individual_criteria); - - // match self.handle_possibly_outweighed_accounts( - // accounts_with_individual_criteria, - // required_balance_total, - // criteria_total, - // ) { - // Either::Left(accounts_with_individual_criteria) => { - self.perform_adjustment_and_determine_adjustment_iteration_result( - accounts_with_individual_criteria, - criteria_total, - ) - // } - // Either::Right((outweighed, remaining)) => AdjustmentIterationSummary { - // decided_accounts: outweighed, - // remaining_accounts: remaining, - // disqualified_accounts: vec![], - // }, - // } - } - - fn perform_adjustment_and_determine_adjustment_iteration_result( - &mut self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - criteria_total: u128, - ) -> AdjustmentIterationSummary { //TODO simplify this...one of these enums is probably redundant match self.recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, @@ -512,8 +487,33 @@ impl PaymentAdjusterReal { disqualified_accounts: vec![], }, } + // match self.handle_possibly_outweighed_accounts( + // accounts_with_individual_criteria, + // required_balance_total, + // criteria_total, + // ) { + // Either::Left(accounts_with_individual_criteria) => { + // self.perform_adjustment_and_determine_adjustment_iteration_result( + // accounts_with_individual_criteria, + // criteria_total, + // ) + // } + // Either::Right((outweighed, remaining)) => AdjustmentIterationSummary { + // decided_accounts: outweighed, + // remaining_accounts: remaining, + // disqualified_accounts: vec![], + // }, + // } } + // fn perform_adjustment_and_determine_adjustment_iteration_result( + // &mut self, + // accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + // criteria_total: u128, + // ) -> AdjustmentIterationSummary { + // + // } + fn recreate_accounts_with_proportioned_balances( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, @@ -553,18 +553,18 @@ impl PaymentAdjusterReal { }) .collect(); - let unchecked_for_outweighed = - match Self::consider_account_disqualification_from_percentage_insignificance( - accounts_with_unchecked_adjustment, - ) { + let unchecked_for_disqualified = + match self.handle_possibly_outweighed_account(accounts_with_unchecked_adjustment) { Left(still_not_fully_checked) => still_not_fully_checked, - Right(with_some_disqualified) => return with_some_disqualified, + Right(with_some_outweighed) => return with_some_outweighed, }; let finalized_accounts = - match self.handle_possibly_outweighed_account(unchecked_for_outweighed) { - Left(finalized_accounts) => finalized_accounts, - Right(with_some_outweighed) => return with_some_outweighed, + match Self::consider_account_disqualification_from_percentage_insignificance( + unchecked_for_disqualified, + ) { + Left(adjusted_accounts) => adjusted_accounts, + Right(with_some_disqualified) => return with_some_disqualified, }; AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) @@ -572,16 +572,20 @@ impl PaymentAdjusterReal { fn consider_account_disqualification_from_percentage_insignificance( accounts_with_unchecked_adjustment: Vec, - ) -> Either, AccountsRecreationResult> { - let wallets_of_accounts_to_disqualify: Vec<&Wallet> = accounts_with_unchecked_adjustment + ) -> Either, AccountsRecreationResult> { + let wallets_of_accounts_to_disqualify: Vec = accounts_with_unchecked_adjustment .iter() .flat_map(|account_info| { let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor); + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + eprintln!( + "proposed balance: {} --- balancea at edge: {}", + proposed_adjusted_balance, balance_at_the_edge + ); if proposed_adjusted_balance <= balance_at_the_edge { - Some(&account_info.original_account.wallet) + Some(account_info.original_account.wallet.clone()) } else { None // let disqualified_account = DisqualifiedPayableAccount::new( @@ -595,17 +599,48 @@ impl PaymentAdjusterReal { }) .collect(); if wallets_of_accounts_to_disqualify.is_empty() { - Left(accounts_with_unchecked_adjustment) + let finalized_accounts = AccountWithUncheckedAdjustment::finalize_collection( + accounts_with_unchecked_adjustment, + DecidedPayableAccountResolution::Finalize, + ); + Left(finalized_accounts) } else { - //TODO add console diagnostics here - todo!("create the account creation result") + let (disqualified, remaining): ( + Vec, + Vec, + ) = accounts_with_unchecked_adjustment + .into_iter() + .partition(|account_info| { + wallets_of_accounts_to_disqualify + .contains(&account_info.original_account.wallet) + }); + let debugable_disqualified = disqualified + .into_iter() + .map(|account_info| { + DisqualifiedPayableAccount::new( + account_info.original_account.wallet, + account_info.original_account.balance_wei, + account_info.proposed_adjusted_balance, + ) + }) + .collect(); + let remaining_stripped_off = remaining + .into_iter() + .map(|account_info| { + PayableAccount::from((account_info, DecidedPayableAccountResolution::Revert)) + }) + .collect(); + Right(AccountsRecreationResult::InsignificantAccounts { + disqualified: debugable_disqualified, + remaining: remaining_stripped_off, + }) } } fn handle_possibly_outweighed_account( &mut self, accounts_with_unchecked_adjustment: Vec, - ) -> Either, AccountsRecreationResult> { + ) -> Either, AccountsRecreationResult> { let init: ( Vec<(u128, PayableAccount)>, Vec, @@ -642,10 +677,7 @@ impl PaymentAdjusterReal { ); if outweighed_with_already_made_criteria.is_empty() { - Left(AccountWithUncheckedAdjustment::finalize_collection( - passing_through, - DecidedPayableAccountResolution::Finalize, - )) + Left(passing_through) } else { let clean_outweighed_accounts = self .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( @@ -868,6 +900,7 @@ fn log_2(x: u128) -> u32 { num_bits::() as u32 - x.leading_zeros() - 1 } +#[derive(Debug)] enum AccountsRecreationResult { AllAccountsCleanlyProcessed(Vec), InsignificantAccounts { @@ -880,16 +913,6 @@ enum AccountsRecreationResult { }, } -impl AccountsRecreationResult { - fn add_ok_adjusted_account(mut self, adjusted_account: PayableAccount) -> Self { - match self { - AccountsRecreationResult::AllAccountsCleanlyProcessed(_) => todo!(), - AccountsRecreationResult::InsignificantAccounts { .. } => todo!(), - AccountsRecreationResult::OutweighedAccounts { .. } => todo!(), - } - } -} - #[derive(Debug)] struct AdjustmentIterationSummary { decided_accounts: Vec, @@ -1022,9 +1045,10 @@ mod tests { PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::{ - log_10, log_2, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, - PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - AGE_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, + log_10, log_2, AccountWithUncheckedAdjustment, AccountsRecreationResult, Adjustment, + AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, + PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_EXPONENT, + AGE_MULTIPLIER, BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, BALANCE_TAIL_WEIGHT_EXPONENT, BALANCE_TAIL_WEIGHT_MASK, BALANCE_TAIL_WEIGHT_MULTIPLIER, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; @@ -1359,8 +1383,6 @@ mod tests { let balance_wei_for_each_account = *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 1000; let different_accounts_with_criteria = get_extreme_criteria(account_debt_ages_in_months, balance_wei_for_each_account); - let final_criteria_total = - PaymentAdjusterReal::criteria_total(&different_accounts_with_criteria); let cw_balance_in_minor = 1; let results = different_accounts_with_criteria @@ -1393,49 +1415,70 @@ mod tests { } #[test] - fn recreate_accounts_with_proportioned_balances_adheres_to_the_manifested_edge_of_insignificance( + fn consider_account_disqualification_from_percentage_insignificance_adheres_to_the_manifest_consts_of_insignificance( ) { - todo!("fix me") - // let mut payable_account_1 = make_payable_account(1); - // payable_account_1.balance_wei = 1_000_000; - // let mut payable_account_2 = make_payable_account(2); - // payable_account_2.balance_wei = 1_000_001; - // let proportional_fragment_of_cw_balance = 200_000; - // let multiplication_coeff = 10; - // let proposed_adjusted_balance = 500_000; - // let criterion = - // proposed_adjusted_balance * multiplication_coeff / proportional_fragment_of_cw_balance; // = 25 - // let weights_and_accounts = vec![ - // (criterion, payable_account_1.clone()), - // (criterion, payable_account_2.clone()), - // ]; - // - // let (decided_accounts, disqualified_accounts) = - // PaymentAdjusterReal::recreate_accounts_with_proportioned_balances( - // weights_and_accounts, - // proportional_fragment_of_cw_balance, - // multiplication_coeff, - // ); - // - // // coefficients used reversely on purpose - // let ok_former_balance = proposed_adjusted_balance - // * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - // / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; - // let expected_decided_payable_account = ReversiblePayableAccount { - // adjusted_account: PayableAccount { - // balance_wei: 500_000, - // ..payable_account_1 - // }, - // former_balance: ok_former_balance, - // }; - // let bad_former_balance = ok_former_balance + 1; - // let expected_disqualified_account = DisqualifiedPayableAccount { - // wallet: payable_account_2.wallet, - // proposed_adjusted_balance: 500_000, - // original_balance: bad_former_balance, - // }; - // assert_eq!(decided_accounts, vec![expected_decided_payable_account]); - // assert_eq!(disqualified_accounts, vec![expected_disqualified_account]) + let cw_masq_balance = 1_000_000; + let mut subject = make_initialized_subject(SystemTime::now(), Some(cw_masq_balance), None); + let account_balance = 1_000_000; + let prepare_account = |n: u64| { + let mut account = make_payable_account(n); + account.balance_wei = account_balance; + account + }; + let payable_account_1 = prepare_account(1); + let wallet_1 = payable_account_1.wallet.clone(); + let payable_account_2 = prepare_account(2); + let wallet_2 = payable_account_2.wallet.clone(); + let payable_account_3 = prepare_account(3); + let wallet_3 = payable_account_3.wallet.clone(); + const IRRELEVANT_CRITERIA_SUM: u128 = 1111; + let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + let proposed_ok_balance = edge + 1; + let account_info_1 = AccountWithUncheckedAdjustment::new( + payable_account_1, + proposed_ok_balance, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_equal = edge; + let account_info_2 = AccountWithUncheckedAdjustment::new( + payable_account_2, + proposed_bad_balance_because_equal, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_smaller = edge - 1; + let account_info_3 = AccountWithUncheckedAdjustment::new( + payable_account_3, + proposed_bad_balance_because_smaller, + IRRELEVANT_CRITERIA_SUM, + ); + let accounts_with_unchecked_adjustment = + vec![account_info_1, account_info_2, account_info_3]; + + let result = + PaymentAdjusterReal::consider_account_disqualification_from_percentage_insignificance( + accounts_with_unchecked_adjustment, + ) + .right() + .unwrap(); + + let (disqualified, remaining) = match result { + AccountsRecreationResult::InsignificantAccounts { + disqualified, + remaining, + } => (disqualified, remaining), + x => panic!( + "we expected some disqualified accounts but got this: {:?}", + x + ), + }; + let expected_disqualified_accounts = vec![wallet_2, wallet_3]; + disqualified.iter().for_each(|account_info| { + assert!(expected_disqualified_accounts.contains(&account_info.wallet)) + }); + assert_eq!(remaining[0].wallet, wallet_1); + assert_eq!(disqualified.len(), 2); + assert_eq!(remaining.len(), 1); } #[test] @@ -1478,14 +1521,6 @@ mod tests { let now = SystemTime::now(); let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000; let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); - // let collected_setup_data = FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // gas_currency_wei: U256::from(u128::MAX), - // masq_tokens_wei: U256::from(1_500_000_000_000_u64 - 25_000_000), - // }, - // desired_gas_price_gwei: 50, - // estimated_gas_limit_per_transaction: 55_000, - // }; let balance_1 = 1_500_000_000_000; let balance_2 = 25_000_000; let wallet_1 = make_wallet("blah"); @@ -1587,89 +1622,97 @@ mod tests { } #[test] - fn try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum( - ) { - todo!("fix me") - // let test_name = "try_separating_outweighed_accounts_cuts_back_multiple_outweighed_accounts_if_cw_balance_is_even_less_than_their_sum"; - // let now = SystemTime::now(); - // let consuming_wallet_balance = 90_000_000_000_000; - // let mut subject = make_initialized_subject( - // now, - // Some(consuming_wallet_balance), - // Some(Logger::new(test_name)), - // ); - // let balance_1 = 150_000_000_000_000; - // let account_1 = PayableAccount { - // wallet: make_wallet("blah"), - // balance_wei: balance_1, - // last_paid_timestamp: SystemTime::now() - // .checked_sub(Duration::from_secs(170000)) - // .unwrap(), - // pending_payable_opt: None, - // }; - // let balance_2 = 300_000_000_000_000; - // let account_2 = PayableAccount { - // wallet: make_wallet("booga"), - // balance_wei: balance_2, - // last_paid_timestamp: SystemTime::now() - // .checked_sub(Duration::from_secs(170000)) - // .unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("giraffe"), - // balance_wei: 800_000_000_000_000_000, - // last_paid_timestamp: SystemTime::now() - // .checked_sub(Duration::from_secs(1000)) - // .unwrap(), - // pending_payable_opt: None, - // }; - // let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ - // account_1.clone(), - // account_2.clone(), - // account_3.clone(), - // ]); - // let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); - // let required_balance_total = - // PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); - // let criteria_total = - // PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); - // - // let (outweighed, remaining) = subject - // .handle_possibly_outweighed_account( - // accounts_with_individual_criteria.clone(), - // required_balance_total, - // criteria_total, - // ) - // .right() - // .unwrap(); - // - // // the algorithm first isolates the two first outweighed accounts and because - // // they are more than one it will apply the standard adjustment strategy according to their criteria, - // // with one big difference, now the third account will stay out - // let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria - // .iter() - // .take(2) - // .map(|(criteria_sum, _)| *criteria_sum) - // .collect(); - // let expected_balances = compute_expected_balanced_portions_from_criteria( - // individual_criteria_for_just_two_accounts, - // consuming_wallet_balance, - // ); - // // //making sure the proposed balances are non zero - // // assert!(expected_balances[0] > (balance_1 / 4)); - // // assert!(expected_balances[1] > (balance_2 / 4)); - // // let check_sum = expected_balances.iter().sum(); - // // assert!( (consuming_wallet_balance / 100) * 99 <= check_sum && check_sum <= (consuming_wallet_balance / 100) * 101); - // let expected_outweighed_accounts = { - // let mut account_1 = account_1; - // account_1.balance_wei = expected_balances[0]; - // let mut account_2 = account_2; - // account_2.balance_wei = expected_balances[1]; - // vec![account_1, account_2] - // }; - // assert_eq!(outweighed, expected_outweighed_accounts); - // assert_eq!(remaining, vec![account_3]) + fn even_outweighed_accounts_get_their_balances_reduced_if_cw_balance_is_less_than_their_sum() { + const SECONDS_IN_3_DAYS: u64 = 259_200; + let test_name = "even_outweighed_accounts_get_their_balances_reduced_if_cw_balance_is_less_than_their_sum"; + let now = SystemTime::now(); + let consuming_wallet_balance = 400_000_000_000_000; + let mut subject = make_initialized_subject( + now, + Some(consuming_wallet_balance), + Some(Logger::new(test_name)), + ); + let balance_1 = 150_000_000_000_000; + let account_1 = PayableAccount { + wallet: make_wallet("blah"), + balance_wei: balance_1, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) + .unwrap(), + pending_payable_opt: None, + }; + let balance_2 = 900_000_000_000_000; + let account_2 = PayableAccount { + wallet: make_wallet("booga"), + balance_wei: balance_2, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) + .unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("giraffe"), + balance_wei: 800_000_000_000_000_000, + last_paid_timestamp: SystemTime::now() + .checked_sub(Duration::from_secs(1000)) + .unwrap(), + pending_payable_opt: None, + }; + let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ + account_1.clone(), + account_2.clone(), + account_3.clone(), + ]); + let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); + let required_balance_total = + PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); + let criteria_total = + PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); + + let result = subject.recreate_accounts_with_proportioned_balances( + accounts_with_individual_criteria.clone(), + criteria_total, + ); + + let (outweighed, remaining) = match result { + AccountsRecreationResult::OutweighedAccounts { + outweighed, + remaining, + } => (outweighed, remaining), + x => panic!( + "we expected to see some outweighed accounts bu got: {:?}", + x + ), + }; + // the algorithm first isolates the two first outweighed accounts and because + // they are more than one it will apply the standard adjustment strategy according to their criteria, + // with one big difference, now the third account will stay out + let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria + .iter() + .take(2) + .map(|(criteria_sum, _)| *criteria_sum) + .collect(); + let expected_balances = compute_expected_balanced_portions_from_criteria( + individual_criteria_for_just_two_accounts, + consuming_wallet_balance, + ); + //making sure the proposed balances are non zero + assert!(expected_balances[0] > (balance_1 / 4)); + assert!(expected_balances[1] > (balance_2 / 4)); + let check_sum = expected_balances.iter().sum(); + assert!( + (consuming_wallet_balance / 100) * 99 <= check_sum + && check_sum <= (consuming_wallet_balance / 100) * 101 + ); + let expected_outweighed_accounts = { + let mut account_1 = account_1; + account_1.balance_wei = expected_balances[0]; + let mut account_2 = account_2; + account_2.balance_wei = expected_balances[1]; + vec![account_1, account_2] + }; + assert_eq!(outweighed, expected_outweighed_accounts); + assert_eq!(remaining, vec![account_3]) } #[test] From 60d2d2ade203370ba7c0e0822aef57fe145d2afd Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 24 Jul 2023 11:10:29 +0200 Subject: [PATCH 046/250] GH-711: better diagnostics layout; before testing a diff age formula --- .../payment_adjuster/diagnostics.rs | 83 ++++++++----------- node/src/accountant/payment_adjuster/mod.rs | 33 ++++---- 2 files changed, 53 insertions(+), 63 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 0b1473cfc..495f9cbc3 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -10,6 +10,7 @@ use thousands::Separable; pub static AGE_SINGLETON: Once = Once::new(); pub static BALANCE_SINGLETON: Once = Once::new(); +const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18]; pub fn diagnostics(account: &Wallet, description: &str, value_renderer: F) where @@ -35,44 +36,6 @@ pub fn diagnostics_collective(label: &str, accounts: &[PayableAccount]) { } } -pub fn compute_progressive_characteristics( - label: &str, - formula: F, - singleton: &Once, - examples_count: usize, -) where - F: Fn(u128) -> u128, -{ - if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { - singleton.call_once(|| { - let different_values_for_chief_parameter: Vec<(u128, u32)> = vec![ - (10, 1), - (10, 3), - (10, 4), - (10, 5), - (10, 6), - (10, 7), - (10, 8), - (10, 9), - (10, 12), - (10, 15), - (10, 18), - ]; - - eprintln!("{}", label); - different_values_for_chief_parameter - .into_iter() - .take(examples_count) - .map(|(base, factor)| base.pow(factor)) - .for_each(|num| { - let value = formula(num); - eprintln!("x: {: where F: Fn(u128) -> u128, @@ -80,12 +43,39 @@ where pub account: PayableAccount, pub criterion: u128, pub criteria_sum_so_far: u128, - // below only diagnostics purposes + pub diagnostics: DiagnosticsSetting<'a, F>, +} + +pub struct DiagnosticsSetting<'a, F> +where + F: Fn(u128) -> u128, +{ pub label: &'static str, - pub diagnostics_adapted_formula: F, + pub diagnostics_adaptive_formula: F, pub singleton_ref: &'a Once, - // max 9 - pub safe_count_of_examples_to_print: usize, + pub bonds_safe_count_to_print: usize, +} + +impl<'a, F> DiagnosticsSetting<'a, F> +where + F: Fn(u128) -> u128, +{ + pub fn compute_progressive_characteristics(&self) { + if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { + self.singleton_ref.call_once(|| { + eprintln!("CHARACTERISTICS FOR {} FORMULA", self.label); + EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS + .into_iter() + .take(self.bonds_safe_count_to_print) + .map(|exponent| 10_u128.pow(exponent)) + .for_each(|num| { + let value = (self.diagnostics_adaptive_formula)(num); + eprintln!("x: {: FinalizationAndDiagnostics<'_, F> @@ -95,15 +85,10 @@ where pub fn perform(self) -> (u128, PayableAccount) { diagnostics( &self.account.wallet, - &format!("COMPUTED {} CRITERIA", self.label), + &format!("COMPUTED {} CRITERIA", self.diagnostics.label), || self.criterion.separate_with_commas(), ); - compute_progressive_characteristics( - &format!("CHARACTERISTICS FOR {} FORMULA", self.label), - self.diagnostics_adapted_formula, - self.singleton_ref, - self.safe_count_of_examples_to_print, - ); + self.diagnostics.compute_progressive_characteristics(); ( self.criteria_sum_so_far diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 6dabae07f..014ae76a4 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -6,8 +6,8 @@ mod log_functions; use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics::{ - diagnostics, diagnostics_collective, FinalizationAndDiagnostics, AGE_SINGLETON, - BALANCE_SINGLETON, + diagnostics, diagnostics_collective, DiagnosticsSetting, FinalizationAndDiagnostics, + AGE_SINGLETON, BALANCE_SINGLETON, }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -371,15 +371,18 @@ impl PaymentAdjusterReal { account, criterion, criteria_sum_so_far, - label: "AGE", - diagnostics_adapted_formula: |x: u128| { - let approx_before = SystemTime::now() - .checked_sub(Duration::from_secs(x as u64)) - .expect("characteristics for age blew up"); - formula(approx_before) + diagnostics: DiagnosticsSetting { + label: "AGE", + diagnostics_adaptive_formula: |x: u128| { + let secs_in_the_past = Duration::from_secs(x as u64); + let approx_time_anchor = SystemTime::now() + .checked_sub(secs_in_the_past) + .expect("age formula characteristics blew up"); + formula(approx_time_anchor) + }, + singleton_ref: &AGE_SINGLETON, + bonds_safe_count_to_print: 8, }, - singleton_ref: &AGE_SINGLETON, - safe_count_of_examples_to_print: 8, } .perform() }); @@ -403,10 +406,12 @@ impl PaymentAdjusterReal { account, criterion, criteria_sum_so_far, - label: "BALANCE", - diagnostics_adapted_formula: |x: u128| formula(x), - singleton_ref: &BALANCE_SINGLETON, - safe_count_of_examples_to_print: 11, + diagnostics: DiagnosticsSetting { + label: "BALANCE", + diagnostics_adaptive_formula: |x: u128| formula(x), + singleton_ref: &BALANCE_SINGLETON, + bonds_safe_count_to_print: 11, + }, } .perform() }); From fa0d5d84089fc665f5c67f0211121b8b9f11c067 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 25 Jul 2023 00:50:21 +0200 Subject: [PATCH 047/250] GH-711: continuing in batling with the wind mills :-) --- .../payment_adjuster/diagnostics.rs | 62 ++- node/src/accountant/payment_adjuster/inner.rs | 6 +- node/src/accountant/payment_adjuster/mod.rs | 467 +++++++++--------- 3 files changed, 286 insertions(+), 249 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 495f9cbc3..051cf8b18 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -5,24 +5,47 @@ use crate::accountant::payment_adjuster::{ COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::sub_lib::wallet::Wallet; +use itertools::Either; use std::sync::Once; use thousands::Separable; pub static AGE_SINGLETON: Once = Once::new(); pub static BALANCE_SINGLETON: Once = Once::new(); -const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18]; +pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 13] = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21]; -pub fn diagnostics(account: &Wallet, description: &str, value_renderer: F) +pub const fn diagnostics_x_axis_exponents_len() -> usize { + EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len() +} +pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 40; + +#[macro_export] +macro_rules! diagnostics { + ($description: literal, $value_renderer: expr) => { + diagnostics(|| Either::Left(""), $description, $value_renderer) + }; + ($wallet_ref: expr, $description: expr, $value_renderer: expr) => { + diagnostics( + || Either::Right($wallet_ref.to_string()), + $description, + $value_renderer, + ) + }; +} + +pub fn diagnostics(subject_renderer: F1, description: &str, value_renderer: F2) where - F: Fn() -> String, + F1: Fn() -> Either<&'static str, String>, + F2: Fn() -> String, { if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { eprintln!( - "{} {: +//TODO kill this when you have CriteriaCumputers that can take characteristics tests on them +pub struct CriteriaWithDiagnostics<'a, F> where F: Fn(u128) -> u128, { @@ -68,9 +92,14 @@ where .into_iter() .take(self.bonds_safe_count_to_print) .map(|exponent| 10_u128.pow(exponent)) - .for_each(|num| { - let value = (self.diagnostics_adaptive_formula)(num); - eprintln!("x: {: FinalizationAndDiagnostics<'_, F> +impl CriteriaWithDiagnostics<'_, F> where F: Fn(u128) -> u128, { - pub fn perform(self) -> (u128, PayableAccount) { - diagnostics( - &self.account.wallet, - &format!("COMPUTED {} CRITERIA", self.diagnostics.label), - || self.criterion.separate_with_commas(), - ); + pub fn diagnose_and_sum(self) -> (u128, PayableAccount) { + let account = &self.account.wallet; + let description = format!("COMPUTED {} CRITERIA", self.diagnostics.label); + let value_renderer = || self.criterion.separate_with_commas(); + diagnostics!(account, &description, value_renderer); self.diagnostics.compute_progressive_characteristics(); ( diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 62eee7165..eb14d1702 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -47,7 +47,11 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { } fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { - self.cw_masq_balance -= subtrahend + let lowered_theoretical_cw_balance = self + .cw_masq_balance + .checked_sub(subtrahend) + .expect("should always subtract a small enough amount"); + self.cw_masq_balance = lowered_theoretical_cw_balance } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 014ae76a4..d939811f3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -6,8 +6,8 @@ mod log_functions; use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics::{ - diagnostics, diagnostics_collective, DiagnosticsSetting, FinalizationAndDiagnostics, - AGE_SINGLETON, BALANCE_SINGLETON, + diagnostics, diagnostics_collective, diagnostics_x_axis_exponents_len, CriteriaWithDiagnostics, + DiagnosticsSetting, AGE_SINGLETON, BALANCE_SINGLETON, }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -21,6 +21,7 @@ use crate::accountant::scanners::payable_scan_setup_msgs::{ }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::{gwei_to_wei, wei_to_gwei}; +use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; @@ -140,18 +141,22 @@ impl PaymentAdjuster for PaymentAdjusterReal { implement_as_any!(); } -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = true; -const AGE_EXPONENT: u32 = 4; -const AGE_MULTIPLIER: u128 = 10; +const AGE_MAIN_EXPONENT: u32 = 3; +// divisor^(numerator/denominator) +const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; +const AGE_MULTIPLIER: u128 = 150; +const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; +const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; +const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; // this parameter affects the steepness (sensitivity on increase in balance) -const BALANCE_LOG_2_BASE_MULTIPLIER: u128 = 9; -// this parameter affects proportion against the different criteria class -const BALANCE_OUTER_MULTIPLIER: u128 = 2; -const BALANCE_TAIL_WEIGHT_MASK: u128 = 0xFFF; +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; +const BALANCE_TAIL_WEIGHT_MODULO_OPERAND: u128 = 1_000; const BALANCE_TAIL_WEIGHT_EXPONENT: u32 = 2; -const BALANCE_TAIL_WEIGHT_MULTIPLIER: u128 = 3; // represents 50% const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = PercentageAccountInsignificance { @@ -349,25 +354,27 @@ impl PaymentAdjusterReal { //caution: always remember to use checked math operations! - let age_criteria_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { + let age_criterion_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { let formula = |last_paid_timestamp: SystemTime| { - let elapsed_sec: u64 = self + let elapsed_secs: u64 = self .inner .now() .duration_since(last_paid_timestamp) .expect("time traveller") .as_secs(); - let divisor = (elapsed_sec as f64).sqrt().ceil() as u128; - (elapsed_sec as u128) - .checked_pow(AGE_EXPONENT) + let divisor = Self::compute_divisor(elapsed_secs); + let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) .unwrap_or(u128::MAX) //TODO sensible and tested ???? .checked_div(divisor) .expect("div overflow") - * AGE_MULTIPLIER + .checked_mul(log_multiplier) + .expect("mul overflow") }; let criterion = formula(account.last_paid_timestamp); - FinalizationAndDiagnostics { + CriteriaWithDiagnostics { account, criterion, criteria_sum_so_far, @@ -381,28 +388,27 @@ impl PaymentAdjusterReal { formula(approx_time_anchor) }, singleton_ref: &AGE_SINGLETON, - bonds_safe_count_to_print: 8, + bonds_safe_count_to_print: 10, }, } - .perform() + .diagnose_and_sum() }); - let balance_criteria_closure: CriterionFormula = + let balance_criterion_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { // constants used to keep the weights of balance and time balanced let formula = |balance_wei: u128| { - let binary_weight = log_2(balance_wei * BALANCE_LOG_2_BASE_MULTIPLIER); - let multiplier = (binary_weight as u128) * BALANCE_OUTER_MULTIPLIER; - let multiplied = balance_wei.checked_mul(multiplier).expect("mul overflow"); - let tail_weight = Self::balance_tail_weight(balance_wei); - eprintln!("tail weight {}", tail_weight); - let tail_weight_stressed = tail_weight.pow(BALANCE_TAIL_WEIGHT_EXPONENT) - * BALANCE_TAIL_WEIGHT_MULTIPLIER; - eprintln!("tail weight stressed {}", tail_weight_stressed); - multiplied + tail_weight_stressed + let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); + let multiplied = balance_wei + .checked_mul(binary_weight as u128) + .expect("mul overflow"); + let tail_weight = 0; + // if balance_wei > 1_000_000_000 {Self::balance_tail_weight(balance_wei)} else {1}; //TODO untested!!! + //eprintln!("tail weigh {}", tail_weight); + multiplied + tail_weight }; let criterion = formula(account.balance_wei); - FinalizationAndDiagnostics { + CriteriaWithDiagnostics { account, criterion, criteria_sum_so_far, @@ -410,22 +416,47 @@ impl PaymentAdjusterReal { label: "BALANCE", diagnostics_adaptive_formula: |x: u128| formula(x), singleton_ref: &BALANCE_SINGLETON, - bonds_safe_count_to_print: 11, + bonds_safe_count_to_print: diagnostics_x_axis_exponents_len(), }, } - .perform() + .diagnose_and_sum() }); let weights_and_accounts = accounts_with_zero_criteria - .map(age_criteria_closure) - .map(balance_criteria_closure); + .map(age_criterion_closure) + .map(balance_criterion_closure); Self::sort_in_descendant_order_by_weights(weights_and_accounts) } + //TODO this fn should later become property of the balance criteria computing class e.g. "CriteriaComputer" //this enables to sense also small differences fn balance_tail_weight(balance_wei: u128) -> u128 { - BALANCE_TAIL_WEIGHT_MASK - (balance_wei & BALANCE_TAIL_WEIGHT_MASK) + (balance_wei % BALANCE_TAIL_WEIGHT_MODULO_OPERAND).pow(BALANCE_TAIL_WEIGHT_EXPONENT) + } + + //TODO this fn should later become property of the age criteria computing class e.g. "CriteriaComputer" + fn compute_divisor(elapsed_sec: u64) -> u128 { + (elapsed_sec as f64).sqrt().ceil() as u128 + } + + //TODO this fn should later become property of the age criteria computing class e.g. "CriteriaComputer" + fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { + let fast_growing_argument = (elapsed_secs as u128) + .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) + .expect("pow blew up") as f64; + let log = fast_growing_argument.ln(); + let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) + * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; + let final_log_multiplier = (log_stressed + / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) + .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); + x_or_1(final_log_multiplier) + } + + //TODO this fn should later become property of the balance criteria computing class e.g. "CriteriaComputer" + fn compute_binary_argument(balance_wei: u128) -> u128 { + x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) } fn give_job_to_adjustment_workers( @@ -511,14 +542,6 @@ impl PaymentAdjusterReal { // } } - // fn perform_adjustment_and_determine_adjustment_iteration_result( - // &mut self, - // accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - // criteria_total: u128, - // ) -> AdjustmentIterationSummary { - // - // } - fn recreate_accounts_with_proportioned_balances( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, @@ -547,7 +570,7 @@ impl PaymentAdjusterReal { / multiplication_coeff_u256) .as_u128(); - diagnostics(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { + diagnostics!(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { proposed_adjusted_balance.separate_with_commas() }); AccountWithUncheckedAdjustment::new( @@ -574,7 +597,7 @@ impl PaymentAdjusterReal { AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) } - + //100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000 fn consider_account_disqualification_from_percentage_insignificance( accounts_with_unchecked_adjustment: Vec, ) -> Either, AccountsRecreationResult> { @@ -585,21 +608,19 @@ impl PaymentAdjusterReal { let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; - eprintln!( - "proposed balance: {} --- balancea at edge: {}", - proposed_adjusted_balance, balance_at_the_edge - ); if proposed_adjusted_balance <= balance_at_the_edge { + diagnostics!( + &account_info.original_account.wallet, + "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", + || format!( + "proposed adjusted balance: {}, qualification limit: {}", + proposed_adjusted_balance, balance_at_the_edge + ) + ); + Some(account_info.original_account.wallet.clone()) } else { None - // let disqualified_account = DisqualifiedPayableAccount::new( - // account.wallet, - // original_balance, - // proposed_adjusted_balance, - // ); - // disqualified.push(disqualified_account); - // (decided, disqualified) } }) .collect(); @@ -658,16 +679,14 @@ impl PaymentAdjusterReal { > account_info.original_account.balance_wei //TODO test the operator against <= { - diagnostics( + diagnostics!( &account_info.original_account.wallet, "OUTWEIGHED ACCOUNT FOUND", - || { - format!( - "original balance: {}, proposed balance {}", - account_info.original_account.balance_wei, - account_info.proposed_adjusted_balance - ) - }, + || format!( + "original balance: {}, proposed balance {}", + account_info.original_account.balance_wei, + account_info.proposed_adjusted_balance + ) ); let outweighed_record = @@ -828,13 +847,13 @@ impl PaymentAdjusterReal { fn adjust_next_round_cw_balance_down(&mut self, processed_outweighed: &[PayableAccount]) { let subtrahend_total: u128 = Self::sum_as(processed_outweighed, |account| account.balance_wei); - eprintln!( - "cw masq balance {} to subtract {}", - self.inner.cw_masq_balance(), - subtrahend_total - ); - //TODO what happens if we choose one that will get us into negative when subtracted - self.inner.lower_remaining_cw_balance(subtrahend_total) + self.inner.lower_remaining_cw_balance(subtrahend_total); + + diagnostics!("LOWERED CW BALANCE", || format!( + "lowered by {} to {}", + subtrahend_total, + self.inner.cw_masq_balance() + )) } fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { @@ -853,10 +872,14 @@ impl PaymentAdjusterReal { const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; let criteria_sum_digits_count = log_10(criteria_sum); let cw_balance_digits_count = log_10(cw_masq_balance); - let smallest_mul_coeff_between = criteria_sum_digits_count + let positive_difference = criteria_sum_digits_count .checked_sub(cw_balance_digits_count) .unwrap_or(0); - let safe_mul_coeff = smallest_mul_coeff_between + EMPIRIC_PRECISION_COEFFICIENT; + let safe_mul_coeff = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; + eprintln!( + "criteria sum {}, safe mul coeff {}", + criteria_sum, safe_mul_coeff + ); 10_u128 .checked_pow(safe_mul_coeff as u32) .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) @@ -905,6 +928,14 @@ fn log_2(x: u128) -> u32 { num_bits::() as u32 - x.leading_zeros() - 1 } +fn x_or_1(x: u128) -> u128 { + if x == 0 { + 1 + } else { + x + } +} + #[derive(Debug)] enum AccountsRecreationResult { AllAccountsCleanlyProcessed(Vec), @@ -925,13 +956,6 @@ struct AdjustmentIterationSummary { disqualified_accounts: Vec, } -// #[derive(Debug, PartialEq, Eq)] -// struct ReversiblePayableAccount { -// adjusted_account: PayableAccount, -// former_balance: u128, -// } -// - //TODO rename??? #[derive(Clone, Copy)] enum DecidedPayableAccountResolution { @@ -944,16 +968,6 @@ enum AdjustmentCompletion { Continue(AdjustmentIterationSummary), } -// impl ReversiblePayableAccount { -// fn new(adjusted_account: PayableAccount, former_balance: u128) -> Self { -// Self { -// adjusted_account, -// former_balance, -// } -// } -// } -// - impl From<( AccountWithUncheckedAdjustment, @@ -1046,16 +1060,17 @@ pub enum AnalysisError { #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::{ log_10, log_2, AccountWithUncheckedAdjustment, AccountsRecreationResult, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, - PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_EXPONENT, - AGE_MULTIPLIER, BALANCE_LOG_2_BASE_MULTIPLIER, BALANCE_OUTER_MULTIPLIER, - BALANCE_TAIL_WEIGHT_EXPONENT, BALANCE_TAIL_WEIGHT_MASK, BALANCE_TAIL_WEIGHT_MULTIPLIER, - COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, + PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, + BALANCE_TAIL_WEIGHT_EXPONENT, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, + PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -1068,7 +1083,8 @@ mod tests { }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; - use itertools::Itertools; + use itertools::Either::Left; + use itertools::{Either, Itertools}; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; @@ -1145,10 +1161,10 @@ mod tests { fn constants_are_correct() { assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); assert_eq!(COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, false); - assert_eq!(AGE_EXPONENT, 4); + assert_eq!(AGE_MAIN_EXPONENT, 4); + assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); assert_eq!(AGE_MULTIPLIER, 10); - assert_eq!(BALANCE_LOG_2_BASE_MULTIPLIER, 9); - assert_eq!(BALANCE_OUTER_MULTIPLIER, 2); + assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 9); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, PercentageAccountInsignificance { @@ -1298,10 +1314,10 @@ mod tests { #[test] fn balance_tail_weight_works() { vec![ - (0xfff_u128 - 1, BALANCE_TAIL_WEIGHT_MASK - (0xfff - 1)), - (0xfff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), - (0x1fff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), - (0xfffff, BALANCE_TAIL_WEIGHT_MASK - 0xfff), + (12345678, 678_u128.pow(2)), + (5555, 555_u128.pow(2)), + (55, 55_u128.pow(2)), + (1, 1), ] .into_iter() .for_each(|(num, expected_result)| { @@ -1383,11 +1399,18 @@ mod tests { } #[test] - fn multiplication_coeff_showing_extreme_inputs_the_produced_values_and_ceiling() { - let account_debt_ages_in_months = vec![1, 5, 12, 120, 600]; - let balance_wei_for_each_account = *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 1000; + fn multiplication_coeff_showing_extreme_feeding_and_safety_ceiling() { + let account_debt_ages_in_months_and_balances = vec![ + (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (5, 10_u128.pow(18)), + (12, 10_u128.pow(18)), + (120, 10_u128.pow(20)), + (600, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), + ]; let different_accounts_with_criteria = - get_extreme_criteria(account_debt_ages_in_months, balance_wei_for_each_account); + get_extreme_criteria(Either::Right(account_debt_ages_in_months_and_balances)); let cw_balance_in_minor = 1; let results = different_accounts_with_criteria @@ -1405,11 +1428,13 @@ mod tests { assert_eq!( results, vec![ - 100000000000000000000000000000000000000, - 100000000000000000000000000000000000000, 100000000000000000000000000000000000, - 1000000000000000000000000000000000, - 100000000000000000000000000000000 + 100000000000000000000000000000000000, + 100000000000000000000000000000000000, + todo!("careful!! these are sorted criteria!!!...different from what you think they are"), + 10000000000000000000000000000000, + 1000000000000000000000000000000, + 1000000000000000000000000000000 ] ) // enough space for our counts; mostly we use it for division and multiplication and @@ -1521,6 +1546,63 @@ mod tests { assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } + #[test] + fn compute_divisor_works() { + let result: Vec<_> = [100, 81, 82, 80] + .into_iter() + .map(|secs| PaymentAdjusterReal::compute_divisor(secs)) + .collect(); + + assert_eq!(result, vec![10, 9, 10, 9]) + } + + #[test] + fn compute_descending_multiplier_works() { + let result: Vec<_> = EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS + .into_iter() + .map(|exp| 10_u64.pow(exp)) + .map(|seconds_elapsed| { + let divisor = PaymentAdjusterReal::compute_divisor(seconds_elapsed); + PaymentAdjusterReal::compute_descending_multiplier(seconds_elapsed, divisor) + }) + .collect(); + + assert_eq!( + result, + vec![ + 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, + 1 + ] + ) + } + + #[test] + fn compute_binary_argument_works() { + let inputs = [ + 1, + BALANCE_LOG_2_ARG_DIVISOR - 1, + BALANCE_LOG_2_ARG_DIVISOR, + BALANCE_LOG_2_ARG_DIVISOR + 1, + BALANCE_LOG_2_ARG_DIVISOR + 1000, + ]; + + let result: Vec<_> = inputs + .into_iter() + .map(|arg| PaymentAdjusterReal::compute_binary_argument(arg)) + .collect(); + + assert_eq!( + result, + vec![ + 1, + 1, + 1, + 1, + (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR + ] + ) + } + #[test] fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { let now = SystemTime::now(); @@ -1579,94 +1661,39 @@ mod tests { } #[test] - fn try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less( + fn an_accounts_never_becomes_outweighed_while_cw_balance_is_less_than_its_balance_because_disqualified_accounts_come_considered_first( ) { - todo!("fix me") - // let test_name = "try_separating_outweighed_accounts_cuts_back_the_outweighed_account_when_only_one_and_if_cw_balance_is_even_less"; - // let now = SystemTime::now(); - // let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; - // let mut subject = make_initialized_subject( - // now, - // Some(consuming_wallet_balance), - // Some(Logger::new(test_name)), - // ); - // let account_1_made_up_criteria = 18_000_000_000_000; - // let account_1 = PayableAccount { - // wallet: make_wallet("blah"), - // balance_wei: 1_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(200000)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2_made_up_criteria = 5_000_000_000_000; - // let account_2 = PayableAccount { - // wallet: make_wallet("booga"), - // balance_wei: 8_000_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - // pending_payable_opt: None, - // }; - // let required_balance_total = 1_000_000_000_000 + 333 + 8_000_000_000_000_000; - // let criteria_total = account_1_made_up_criteria + account_2_made_up_criteria; - // let accounts_with_individual_criteria = vec![ - // (account_1_made_up_criteria, account_1.clone()), - // (account_2_made_up_criteria, account_2.clone()), - // ]; - // - // let (outweighed, remaining) = subject - // .handle_possibly_outweighed_account( - // accounts_with_individual_criteria, - // required_balance_total, - // criteria_total, - // ) - // .right() - // .unwrap(); - // - // let mut expected_account = account_1; - // expected_account.balance_wei = 1_000_000_000_000 - 1; - // assert_eq!(outweighed, vec![expected_account]); - // assert_eq!(remaining, vec![account_2]) - } - - #[test] - fn even_outweighed_accounts_get_their_balances_reduced_if_cw_balance_is_less_than_their_sum() { + //NOTE that the same applies for more than one outweighed accounts that would originally request more than the cw balance, + //therefore there is no such a test either const SECONDS_IN_3_DAYS: u64 = 259_200; - let test_name = "even_outweighed_accounts_get_their_balances_reduced_if_cw_balance_is_less_than_their_sum"; + let test_name = + "only_outweighed_account_gets_its_balance_cut_back_if_cw_balance_is_even_less"; let now = SystemTime::now(); - let consuming_wallet_balance = 400_000_000_000_000; + let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; let mut subject = make_initialized_subject( now, Some(consuming_wallet_balance), Some(Logger::new(test_name)), ); - let balance_1 = 150_000_000_000_000; let account_1 = PayableAccount { wallet: make_wallet("blah"), - balance_wei: balance_1, - last_paid_timestamp: SystemTime::now() + balance_wei: 1_000_000_000_000, + last_paid_timestamp: now .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) .unwrap(), pending_payable_opt: None, }; - let balance_2 = 900_000_000_000_000; + let balance_2 = 8_000_000_000_000_000; + let wallet_2 = make_wallet("booga"); let account_2 = PayableAccount { - wallet: make_wallet("booga"), + wallet: wallet_2.clone(), balance_wei: balance_2, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) - .unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("giraffe"), - balance_wei: 800_000_000_000_000_000, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(1000)) - .unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ account_1.clone(), account_2.clone(), - account_3.clone(), ]); let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); let required_balance_total = @@ -1679,45 +1706,20 @@ mod tests { criteria_total, ); - let (outweighed, remaining) = match result { - AccountsRecreationResult::OutweighedAccounts { - outweighed, + let (disqualified, remaining) = match result { + AccountsRecreationResult::InsignificantAccounts { + disqualified, remaining, - } => (outweighed, remaining), - x => panic!( - "we expected to see some outweighed accounts bu got: {:?}", - x - ), + } => (disqualified, remaining), + x => panic!("we expected to see a disqualified account but got: {:?}", x), }; - // the algorithm first isolates the two first outweighed accounts and because - // they are more than one it will apply the standard adjustment strategy according to their criteria, - // with one big difference, now the third account will stay out - let individual_criteria_for_just_two_accounts = accounts_with_individual_criteria - .iter() - .take(2) - .map(|(criteria_sum, _)| *criteria_sum) - .collect(); - let expected_balances = compute_expected_balanced_portions_from_criteria( - individual_criteria_for_just_two_accounts, - consuming_wallet_balance, - ); - //making sure the proposed balances are non zero - assert!(expected_balances[0] > (balance_1 / 4)); - assert!(expected_balances[1] > (balance_2 / 4)); - let check_sum = expected_balances.iter().sum(); - assert!( - (consuming_wallet_balance / 100) * 99 <= check_sum - && check_sum <= (consuming_wallet_balance / 100) * 101 - ); - let expected_outweighed_accounts = { - let mut account_1 = account_1; - account_1.balance_wei = expected_balances[0]; - let mut account_2 = account_2; - account_2.balance_wei = expected_balances[1]; - vec![account_1, account_2] + let expected_disqualified_account = DisqualifiedPayableAccount { + wallet: wallet_2, + proposed_adjusted_balance: 49_201, + original_balance: balance_2, }; - assert_eq!(outweighed, expected_outweighed_accounts); - assert_eq!(remaining, vec![account_3]) + assert_eq!(disqualified, vec![expected_disqualified_account]); + assert_eq!(remaining, vec![account_1]) } #[test] @@ -1729,9 +1731,8 @@ mod tests { //each of 3 accounts contains half of the full supply and a 10-years-old debt which generates extremely big numbers in the criteria let qualified_payables = { let mut accounts = get_extreme_accounts( - vec![120, 120, 120], + Either::Left((vec![120, 120, 120], *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), now, - *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, ); accounts .iter_mut() @@ -2100,6 +2101,7 @@ mod tests { .accounts } + //TODO write this test for balances under 1M and above #[test] fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { // accounts in this test are evenly significant and so one cannot compete another, @@ -2130,7 +2132,7 @@ mod tests { assert_eq!(result, vec![]); // scenario B - const TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED: u128 = 500; + const TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED: u128 = 5_000_000; let second_scenario_name = merge_test_name_and_study_description(test_name, "first_heavier_by_balance"); @@ -2402,19 +2404,18 @@ mod tests { .iter() .map(|account| { let elapsed = secs_elapsed(account.last_paid_timestamp, now); - elapsed.pow(AGE_EXPONENT) / (((elapsed as f64).sqrt().ceil()) as u128) - * AGE_MULTIPLIER + let divisor = + (((elapsed as f64).sqrt().ceil()) as u128).pow(AGE_DIVISOR_EXP_IN_NUMERATOR); + elapsed.pow(AGE_MAIN_EXPONENT) * AGE_MULTIPLIER / divisor }) .collect(); let balance_criteria = accounts .iter() .map(|account| { let balance = account.balance_wei; - let significance = log_2(balance * BALANCE_LOG_2_BASE_MULTIPLIER) as u128; + let significance = log_2(balance / BALANCE_LOG_2_ARG_DIVISOR) as u128; let tail_weight = PaymentAdjusterReal::balance_tail_weight(balance); - balance * (significance * BALANCE_OUTER_MULTIPLIER) - + (tail_weight.pow(BALANCE_TAIL_WEIGHT_EXPONENT) - * BALANCE_TAIL_WEIGHT_MULTIPLIER) + balance * significance + tail_weight } as u128) .collect(); @@ -2512,27 +2513,31 @@ mod tests { } fn get_extreme_criteria( - months_of_debt_matrix: Vec, - common_balance_wei: u128, + months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, ) -> Vec<(u128, PayableAccount)> { let now = SystemTime::now(); - let accounts = get_extreme_accounts(months_of_debt_matrix, now, common_balance_wei); + let accounts = get_extreme_accounts(months_of_debt_matrix_and_balance_setup, now); let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); let subject = make_initialized_subject(now, None, None); subject.apply_criteria(zero_criteria_accounts) } fn get_extreme_accounts( - months_of_debt_matrix: Vec, + months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, now: SystemTime, - common_balance_wei: u128, ) -> Vec { - months_of_debt_matrix - .into_iter() + let seed: Vec<(usize, u128)> = match months_of_debt_matrix_and_balance_setup { + Either::Left((vec, const_balance)) => vec + .into_iter() + .map(|months| (months, const_balance)) + .collect(), + Either::Right(vec_of_pairs) => vec_of_pairs, + }; + seed.into_iter() .enumerate() - .map(|(idx, number_of_months)| PayableAccount { + .map(|(idx, (number_of_months, balance_wei))| PayableAccount { wallet: make_wallet(&format!("blah{}", idx)), - balance_wei: common_balance_wei, + balance_wei, last_paid_timestamp: now .checked_sub(Duration::from_secs( number_of_months as u64 * (*ONE_MONTH_LONG_DEBT_SEC), From 662b1b550365253eac89f90ef0a2b25ea0358631 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 25 Jul 2023 13:28:36 +0200 Subject: [PATCH 048/250] GH-711: improving future maintanance; module management; also continuing to try clearing out how the mechanism works for non insiders (but even me hehe) --- .../payment_adjuster/auxiliary_fns.rs | 300 +++++++++++++ .../payment_adjuster/diagnostics.rs | 4 +- .../{log_functions.rs => log_fns.rs} | 2 +- node/src/accountant/payment_adjuster/mod.rs | 409 +++--------------- .../accountant/payment_adjuster/test_utils.rs | 60 +++ 5 files changed, 426 insertions(+), 349 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/auxiliary_fns.rs rename node/src/accountant/payment_adjuster/{log_functions.rs => log_fns.rs} (99%) create mode 100644 node/src/accountant/payment_adjuster/test_utils.rs diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs new file mode 100644 index 000000000..c3150058e --- /dev/null +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -0,0 +1,300 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use itertools::Itertools; +use std::iter::successors; + +const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; +const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; + +pub fn sum_as(collection: &[T], arranger: F) -> N +where + N: From, + F: Fn(&T) -> u128, +{ + collection.iter().map(arranger).sum::().into() +} + +pub fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { + sum_as(&accounts_with_individual_criteria, |(_, account)| { + account.balance_wei + }) +} + +pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { + sum_as(&accounts_with_individual_criteria, |(criteria, _)| { + *criteria + }) +} + +pub fn cut_back_by_gas_count_limit( + weights_and_accounts: Vec<(u128, PayableAccount)>, + limit: u16, +) -> Vec<(u128, PayableAccount)> { + weights_and_accounts + .into_iter() + .take(limit as usize) + .collect() +} + +pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { + let criteria_sum_digits_count = log_10(criteria_sum); + let cw_balance_digits_count = log_10(cw_masq_balance); + let positive_difference = criteria_sum_digits_count + .checked_sub(cw_balance_digits_count) + .unwrap_or(0); + let safe_mul_coeff = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; + eprintln!( + "criteria sum {}, safe mul coeff {}", + criteria_sum, safe_mul_coeff + ); + 10_u128 + .checked_pow(safe_mul_coeff as u32) + .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) +} + +pub fn drop_criteria_and_leave_accounts( + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, +) -> Vec { + accounts_with_individual_criteria + .into_iter() + .map(|(_, account)| account) + .collect() +} + +pub fn sort_in_descendant_order_by_weights( + unsorted: impl Iterator, +) -> Vec<(u128, PayableAccount)> { + unsorted + .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .collect() +} + +pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { + criteria_and_accounts + .into_iter() + .map(|(_, account)| account) + .collect() +} + +// replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` +// which will be introduced by Rust 1.67.0; this was written with 1.63.0 +pub fn log_10(num: u128) -> usize { + successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() +} + +const fn num_bits() -> usize { + std::mem::size_of::() * 8 +} + +pub fn log_2(x: u128) -> u32 { + if x < 1 { + panic!("log2 of 0 not supported") + } + num_bits::() as u32 - x.leading_zeros() - 1 +} + +pub fn x_or_1(x: u128) -> u128 { + if x == 0 { + 1 + } else { + x + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::auxiliary_fns::{ + compute_fractions_preventing_mul_coeff, log_10, log_2, EMPIRIC_PRECISION_COEFFICIENT, + MAX_EXPONENT_FOR_10_IN_U128, + }; + use crate::accountant::payment_adjuster::test_utils::{ + get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + }; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::sub_lib::wallet::Wallet; + use itertools::{Either, Itertools}; + use std::time::SystemTime; + + #[test] + fn constants_are_correct() { + assert_eq!(MAX_EXPONENT_FOR_10_IN_U128, 38); + assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8) + } + + #[test] + fn log_10_works() { + [ + (4_565_u128, 4), + (1_666_777, 7), + (3, 1), + (123, 3), + (111_111_111_111_111_111, 18), + ] + .into_iter() + .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) + } + + #[test] + fn log_2_works() { + [ + (1, 0), + (2, 1), + (4, 2), + (8192, 13), + (18446744073709551616, 64), + (1267650600228229401496703205376, 100), + (170141183460469231731687303715884105728, 127), + ] + .into_iter() + .for_each(|(num, expected_result)| assert_eq!(log_2(num), expected_result)) + } + + #[test] + #[should_panic(expected = "log2 of 0 not supported")] + fn log_2_dislikes_0() { + let _ = log_2(0); + } + + #[test] + fn multiplication_coeff_for_integers_to_be_above_one_instead_of_fractional_numbers() { + let final_criteria_sum = 5_000_000_000_000_u128; + let consuming_wallet_balances = vec![ + 222_222_222_222_u128, + 100_000, + 123_456_789, + 5_555_000_000_000, + 5_000_555_000_000_000, + 1_000_000_000_000_000_000, //1 MASQ + ]; + + let result = consuming_wallet_balances + .clone() + .into_iter() + .map(|cw_balance| { + compute_fractions_preventing_mul_coeff(cw_balance, final_criteria_sum) + }) + .collect::>(); + + assert_eq!( + result, + vec![ + 1_000_000_000, + 1_000_000_000_000_000, + 1_000_000_000_000, + 100_000_000, + 100_000_000, + 100_000_000 + ] + ) + } + + #[test] + fn multiplication_coeff_showing_extreme_feeding_and_safety_ceiling() { + // the coeff is how many multiples of 10 we need to use to multiply the cw balance + // in order to get at a number bigger than the criteria sum (and the bigger the more + // accurate numbers calculated afterwards, so we add some for better precision) + // + // we cannot say by heart what the criteria sum of a debt of these parameters evaluates to, + // so we also can hardly put them in an order + let accounts_as_months_and_balances = vec![ + (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (5, 10_u128.pow(18)), + (12, 10_u128.pow(18)), + (120, 10_u128.pow(20)), + (600, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), + ]; + let (different_accounts_with_criteria, initial_accounts_order_from_the_seeds) = + get_extreme_criteria_and_initial_accounts_order(accounts_as_months_and_balances); + let cw_balance_in_minor = 1; + + let results = different_accounts_with_criteria + .into_iter() + .map(|(criteria_sum, account)| { + // scenario simplification: we asume there is always just one account in a time + let final_criteria_total = criteria_sum; + let resulted_coeff = compute_fractions_preventing_mul_coeff( + cw_balance_in_minor, + final_criteria_total, + ); + (resulted_coeff, account.wallet, criteria_sum) + }) + .collect::>(); + + eprintln!("results {:?}", results); + let mut initial_accounts_order_from_the_seeds_iter = + initial_accounts_order_from_the_seeds.iter().enumerate(); + let coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs = results + .into_iter() + .map(|(coeff, wallet, criteria_sum)| { + let (idx, _) = initial_accounts_order_from_the_seeds_iter + .clone() + .find(|(_, wallet_ordered)| wallet_ordered == &&wallet) + .unwrap(); + (idx, coeff, criteria_sum) + }) + .sorted_by(|(a_idx, _, _), (b_idx, _, _)| Ord::cmp(&b_idx, &a_idx)) + .map(|(_, coeff, criteria_sum)| (coeff, criteria_sum)) + .collect::>(); + //to preserve easy visual checks + #[rustfmt::skip] + fn expected_result() -> Vec<(u128, u128)> { + vec![ + ( + 100000000000000000000000000000000000000, + 3337514568138519074931415968855 + ), + ( + 100000000000000000000000000000000000, + 2977068138519074931415968855 + ), + ( + 100000000000000000000000000000000000, + 2968604285622712478129675136 + ), + ( + 10000000000000000000000000000000, + 879662486510538526960128 + ), + ( + 1000000000000000000000000000000, + 43211890301705270704000 + ), + ( + 1000000000000000000000000000000, + 13327534955520000000000 + ), + ( + 100000000000000000000000000000000000, + 2962501520859680498325341824 + ) + ] + }; + assert_eq!( + coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs, + expected_result() + ) + } + + fn get_extreme_criteria_and_initial_accounts_order( + months_of_debt_and_balances_matrix: Vec<(usize, u128)>, + ) -> (Vec<(u128, PayableAccount)>, Vec) { + let now = SystemTime::now(); + let accounts = get_extreme_accounts(Either::Right(months_of_debt_and_balances_matrix), now); + let wallets_in_order = accounts + .iter() + .map(|account| account.wallet.clone()) + .collect(); + let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); + let subject = make_initialized_subject(now, None, None); + // when criteria applied the collection gets sorted and so it does not have to match the initial order + eprintln!("wallets in order {:?}", wallets_in_order); + ( + subject.apply_criteria(zero_criteria_accounts), + wallets_in_order, + ) + } +} diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 051cf8b18..e272f3935 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -11,8 +11,8 @@ use thousands::Separable; pub static AGE_SINGLETON: Once = Once::new(); pub static BALANCE_SINGLETON: Once = Once::new(); -pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 13] = - [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21]; +pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 14] = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25]; pub const fn diagnostics_x_axis_exponents_len() -> usize { EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len() diff --git a/node/src/accountant/payment_adjuster/log_functions.rs b/node/src/accountant/payment_adjuster/log_fns.rs similarity index 99% rename from node/src/accountant/payment_adjuster/log_functions.rs rename to node/src/accountant/payment_adjuster/log_fns.rs index d8b17cfdf..ca0bf3fcd 100644 --- a/node/src/accountant/payment_adjuster/log_functions.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -163,7 +163,7 @@ pub fn log_insufficient_transaction_fee_balance( #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::log_functions::{ + use crate::accountant::payment_adjuster::log_fns::{ log_info_for_disqualified_accounts, REFILL_RECOMMENDATION, }; use crate::accountant::payment_adjuster::DisqualifiedPayableAccount; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d939811f3..857a0686a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,10 +1,17 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +//keep these modules private +mod auxiliary_fns; mod diagnostics; mod inner; -mod log_functions; +mod log_fns; +mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::auxiliary_fns::{ + compute_fractions_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, log_2, + rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, x_or_1, +}; use crate::accountant::payment_adjuster::diagnostics::{ diagnostics, diagnostics_collective, diagnostics_x_axis_exponents_len, CriteriaWithDiagnostics, DiagnosticsSetting, AGE_SINGLETON, BALANCE_SINGLETON, @@ -12,7 +19,7 @@ use crate::accountant::payment_adjuster::diagnostics::{ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; -use crate::accountant::payment_adjuster::log_functions::{ +use crate::accountant::payment_adjuster::log_fns::{ before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_accounts, log_insufficient_transaction_fee_balance, }; @@ -163,7 +170,6 @@ const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = multiplier: 1, divisor: 2, }; -const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; // sets the minimal percentage of the original balance that must be // proposed after the adjustment or the account will be eliminated for insignificance @@ -208,14 +214,6 @@ impl PaymentAdjusterReal { self.inner = Box::new(inner); } - fn sum_as(collection: &[T], arranger: F) -> N - where - N: From, - F: Fn(&T) -> u128, - { - collection.iter().map(arranger).sum::().into() - } - fn determine_transactions_count_limit_by_gas( tech_info: &FinancialAndTechDetails, required_transactions_count: usize, @@ -262,10 +260,9 @@ impl PaymentAdjusterReal { .map(|(_, account)| account) .collect(), }; - let required_masq_sum: u128 = - Self::sum_as(&qualified_payables, |account: &&PayableAccount| { - account.balance_wei - }); + let required_masq_sum: u128 = sum_as(&qualified_payables, |account: &&PayableAccount| { + account.balance_wei + }); if required_masq_sum <= consuming_wallet_balance_wei { false @@ -316,7 +313,7 @@ impl PaymentAdjusterReal { let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts } else { - self.adjust_next_round_cw_balance_down(&adjustment_result.decided_accounts); + self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts); return self.run_full_adjustment_procedure( adjustment_result.remaining_accounts, adjustment_result.decided_accounts, @@ -401,10 +398,7 @@ impl PaymentAdjusterReal { let multiplied = balance_wei .checked_mul(binary_weight as u128) .expect("mul overflow"); - let tail_weight = 0; - // if balance_wei > 1_000_000_000 {Self::balance_tail_weight(balance_wei)} else {1}; //TODO untested!!! - //eprintln!("tail weigh {}", tail_weight); - multiplied + tail_weight + multiplied }; let criterion = formula(account.balance_wei); @@ -426,13 +420,7 @@ impl PaymentAdjusterReal { .map(age_criterion_closure) .map(balance_criterion_closure); - Self::sort_in_descendant_order_by_weights(weights_and_accounts) - } - - //TODO this fn should later become property of the balance criteria computing class e.g. "CriteriaComputer" - //this enables to sense also small differences - fn balance_tail_weight(balance_wei: u128) -> u128 { - (balance_wei % BALANCE_TAIL_WEIGHT_MODULO_OPERAND).pow(BALANCE_TAIL_WEIGHT_EXPONENT) + sort_in_descendant_order_by_weights(weights_and_accounts) } //TODO this fn should later become property of the age criteria computing class e.g. "CriteriaComputer" @@ -465,7 +453,7 @@ impl PaymentAdjusterReal { ) -> AdjustmentCompletion { match self.inner.gas_limitation_opt() { Some(limitation_by_gas) => { - let weighted_accounts_cut_by_gas = Self::cut_back_by_gas_count_limit( + let weighted_accounts_cut_by_gas = cut_back_by_gas_count_limit( accounts_with_individual_criteria, limitation_by_gas, ); @@ -477,7 +465,7 @@ impl PaymentAdjusterReal { true => AdjustmentCompletion::Continue( self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas), ), - false => AdjustmentCompletion::Finished(Self::rebuild_accounts( + false => AdjustmentCompletion::Finished(rebuild_accounts( weighted_accounts_cut_by_gas, )), } @@ -493,7 +481,7 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationSummary { // let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); - let criteria_total = Self::criteria_total(&accounts_with_individual_criteria); + let criteria_total = criteria_total(&accounts_with_individual_criteria); //TODO simplify this...one of these enums is probably redundant match self.recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, @@ -548,14 +536,12 @@ impl PaymentAdjusterReal { criteria_total: u128, ) -> AccountsRecreationResult { let cw_masq_balance = self.inner.cw_masq_balance(); - let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( - cw_masq_balance, - criteria_total, - ); + let multiplication_coeff = + compute_fractions_preventing_mul_coeff(cw_masq_balance, criteria_total); let cw_masq_balance_big_u256 = U256::from(cw_masq_balance); let criteria_total_u256 = U256::from(criteria_total); let multiplication_coeff_u256 = U256::from(multiplication_coeff); - let proportional_fragment_of_cw_balance = cw_masq_balance_big_u256 + let proportional_piece_of_cw_balance = cw_masq_balance_big_u256 .checked_mul(multiplication_coeff_u256) .unwrap() //TODO try killing this unwrap() in a test // TODO how to give the criteria some kind of ceiling? We don't want to exceed certain dangerous limit @@ -566,7 +552,7 @@ impl PaymentAdjusterReal { .into_iter() .map(|(criteria_sum, account)| { let proposed_adjusted_balance = (U256::from(criteria_sum) - * proportional_fragment_of_cw_balance + * proportional_piece_of_cw_balance / multiplication_coeff_u256) .as_u128(); @@ -597,7 +583,7 @@ impl PaymentAdjusterReal { AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) } - //100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000, 100000000000000000000000000000000 + fn consider_account_disqualification_from_percentage_insignificance( accounts_with_unchecked_adjustment: Vec, ) -> Either, AccountsRecreationResult> { @@ -819,7 +805,7 @@ impl PaymentAdjusterReal { } else { vec![only_account] } - } else if Self::sum_as::(&outweighed_with_criteria, |(_, account)| { + } else if sum_as::(&outweighed_with_criteria, |(_, account)| { account.balance_wei }) > cw_masq_balance { @@ -835,18 +821,8 @@ impl PaymentAdjusterReal { } } - fn drop_criteria_and_leave_accounts( - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> Vec { - accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .collect() - } - - fn adjust_next_round_cw_balance_down(&mut self, processed_outweighed: &[PayableAccount]) { - let subtrahend_total: u128 = - Self::sum_as(processed_outweighed, |account| account.balance_wei); + fn adjust_cw_balance_down_for_next_round(&mut self, processed_outweighed: &[PayableAccount]) { + let subtrahend_total: u128 = sum_as(processed_outweighed, |account| account.balance_wei); self.inner.lower_remaining_cw_balance(subtrahend_total); diagnostics!("LOWERED CW BALANCE", || format!( @@ -855,85 +831,6 @@ impl PaymentAdjusterReal { self.inner.cw_masq_balance() )) } - - fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { - Self::sum_as(&accounts_with_individual_criteria, |(_, account)| { - account.balance_wei - }) - } - - fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { - Self::sum_as(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }) - } - - fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; - let criteria_sum_digits_count = log_10(criteria_sum); - let cw_balance_digits_count = log_10(cw_masq_balance); - let positive_difference = criteria_sum_digits_count - .checked_sub(cw_balance_digits_count) - .unwrap_or(0); - let safe_mul_coeff = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; - eprintln!( - "criteria sum {}, safe mul coeff {}", - criteria_sum, safe_mul_coeff - ); - 10_u128 - .checked_pow(safe_mul_coeff as u32) - .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) - } - - fn cut_back_by_gas_count_limit( - weights_and_accounts: Vec<(u128, PayableAccount)>, - limit: u16, - ) -> Vec<(u128, PayableAccount)> { - weights_and_accounts - .into_iter() - .take(limit as usize) - .collect() - } - - fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, - ) -> Vec<(u128, PayableAccount)> { - unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) - .collect() - } - - fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { - criteria_and_accounts - .into_iter() - .map(|(_, account)| account) - .collect() - } -} - -// replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` -// which will be introduced by Rust 1.67.0; this was written with 1.63.0 -fn log_10(num: u128) -> usize { - successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() -} - -const fn num_bits() -> usize { - std::mem::size_of::() * 8 -} - -fn log_2(x: u128) -> u32 { - if x < 1 { - panic!("log2 of 0 not supported") - } - num_bits::() as u32 - x.leading_zeros() - 1 -} - -fn x_or_1(x: u128) -> u128 { - if x == 0 { - 1 - } else { - x - } } #[derive(Debug)] @@ -1060,13 +957,19 @@ pub enum AnalysisError { #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::auxiliary_fns::{ + balance_total, compute_fractions_preventing_mul_coeff, criteria_total, log_2, + }; use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; + use crate::accountant::payment_adjuster::test_utils::{ + get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + }; use crate::accountant::payment_adjuster::{ - log_10, log_2, AccountWithUncheckedAdjustment, AccountsRecreationResult, Adjustment, - AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, + AccountWithUncheckedAdjustment, AccountsRecreationResult, Adjustment, AnalysisError, + DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, BALANCE_TAIL_WEIGHT_EXPONENT, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, @@ -1096,12 +999,6 @@ mod tests { use thousands::Separable; use web3::types::U256; - lazy_static! { - static ref MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR: u128 = - MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18); - static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; - } - fn make_payable_setup_msg_coming_from_blockchain_bridge( q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, gas_price_opt: Option, @@ -1311,139 +1208,6 @@ mod tests { ); } - #[test] - fn balance_tail_weight_works() { - vec![ - (12345678, 678_u128.pow(2)), - (5555, 555_u128.pow(2)), - (55, 55_u128.pow(2)), - (1, 1), - ] - .into_iter() - .for_each(|(num, expected_result)| { - assert_eq!( - PaymentAdjusterReal::balance_tail_weight(num), - expected_result - ) - }) - } - - #[test] - fn log_10_works() { - [ - (4_565_u128, 4), - (1_666_777, 7), - (3, 1), - (123, 3), - (111_111_111_111_111_111, 18), - ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) - } - - #[test] - fn log_2_works() { - [ - (1, 0), - (2, 1), - (4, 2), - (8192, 13), - (18446744073709551616, 64), - (1267650600228229401496703205376, 100), - (170141183460469231731687303715884105728, 127), - ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_2(num), expected_result)) - } - - #[test] - #[should_panic(expected = "log2 of 0 not supported")] - fn log_2_dislikes_0() { - let _ = log_2(0); - } - - #[test] - fn multiplication_coeff_for_integers_to_be_above_one_instead_of_fractional_numbers() { - let final_criteria_sum = 5_000_000_000_000_u128; - let consuming_wallet_balances = vec![ - 222_222_222_222_u128, - 100_000, - 123_456_789, - 5_555_000_000_000, - 5_000_555_000_000_000, - 1_000_000_000_000_000_000, //1 MASQ - ]; - - let result = consuming_wallet_balances - .clone() - .into_iter() - .map(|cw_balance| { - PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( - cw_balance, - final_criteria_sum, - ) - }) - .collect::>(); - - assert_eq!( - result, - vec![ - 1_000_000_000, - 1_000_000_000_000_000, - 1_000_000_000_000, - 100_000_000, - 100_000_000, - 100_000_000 - ] - ) - } - - #[test] - fn multiplication_coeff_showing_extreme_feeding_and_safety_ceiling() { - let account_debt_ages_in_months_and_balances = vec![ - (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), - (5, 10_u128.pow(18)), - (12, 10_u128.pow(18)), - (120, 10_u128.pow(20)), - (600, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), - ]; - let different_accounts_with_criteria = - get_extreme_criteria(Either::Right(account_debt_ages_in_months_and_balances)); - let cw_balance_in_minor = 1; - - let results = different_accounts_with_criteria - .into_iter() - .map(|(criteria, account)| { - // scenario simplification: we asume there is always just one account in a time - let final_criteria_total = criteria; - PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( - cw_balance_in_minor, - final_criteria_total, - ) - }) - .collect::>(); - - assert_eq!( - results, - vec![ - 100000000000000000000000000000000000, - 100000000000000000000000000000000000, - 100000000000000000000000000000000000, - todo!("careful!! these are sorted criteria!!!...different from what you think they are"), - 10000000000000000000000000000000, - 1000000000000000000000000000000, - 1000000000000000000000000000000 - ] - ) - // enough space for our counts; mostly we use it for division and multiplication and - // in both cases the coefficient is picked carefully to handle it (we near the extremes - // either by increasing the criteria sum and decreasing the cw balance or vica versa) - // - // it allows operating without the use of point floating numbers - } - #[test] fn consider_account_disqualification_from_percentage_insignificance_adheres_to_the_manifest_consts_of_insignificance( ) { @@ -1560,6 +1324,7 @@ mod tests { fn compute_descending_multiplier_works() { let result: Vec<_> = EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS .into_iter() + .take(12) .map(|exp| 10_u64.pow(exp)) .map(|seconds_elapsed| { let divisor = PaymentAdjusterReal::compute_divisor(seconds_elapsed); @@ -1605,6 +1370,7 @@ mod tests { #[test] fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { + const SAFETY_MULTIPLIER: u128 = 1_000_000_000_000_000; let now = SystemTime::now(); let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000; let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); @@ -1630,29 +1396,28 @@ mod tests { let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important - let total = balance_1 * 10_000 + balance_2 * 10_000; - let ratio_2_u128 = total / (balance_2 * 10_000); - let ratio_2 = ratio_2_u128 as f64 / 10_000.0; let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - let criteria = subject.apply_criteria(zero_criteria_accounts); - let account_1_criterion = criteria[0].0 * 10_000; - let account_2_criterion = criteria[1].0 * 10_000; - let criteria_total = account_1_criterion + account_2_criterion; - let criterion_2_ratio = ((criteria_total / account_2_criterion) as f64) / 10_000.0; - //the next assertion reads as the weight of the second account grew faster and bigger than at the first account; - //also, the time parameter has a strong impact on the final criteria; + let criteria_and_accounts = subject.apply_criteria(zero_criteria_accounts); + let criteria_total = criteria_total(&criteria_and_accounts); + let account_2_criterion = criteria_and_accounts[1].0; + let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; + let proportional_piece_of_cw_balance = cw_balance_fractional_safe / criteria_total; + let proposed_adjusted_balance_2 = + (account_2_criterion * proportional_piece_of_cw_balance) / SAFETY_MULTIPLIER; + //the weight of the second account grew very progressively due to the effect of the long age; //consequences are that redistributing the new balances according to the computed weights would've attributed - //the second account with more tokens to pay than it had when the test started; - //to prevent it, we've got a rule that any account can never demand more than 100% of the initial amount - assert!(ratio_2 > criterion_2_ratio); + //the second account with more tokens to pay than it'd had before the test started; + //to prevent it, we've got a rule that no account can ever demand more than its 100% + assert!(proposed_adjusted_balance_2 > 10 * balance_2, "we expected the proposed balance much bigger than the original which is {} but it was {}", balance_2, proposed_adjusted_balance_2); assert_eq!( result, vec![ - account_2, //outweighed accounts take the first places + account_2, //outweighed account takes the first place PayableAccount { wallet: make_wallet("blah"), - balance_wei: 1_499_949_995_299, + //precisely should be 1_500_000_000_000 - (25_000_000 - 25_000_000) but close enough + balance_wei: 1_499_949_999_252, last_paid_timestamp: last_paid_timestamp_1, pending_payable_opt: None, }, @@ -1696,10 +1461,8 @@ mod tests { account_2.clone(), ]); let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); - let required_balance_total = - PaymentAdjusterReal::balance_total(&accounts_with_individual_criteria); - let criteria_total = - PaymentAdjusterReal::criteria_total(&accounts_with_individual_criteria); + let required_balance_total = balance_total(&accounts_with_individual_criteria); + let criteria_total = criteria_total(&accounts_with_individual_criteria); let result = subject.recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria.clone(), @@ -2046,6 +1809,7 @@ mod tests { fn test_competitive_accounts( test_name_with_unique_description: &str, + consuming_wallet_balance: u128, wallet_1: &Wallet, wallet_2: &Wallet, balance_account_1: u128, @@ -2072,7 +1836,7 @@ mod tests { }; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = U256::from(100_000_000_000_000_u64 - 1); + let consuming_wallet_masq_balance_wei = U256::from(consuming_wallet_balance); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -2111,6 +1875,7 @@ mod tests { format!("{}/{}", test_name, description) } let test_name = "adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account"; + let consuming_wallet_balance = 100_000_000_000_000_u128 - 1; let wallet_1 = make_wallet("abcd"); let wallet_2 = make_wallet("cdef"); let balance_account_1 = 100_000_000_000_000; @@ -2122,6 +1887,8 @@ mod tests { // scenario A let result = test_competitive_accounts( &first_scenario_name, + consuming_wallet_balance, + //TODO many args in these tests are always the same...refactor &wallet_1, &wallet_2, balance_account_1, @@ -2138,6 +1905,7 @@ mod tests { let result = test_competitive_accounts( &second_scenario_name, + consuming_wallet_balance, &wallet_1, &wallet_2, balance_account_1 + TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED, @@ -2155,6 +1923,7 @@ mod tests { let result = test_competitive_accounts( &third_scenario_name, + consuming_wallet_balance, &wallet_1, &wallet_2, balance_account_1, @@ -2167,7 +1936,7 @@ mod tests { assert_eq!(result.len(), 1) } - //TODO do I really want to delete this test? Why? + //TODO do I really want to delete this test? Why? I probably don't // #[test] // fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( // ) { @@ -2414,8 +2183,7 @@ mod tests { .map(|account| { let balance = account.balance_wei; let significance = log_2(balance / BALANCE_LOG_2_ARG_DIVISOR) as u128; - let tail_weight = PaymentAdjusterReal::balance_tail_weight(balance); - balance * significance + tail_weight + balance * significance } as u128) .collect(); @@ -2429,7 +2197,7 @@ mod tests { }, ); let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + let multiplication_coeff = compute_fractions_preventing_mul_coeff( consuming_wallet_masq_balance_wei, final_criteria_sum, ); @@ -2486,7 +2254,7 @@ mod tests { consuming_wallet_masq_balance_wei: u128, ) -> Vec { let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( + let multiplication_coeff = compute_fractions_preventing_mul_coeff( consuming_wallet_masq_balance_wei, final_criteria_sum, ); @@ -2511,55 +2279,4 @@ mod tests { ); new_balanced_portions } - - fn get_extreme_criteria( - months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, - ) -> Vec<(u128, PayableAccount)> { - let now = SystemTime::now(); - let accounts = get_extreme_accounts(months_of_debt_matrix_and_balance_setup, now); - let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); - let subject = make_initialized_subject(now, None, None); - subject.apply_criteria(zero_criteria_accounts) - } - - fn get_extreme_accounts( - months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, - now: SystemTime, - ) -> Vec { - let seed: Vec<(usize, u128)> = match months_of_debt_matrix_and_balance_setup { - Either::Left((vec, const_balance)) => vec - .into_iter() - .map(|months| (months, const_balance)) - .collect(), - Either::Right(vec_of_pairs) => vec_of_pairs, - }; - seed.into_iter() - .enumerate() - .map(|(idx, (number_of_months, balance_wei))| PayableAccount { - wallet: make_wallet(&format!("blah{}", idx)), - balance_wei, - last_paid_timestamp: now - .checked_sub(Duration::from_secs( - number_of_months as u64 * (*ONE_MONTH_LONG_DEBT_SEC), - )) - .unwrap(), - pending_payable_opt: None, - }) - .collect() - } - - fn make_initialized_subject( - now: SystemTime, - cw_masq_balance_opt: Option, - logger_opt: Option, - ) -> PaymentAdjusterReal { - PaymentAdjusterReal { - inner: Box::new(PaymentAdjusterInnerReal::new( - now, - None, - cw_masq_balance_opt.unwrap_or(0), - )), - logger: logger_opt.unwrap_or(Logger::new("test")), - } - } } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs new file mode 100644 index 000000000..50cd04bc7 --- /dev/null +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use crate::test_utils::make_wallet; +use itertools::Either; +use lazy_static::lazy_static; +use masq_lib::constants::MASQ_TOTAL_SUPPLY; +use masq_lib::logger::Logger; +use std::time::{Duration, SystemTime}; + +lazy_static! { + pub static ref MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR: u128 = + MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18); + pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; +} + +pub fn make_initialized_subject( + now: SystemTime, + cw_masq_balance_opt: Option, + logger_opt: Option, +) -> PaymentAdjusterReal { + PaymentAdjusterReal { + inner: Box::new(PaymentAdjusterInnerReal::new( + now, + None, + cw_masq_balance_opt.unwrap_or(0), + )), + logger: logger_opt.unwrap_or(Logger::new("test")), + } +} + +pub fn get_extreme_accounts( + months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, + now: SystemTime, +) -> Vec { + let seed: Vec<(usize, u128)> = match months_of_debt_matrix_and_balance_setup { + Either::Left((vec, const_balance)) => vec + .into_iter() + .map(|months| (months, const_balance)) + .collect(), + Either::Right(vec_of_pairs) => vec_of_pairs, + }; + seed.into_iter() + .enumerate() + .map(|(idx, (number_of_months, balance_wei))| PayableAccount { + wallet: make_wallet(&format!("blah{}", idx)), + balance_wei, + last_paid_timestamp: now + .checked_sub(Duration::from_secs( + number_of_months as u64 * (*ONE_MONTH_LONG_DEBT_SEC), + )) + .unwrap(), + pending_payable_opt: None, + }) + .collect() +} From 6178de2f79df3c48a177d65ab9f300d10fc26aba Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 26 Jul 2023 20:45:41 +0200 Subject: [PATCH 049/250] GH-711: savepoint before I polish away the balances so that it matches the full cw balance if it can --- .../payment_adjuster/auxiliary_fns.rs | 65 +- .../accountant/payment_adjuster/log_fns.rs | 41 +- node/src/accountant/payment_adjuster/mod.rs | 629 +++++++++++------- 3 files changed, 441 insertions(+), 294 deletions(-) diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index c3150058e..30743f5fe 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -1,6 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::{ + AccountWithUncheckedAdjustment, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, +}; +use crate::diagnostics; +use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use std::iter::successors; @@ -53,6 +58,23 @@ pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_su .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) } +pub fn find_disqualified_account_with_smallest_proposed_balance( + accounts: &[&AccountWithUncheckedAdjustment], +) -> Wallet { + let account_ref = accounts.iter().reduce(|previous, current| { + if current.proposed_adjusted_balance <= previous.proposed_adjusted_balance { + current + } else { + previous + } + }); + account_ref + .expect("the iterator was empty but we had checked it") + .original_account + .wallet + .clone() +} + pub fn drop_criteria_and_leave_accounts( accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { @@ -106,16 +128,21 @@ pub fn x_or_1(x: u128) -> u128 { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fractions_preventing_mul_coeff, log_10, log_2, EMPIRIC_PRECISION_COEFFICIENT, - MAX_EXPONENT_FOR_10_IN_U128, + compute_fractions_preventing_mul_coeff, + find_disqualified_account_with_smallest_proposed_balance, log_10, log_2, + EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::payment_adjuster::{ + AccountWithUncheckedAdjustment, PaymentAdjusterReal, + }; + use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; + use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use std::time::SystemTime; + use std::time::{Duration, SystemTime}; #[test] fn constants_are_correct() { @@ -279,6 +306,27 @@ mod tests { ) } + #[test] + fn find_disqualified_account_with_smallest_proposed_balance_when_accounts_with_equal_balances() + { + let account_info = AccountWithUncheckedAdjustment { + original_account: make_payable_account(111), + proposed_adjusted_balance: 1_234_567_890, + criteria_sum: 400_000_000, + }; + let wallet_1 = make_wallet("abc"); + let wallet_2 = make_wallet("def"); + let mut account_info_1 = account_info.clone(); + account_info_1.original_account.wallet = wallet_1; + let mut account_info_2 = account_info; + account_info_2.original_account.wallet = wallet_2.clone(); + let accounts = vec![&account_info_1, &account_info_2]; + + let result = find_disqualified_account_with_smallest_proposed_balance(&accounts); + + assert_eq!(result, wallet_2) + } + fn get_extreme_criteria_and_initial_accounts_order( months_of_debt_and_balances_matrix: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { @@ -288,13 +336,10 @@ mod tests { .iter() .map(|account| account.wallet.clone()) .collect(); - let zero_criteria_accounts = PaymentAdjusterReal::initialize_zero_criteria(accounts); let subject = make_initialized_subject(now, None, None); - // when criteria applied the collection gets sorted and so it does not have to match the initial order + // when criteria are applied the collection will get sorted and will not necessarily have to match the initial order + let criteria_and_accounts = subject.add_criteria_sums_to_accounts(accounts); eprintln!("wallets in order {:?}", wallets_in_order); - ( - subject.apply_criteria(zero_criteria_accounts), - wallets_in_order, - ) + (criteria_and_accounts, wallets_in_order) } } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index ca0bf3fcd..893aa97b8 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -112,11 +112,11 @@ pub fn before_and_after_debug_msg( ) } -pub fn log_info_for_disqualified_accounts( +pub fn log_info_for_disqualified_account( logger: &Logger, - disqualified_accounts: &[DisqualifiedPayableAccount], + disqualified_account_opt: Option<&DisqualifiedPayableAccount>, ) { - disqualified_accounts.iter().for_each(|account| { + disqualified_account_opt.map(|account| { info!( logger, "Consuming wallet low in MASQ balance. Recently qualified \ @@ -164,7 +164,7 @@ pub fn log_insufficient_transaction_fee_balance( #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::log_fns::{ - log_info_for_disqualified_accounts, REFILL_RECOMMENDATION, + log_info_for_disqualified_account, REFILL_RECOMMENDATION, }; use crate::accountant::payment_adjuster::DisqualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; @@ -182,37 +182,4 @@ In order to continue using services of other Nodes and avoid delinquency \ bans you will need to put more funds into your consuming wallet." ) } - - #[test] - fn log_info_for_disqualified_accounts_can_log_multiple_accounts() { - init_test_logging(); - let wallet_1 = make_wallet("abc"); - let wallet_2 = make_wallet("efg"); - let balance_1 = 456_789_012_345; - let balance_2 = 222_444_777; - let disqualified_accounts = vec![ - DisqualifiedPayableAccount { - wallet: wallet_1.clone(), - original_balance: 500_000_000_000, - proposed_adjusted_balance: balance_1, - }, - DisqualifiedPayableAccount { - wallet: wallet_2.clone(), - original_balance: 300_000_000, - proposed_adjusted_balance: balance_2, - }, - ]; - let logger = Logger::new("log_info_for_disqualified_accounts_can_log_multiple_accounts"); - - log_info_for_disqualified_accounts(&logger, &disqualified_accounts); - - let make_expected_msg = |wallet: &Wallet, balance: u128| -> String { - format!("Recently qualified payable for wallet {wallet} is being ignored as the limited consuming \ - balance implied adjustment of its balance down to {} wei, which is not at least half of the debt", balance.separate_with_commas()) - }; - TestLogHandler::new().assert_logs_contain_in_order(vec![ - &make_expected_msg(&wallet_1, balance_1), - &make_expected_msg(&wallet_2, balance_2), - ]); - } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 857a0686a..0b67fbc43 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -9,8 +9,9 @@ mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fractions_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, log_2, - rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, x_or_1, + compute_fractions_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, + find_disqualified_account_with_smallest_proposed_balance, log_2, rebuild_accounts, + sort_in_descendant_order_by_weights, sum_as, x_or_1, }; use crate::accountant::payment_adjuster::diagnostics::{ diagnostics, diagnostics_collective, diagnostics_x_axis_exponents_len, CriteriaWithDiagnostics, @@ -20,8 +21,8 @@ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - before_and_after_debug_msg, log_adjustment_by_masq_required, - log_info_for_disqualified_accounts, log_insufficient_transaction_fee_balance, + before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_account, + log_insufficient_transaction_fee_balance, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -40,6 +41,7 @@ use masq_lib::logger::Logger; use std::any::Any; use std::collections::HashMap; use std::iter::{once, successors}; +use std::ops::Not; use std::time::{Duration, SystemTime}; use thousands::Separable; use web3::types::U256; @@ -139,6 +141,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); + //TODO should also return an error...in case we run adjustment but the resulted collection is empty OutcomingPaymentsInstructions { accounts: adjusted_accounts, response_skeleton_opt, @@ -286,17 +289,23 @@ impl PaymentAdjusterReal { "UNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, ); - let accounts_with_zero_criteria = - Self::initialize_zero_criteria(unresolved_qualified_accounts); let sorted_accounts_with_individual_criteria = - self.apply_criteria(accounts_with_zero_criteria); - + self.add_criteria_sums_to_accounts(unresolved_qualified_accounts); self.run_adjustment_by_criteria_recursively( sorted_accounts_with_individual_criteria, resolved_qualified_accounts, ) } + fn add_criteria_sums_to_accounts( + &self, + accounts: Vec, + ) -> Vec<(u128, PayableAccount)> { + let zero_criteria_accounts = Self::initialize_zero_criteria(accounts); + self.apply_criteria(zero_criteria_accounts) + } + + //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_full_adjustment_procedure fn run_adjustment_by_criteria_recursively( &mut self, sorted_accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, @@ -308,7 +317,10 @@ impl PaymentAdjusterReal { AdjustmentCompletion::Continue(iteration_result) => iteration_result, }; - log_info_for_disqualified_accounts(&self.logger, &adjustment_result.disqualified_accounts); + log_info_for_disqualified_account( + &self.logger, + adjustment_result.disqualified_account_opt.as_ref(), + ); let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts @@ -491,7 +503,7 @@ impl PaymentAdjusterReal { AdjustmentIterationSummary { decided_accounts, remaining_accounts: vec![], - disqualified_accounts: vec![], + disqualified_account_opt: None, } } AccountsRecreationResult::InsignificantAccounts { @@ -500,7 +512,7 @@ impl PaymentAdjusterReal { } => AdjustmentIterationSummary { decided_accounts: vec![], remaining_accounts: remaining, - disqualified_accounts: disqualified, + disqualified_account_opt: Some(disqualified), }, AccountsRecreationResult::OutweighedAccounts { outweighed, @@ -508,7 +520,7 @@ impl PaymentAdjusterReal { } => AdjustmentIterationSummary { decided_accounts: outweighed, remaining_accounts: remaining, - disqualified_accounts: vec![], + disqualified_account_opt: None, }, } // match self.handle_possibly_outweighed_accounts( @@ -535,6 +547,34 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AccountsRecreationResult { + let accounts_with_unchecked_adjustment = self.compute_accounts_with_unchecked_adjustment( + accounts_with_individual_criteria, + criteria_total, + ); + + let unchecked_for_disqualified = + match self.handle_possibly_outweighed_account(accounts_with_unchecked_adjustment) { + Left(still_not_fully_checked) => still_not_fully_checked, + Right(with_some_outweighed) => return with_some_outweighed, + }; + + let finalized_accounts = + match Self::consider_account_disqualification_from_percentage_insignificance( + unchecked_for_disqualified, + &self.logger, + ) { + Left(adjusted_accounts) => adjusted_accounts, + Right(with_some_disqualified) => return with_some_disqualified, + }; + + AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) + } + + fn compute_accounts_with_unchecked_adjustment( + &mut self, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + criteria_total: u128, + ) -> Vec { let cw_masq_balance = self.inner.cw_masq_balance(); let multiplication_coeff = compute_fractions_preventing_mul_coeff(cw_masq_balance, criteria_total); @@ -548,7 +588,7 @@ impl PaymentAdjusterReal { .checked_div(criteria_total_u256) .expect("div overflow"); - let accounts_with_unchecked_adjustment: Vec<_> = accounts_with_individual_criteria + accounts_with_individual_criteria .into_iter() .map(|(criteria_sum, account)| { let proposed_adjusted_balance = (U256::from(criteria_sum) @@ -565,77 +605,44 @@ impl PaymentAdjusterReal { criteria_sum, ) }) - .collect(); - - let unchecked_for_disqualified = - match self.handle_possibly_outweighed_account(accounts_with_unchecked_adjustment) { - Left(still_not_fully_checked) => still_not_fully_checked, - Right(with_some_outweighed) => return with_some_outweighed, - }; - - let finalized_accounts = - match Self::consider_account_disqualification_from_percentage_insignificance( - unchecked_for_disqualified, - ) { - Left(adjusted_accounts) => adjusted_accounts, - Right(with_some_disqualified) => return with_some_disqualified, - }; - - AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) + .collect() } fn consider_account_disqualification_from_percentage_insignificance( accounts_with_unchecked_adjustment: Vec, + logger: &Logger, ) -> Either, AccountsRecreationResult> { - let wallets_of_accounts_to_disqualify: Vec = accounts_with_unchecked_adjustment - .iter() - .flat_map(|account_info| { - let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; - let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; - if proposed_adjusted_balance <= balance_at_the_edge { - diagnostics!( - &account_info.original_account.wallet, - "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", - || format!( - "proposed adjusted balance: {}, qualification limit: {}", - proposed_adjusted_balance, balance_at_the_edge - ) - ); - - Some(account_info.original_account.wallet.clone()) - } else { - None - } - }) - .collect(); - if wallets_of_accounts_to_disqualify.is_empty() { - let finalized_accounts = AccountWithUncheckedAdjustment::finalize_collection( - accounts_with_unchecked_adjustment, - DecidedPayableAccountResolution::Finalize, + if let Some(disq_account_wallet) = + Self::maybe_find_an_account_to_disqualify_in_this_iteration( + &accounts_with_unchecked_adjustment, + logger, + ) + { + let init = ( + None, + Vec::with_capacity(accounts_with_unchecked_adjustment.len() - 1), ); - Left(finalized_accounts) - } else { - let (disqualified, remaining): ( - Vec, - Vec, - ) = accounts_with_unchecked_adjustment - .into_iter() - .partition(|account_info| { - wallets_of_accounts_to_disqualify - .contains(&account_info.original_account.wallet) - }); - let debugable_disqualified = disqualified - .into_iter() - .map(|account_info| { - DisqualifiedPayableAccount::new( - account_info.original_account.wallet, - account_info.original_account.balance_wei, - account_info.proposed_adjusted_balance, - ) - }) - .collect(); + let (single_disqualified, remaining) = + accounts_with_unchecked_adjustment.into_iter().fold( + init, + |(disqualified_acc_opt, mut remaining_accounts), current_account| { + if current_account.original_account.wallet == disq_account_wallet { + (Some(current_account), remaining_accounts) + } else { + remaining_accounts.push(current_account); + (disqualified_acc_opt, remaining_accounts) + } + }, + ); + let debugable_disqualified = { + let account_info = + single_disqualified.expect("already verified disqualified account is gone"); + DisqualifiedPayableAccount::new( + account_info.original_account.wallet, + account_info.original_account.balance_wei, + account_info.proposed_adjusted_balance, + ) + }; let remaining_stripped_off = remaining .into_iter() .map(|account_info| { @@ -646,9 +653,58 @@ impl PaymentAdjusterReal { disqualified: debugable_disqualified, remaining: remaining_stripped_off, }) + } else { + let finalized_accounts = AccountWithUncheckedAdjustment::finalize_collection( + accounts_with_unchecked_adjustment, + DecidedPayableAccountResolution::Finalize, + ); + Left(finalized_accounts) } } + fn maybe_find_an_account_to_disqualify_in_this_iteration( + accounts_with_unchecked_adjustment: &[AccountWithUncheckedAdjustment], + logger: &Logger, + ) -> Option { + let disqualifiable_accounts: Vec<&AccountWithUncheckedAdjustment> = + accounts_with_unchecked_adjustment + .iter() + .flat_map(|account_info| { + let original_balance = account_info.original_account.balance_wei; + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + if proposed_adjusted_balance <= balance_at_the_edge { + diagnostics!( + &account_info.original_account.wallet, + "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", + || format!( + "proposed: {}, qualification limit: {}", + proposed_adjusted_balance, balance_at_the_edge + ) + ); + + Some(&*account_info) + } else { + None + } + }) + .collect(); + disqualifiable_accounts.is_empty().not().then(|| { + let wallet = + find_disqualified_account_with_smallest_proposed_balance(&disqualifiable_accounts); + trace!( + logger, + "Found accounts {:?} whose proposed new, adjusted balances laid under \ + the limit for disqualification. Choose the least desirable proposal of account {} to \ + be thrown away in this iteration.", + disqualifiable_accounts, + wallet + ); + wallet + }) + } + fn handle_possibly_outweighed_account( &mut self, accounts_with_unchecked_adjustment: Vec, @@ -837,7 +893,7 @@ impl PaymentAdjusterReal { enum AccountsRecreationResult { AllAccountsCleanlyProcessed(Vec), InsignificantAccounts { - disqualified: Vec, + disqualified: DisqualifiedPayableAccount, remaining: Vec, }, OutweighedAccounts { @@ -850,7 +906,7 @@ enum AccountsRecreationResult { struct AdjustmentIterationSummary { decided_accounts: Vec, remaining_accounts: Vec, - disqualified_accounts: Vec, + disqualified_account_opt: Option, } //TODO rename??? @@ -887,6 +943,7 @@ impl } } +#[derive(Debug, Clone)] pub struct AccountWithUncheckedAdjustment { original_account: PayableAccount, proposed_adjusted_balance: u128, @@ -1211,68 +1268,69 @@ mod tests { #[test] fn consider_account_disqualification_from_percentage_insignificance_adheres_to_the_manifest_consts_of_insignificance( ) { - let cw_masq_balance = 1_000_000; - let mut subject = make_initialized_subject(SystemTime::now(), Some(cw_masq_balance), None); - let account_balance = 1_000_000; - let prepare_account = |n: u64| { - let mut account = make_payable_account(n); - account.balance_wei = account_balance; - account - }; - let payable_account_1 = prepare_account(1); - let wallet_1 = payable_account_1.wallet.clone(); - let payable_account_2 = prepare_account(2); - let wallet_2 = payable_account_2.wallet.clone(); - let payable_account_3 = prepare_account(3); - let wallet_3 = payable_account_3.wallet.clone(); - const IRRELEVANT_CRITERIA_SUM: u128 = 1111; - let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; - let proposed_ok_balance = edge + 1; - let account_info_1 = AccountWithUncheckedAdjustment::new( - payable_account_1, - proposed_ok_balance, - IRRELEVANT_CRITERIA_SUM, - ); - let proposed_bad_balance_because_equal = edge; - let account_info_2 = AccountWithUncheckedAdjustment::new( - payable_account_2, - proposed_bad_balance_because_equal, - IRRELEVANT_CRITERIA_SUM, - ); - let proposed_bad_balance_because_smaller = edge - 1; - let account_info_3 = AccountWithUncheckedAdjustment::new( - payable_account_3, - proposed_bad_balance_because_smaller, - IRRELEVANT_CRITERIA_SUM, - ); - let accounts_with_unchecked_adjustment = - vec![account_info_1, account_info_2, account_info_3]; - - let result = - PaymentAdjusterReal::consider_account_disqualification_from_percentage_insignificance( - accounts_with_unchecked_adjustment, - ) - .right() - .unwrap(); - - let (disqualified, remaining) = match result { - AccountsRecreationResult::InsignificantAccounts { - disqualified, - remaining, - } => (disqualified, remaining), - x => panic!( - "we expected some disqualified accounts but got this: {:?}", - x - ), - }; - let expected_disqualified_accounts = vec![wallet_2, wallet_3]; - disqualified.iter().for_each(|account_info| { - assert!(expected_disqualified_accounts.contains(&account_info.wallet)) - }); - assert_eq!(remaining[0].wallet, wallet_1); - assert_eq!(disqualified.len(), 2); - assert_eq!(remaining.len(), 1); + todo!("rewrite me when you have more energy") + // let cw_masq_balance = 1_000_000; + // let mut subject = make_initialized_subject(SystemTime::now(), Some(cw_masq_balance), None); + // let account_balance = 1_000_000; + // let prepare_account = |n: u64| { + // let mut account = make_payable_account(n); + // account.balance_wei = account_balance; + // account + // }; + // let payable_account_1 = prepare_account(1); + // let wallet_1 = payable_account_1.wallet.clone(); + // let payable_account_2 = prepare_account(2); + // let wallet_2 = payable_account_2.wallet.clone(); + // let payable_account_3 = prepare_account(3); + // let wallet_3 = payable_account_3.wallet.clone(); + // const IRRELEVANT_CRITERIA_SUM: u128 = 1111; + // let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + // * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + // let proposed_ok_balance = edge + 1; + // let account_info_1 = AccountWithUncheckedAdjustment::new( + // payable_account_1, + // proposed_ok_balance, + // IRRELEVANT_CRITERIA_SUM, + // ); + // let proposed_bad_balance_because_equal = edge; + // let account_info_2 = AccountWithUncheckedAdjustment::new( + // payable_account_2, + // proposed_bad_balance_because_equal, + // IRRELEVANT_CRITERIA_SUM, + // ); + // let proposed_bad_balance_because_smaller = edge - 1; + // let account_info_3 = AccountWithUncheckedAdjustment::new( + // payable_account_3, + // proposed_bad_balance_because_smaller, + // IRRELEVANT_CRITERIA_SUM, + // ); + // let accounts_with_unchecked_adjustment = + // vec![account_info_1, account_info_2, account_info_3]; + // + // let result = + // PaymentAdjusterReal::consider_account_disqualification_from_percentage_insignificance( + // accounts_with_unchecked_adjustment, + // ) + // .right() + // .unwrap(); + // + // let (disqualified_account, remaining) = match result { + // AccountsRecreationResult::InsignificantAccounts { + // disqualified, + // remaining, + // } => (disqualified, remaining), + // x => panic!( + // "we expected some disqualified accounts but got this: {:?}", + // x + // ), + // }; + // let expected_disqualified_accounts = vec![wallet_2, wallet_3]; + // .iter().for_each(|account_info| { + // assert!(expected_disqualified_accounts.contains(&disqualified_account.wallet)) + // }); + // assert_eq!(remaining[0].wallet, wallet_1); + // assert_eq!(disqualified_account.len(), 2); + // assert_eq!(remaining.len(), 1); } #[test] @@ -1298,10 +1356,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let zero_criteria_accounts = - PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - - let weights_and_accounts = subject.apply_criteria(zero_criteria_accounts); + let weights_and_accounts = subject.add_criteria_sums_to_accounts(qualified_payables); let only_accounts = weights_and_accounts .iter() @@ -1368,6 +1423,59 @@ mod tests { ) } + #[test] + fn only_the_least_demanding_disqualified_account_is_picked_at_a_time_even_though_more_of_them_can_be_found( + ) { + let test_name = "only_the_least_demanding_disqualified_account_is_picked_at_a_time_even_though_more_of_them_can_be_found"; + let now = SystemTime::now(); + let cw_masq_balance = 1_000_000_000_000_000_000; + let logger = Logger::new(test_name); + let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 600_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 8_000_000_000_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(300_000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(800)).unwrap(), + pending_payable_opt: None, + }; + let wallet_4 = make_wallet("jkl"); + let account_4 = PayableAccount { + wallet: wallet_4.clone(), + balance_wei: 700_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + let accounts_with_individual_criteria = + subject.add_criteria_sums_to_accounts(vec![account_1, account_2, account_3]); + let criteria_total = criteria_total(&accounts_with_individual_criteria); + let accounts_with_unchecked_adjustment = subject + .compute_accounts_with_unchecked_adjustment( + accounts_with_individual_criteria, + criteria_total, + ); + + let result = PaymentAdjusterReal::maybe_find_an_account_to_disqualify_in_this_iteration( + &accounts_with_unchecked_adjustment, + &logger, + ); + + assert_eq!(result, Some(wallet_3)); + } + #[test] fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { const SAFETY_MULTIPLIER: u128 = 1_000_000_000_000_000; @@ -1396,9 +1504,7 @@ mod tests { let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important - let zero_criteria_accounts = - PaymentAdjusterReal::initialize_zero_criteria(qualified_payables); - let criteria_and_accounts = subject.apply_criteria(zero_criteria_accounts); + let criteria_and_accounts = subject.add_criteria_sums_to_accounts(qualified_payables); let criteria_total = criteria_total(&criteria_and_accounts); let account_2_criterion = criteria_and_accounts[1].0; let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; @@ -1426,13 +1532,13 @@ mod tests { } #[test] - fn an_accounts_never_becomes_outweighed_while_cw_balance_is_less_than_its_balance_because_disqualified_accounts_come_considered_first( + fn an_accounts_never_becomes_outweighed_while_cw_balance_does_not_have_enough_balance_for_it_because_disqualified_accounts_come_handled_first( ) { //NOTE that the same applies for more than one outweighed accounts that would originally request more than the cw balance, //therefore there is no such a test either const SECONDS_IN_3_DAYS: u64 = 259_200; let test_name = - "only_outweighed_account_gets_its_balance_cut_back_if_cw_balance_is_even_less"; + "an_accounts_never_becomes_outweighed_while_cw_balance_does_not_have_enough_balance_for_it_because_disqualified_accounts_come_handled_first"; let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; let mut subject = make_initialized_subject( @@ -1456,11 +1562,8 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - let accounts_with_zero_criteria = PaymentAdjusterReal::initialize_zero_criteria(vec![ - account_1.clone(), - account_2.clone(), - ]); - let accounts_with_individual_criteria = subject.apply_criteria(accounts_with_zero_criteria); + let accounts = vec![account_1.clone(), account_2.clone()]; + let accounts_with_individual_criteria = subject.add_criteria_sums_to_accounts(accounts); let required_balance_total = balance_total(&accounts_with_individual_criteria); let criteria_total = criteria_total(&accounts_with_individual_criteria); @@ -1478,10 +1581,10 @@ mod tests { }; let expected_disqualified_account = DisqualifiedPayableAccount { wallet: wallet_2, - proposed_adjusted_balance: 49_201, + proposed_adjusted_balance: 7_871_319_192, original_balance: balance_2, }; - assert_eq!(disqualified, vec![expected_disqualified_account]); + assert_eq!(disqualified, expected_disqualified_account); assert_eq!(remaining, vec![account_1]) } @@ -1529,19 +1632,26 @@ mod tests { //because the proposed final balances all all way lower than (at least) the half of the original balances assert_eq!(result.accounts, vec![]); - let expected_log = |wallet: &str| { + let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!("INFO: {test_name}: Consuming wallet low in MASQ balance. Recently qualified \ - payable for wallet {} will not be paid as the consuming wallet handles to provide only 333 wei \ - which is not at least more than a half of the original debt {}", wallet, - (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 2).separate_with_commas()) + payable for wallet {wallet} will not be paid as the consuming wallet handles to provide only {\ + proposed_adjusted_balance_in_this_iteration} wei which is not at least more than a half of \ + the original debt {}", (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 2).separate_with_commas()) }; let log_handler = TestLogHandler::new(); - log_handler - .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616830")); - log_handler - .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616831")); - log_handler - .exists_log_containing(&expected_log("0x000000000000000000000000000000626c616832")); + // notice that the proposals grow by dropping one disqualified account in each iteration + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616832", + 333, + )); + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616831", + 499, + )); + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616830", + 999, + )); } #[test] @@ -1787,12 +1897,13 @@ mod tests { consuming_wallet_masq_balance_wei.as_u128(), now, ); - let wallets_of_final_accounts = result - .accounts - .iter() - .map(|account| account.wallet.clone()) - .collect::>(); - assert_eq!(wallets_of_final_accounts, vec![wallet_1, wallet_2]); + //TODO delete this garbage + // let wallets_of_final_accounts = result + // .accounts + // .iter() + // .map(|account| account.wallet.clone()) + // .collect::>(); + // assert_eq!(wallets_of_final_accounts, vec![wallet_1, wallet_2]); assert_eq!(result.accounts, expected_accounts); assert_eq!( result.response_skeleton_opt, @@ -1807,36 +1918,46 @@ mod tests { which is not at least more than a half of the original debt 600,000,000")); } - fn test_competitive_accounts( - test_name_with_unique_description: &str, - consuming_wallet_balance: u128, - wallet_1: &Wallet, - wallet_2: &Wallet, + struct CompetitiveAccountsTestInputFeeder<'a> { + common: CommonInput<'a>, balance_account_1: u128, balance_account_2: u128, age_secs_account_1: u64, age_secs_account_2: u64, - ) -> Vec { + } + + #[derive(Clone, Copy)] + struct CommonInput<'a> { + cw_wallet_balance: u128, + wallet_1: &'a Wallet, + wallet_2: &'a Wallet, + } + + fn test_competitive_accounts<'a>( + test_scenario_name: &str, + inputs: CompetitiveAccountsTestInputFeeder, + expected_winning_account: &'a Wallet, + ) { let now = SystemTime::now(); let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: balance_account_1, + wallet: inputs.common.wallet_1.clone(), + balance_wei: inputs.balance_account_1, last_paid_timestamp: now - .checked_sub(Duration::from_secs(age_secs_account_1)) + .checked_sub(Duration::from_secs(inputs.age_secs_account_1)) .unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: balance_account_2, + wallet: inputs.common.wallet_2.clone(), + balance_wei: inputs.balance_account_2, last_paid_timestamp: now - .checked_sub(Duration::from_secs(age_secs_account_2)) + .checked_sub(Duration::from_secs(inputs.age_secs_account_2)) .unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = U256::from(consuming_wallet_balance); + let consuming_wallet_masq_balance_wei = U256::from(inputs.common.cw_wallet_balance); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1856,21 +1977,31 @@ mod tests { adjustment: Adjustment::MasqToken, }; - subject - .adjust_payments( - adjustment_setup, - now, - &Logger::new(test_name_with_unique_description), - ) - .accounts + let mut result = subject + .adjust_payments(adjustment_setup, now, &Logger::new(test_scenario_name)) + .accounts; + + let winning_account = result.remove(0); + assert_eq!( + &winning_account.wallet, expected_winning_account, + "{}: expected {} but got {}", + test_scenario_name, winning_account.wallet, expected_winning_account + ); + assert_eq!( + winning_account.balance_wei, inputs.common.cw_wallet_balance, + "{}: expected full cw balance {}, but the account had {}", + test_scenario_name, winning_account.balance_wei, inputs.common.cw_wallet_balance + ); + assert!( + result.is_empty(), + "{}: is not empty, {:?} remains", + test_scenario_name, + result + ) } - //TODO write this test for balances under 1M and above #[test] fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { - // accounts in this test are evenly significant and so one cannot compete another, - // yet there is not enough balance to pay the minimum required which is a half of each - // thus we conclude none can be paid fn merge_test_name_and_study_description(test_name: &str, description: &str) -> String { format!("{}/{}", test_name, description) } @@ -1880,60 +2011,62 @@ mod tests { let wallet_2 = make_wallet("cdef"); let balance_account_1 = 100_000_000_000_000; let balance_account_2 = 100_000_000_000_000; - let age_account_1 = 12000; - let age_account_2 = 12000; - let first_scenario_name = merge_test_name_and_study_description(test_name, "when_equal"); - + let age_secs_account_1 = 12000; + let age_secs_account_2 = 12000; + let common_input = CommonInput { + cw_wallet_balance: consuming_wallet_balance, + wallet_1: &wallet_1, + wallet_2: &wallet_2, + }; // scenario A - let result = test_competitive_accounts( + let first_scenario_name = merge_test_name_and_study_description(test_name, "when equal"); + let expected_winning_account = &wallet_2; + + let mut result = test_competitive_accounts( &first_scenario_name, - consuming_wallet_balance, - //TODO many args in these tests are always the same...refactor - &wallet_1, - &wallet_2, - balance_account_1, - balance_account_2, - age_account_1, - age_account_2, + CompetitiveAccountsTestInputFeeder { + common: common_input, + balance_account_1, + balance_account_2, + age_secs_account_1, + age_secs_account_2, + }, + expected_winning_account, ); - assert_eq!(result, vec![]); // scenario B - const TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED: u128 = 5_000_000; let second_scenario_name = - merge_test_name_and_study_description(test_name, "first_heavier_by_balance"); + merge_test_name_and_study_description(test_name, "first heavier by balance"); + let expected_winning_account = &wallet_1; - let result = test_competitive_accounts( + let mut result = test_competitive_accounts( &second_scenario_name, - consuming_wallet_balance, - &wallet_1, - &wallet_2, - balance_account_1 + TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED, - balance_account_2 - TOLERATED_MAXIMAL_INEFFECTIVE_BALANCE_GAP_HALVED, - age_account_1, - age_account_2, + CompetitiveAccountsTestInputFeeder { + common: common_input, + balance_account_1: balance_account_1 + 1, + balance_account_2, + age_secs_account_1, + age_secs_account_2, + }, + expected_winning_account, ); - assert_eq!(result[0].wallet, wallet_1); - assert_eq!(result.len(), 1); // scenario C - const TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED: u64 = 30; let third_scenario_name = - merge_test_name_and_study_description(test_name, "second_heavier_by_age"); + merge_test_name_and_study_description(test_name, "second heavier by age"); + let expected_winning_account = &wallet_2; let result = test_competitive_accounts( &third_scenario_name, - consuming_wallet_balance, - &wallet_1, - &wallet_2, - balance_account_1, - balance_account_2, - age_account_1 - TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED, - age_account_2 + TOLERATED_MAXIMAL_INEFFECTIVE_AGE_GAP_SEC_HALVED, + CompetitiveAccountsTestInputFeeder { + common: common_input, + balance_account_1, + balance_account_2, + age_secs_account_1, + age_secs_account_2: age_secs_account_2 + 1, + }, + expected_winning_account, ); - - assert_eq!(result[0].wallet, wallet_2); - assert_eq!(result.len(), 1) } //TODO do I really want to delete this test? Why? I probably don't @@ -2118,19 +2251,20 @@ mod tests { }, }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let mut result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let only_account = result.accounts.remove(0); + assert_eq!( + only_account, + PayableAccount { + wallet: wallet_3, + balance_wei: consuming_wallet_masq_balance, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + } + ); assert_eq!(result.accounts.len(), 1); assert_eq!(result.response_skeleton_opt, None); - let only_account = &result.accounts[0]; - assert_eq!(&only_account.wallet, &wallet_3); - assert!( - ((300_000_000_000_000 * 1000) / 1001) <= only_account.balance_wei - && only_account.balance_wei <= 300_000_000_000_000 - ); - assert_eq!(only_account.last_paid_timestamp, last_paid_timestamp_3); - assert_eq!(only_account.pending_payable_opt, None); - //TODO if there is the only account remaining why don't we use the exact value...just the original balance..we would fit easily let log_msg = format!( "DEBUG: {test_name}: \n\ @@ -2140,13 +2274,14 @@ mod tests { | Adjusted | |0x000000000000000000000000000000000067686b 333000000000000 -| 299999993982547 +| 300000000000000 | |Ignored minor payables Original | |0x0000000000000000000000000000000000616263 10000000000000 |0x0000000000000000000000000000000000646566 55000000000" ); + //TODO we shouldn't call them "minor payables"...they sometimes are huge but disqualified TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } From 9d7d6ba1ae2c0dce126a33ec668ca820c3e58743 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 27 Jul 2023 12:10:16 +0200 Subject: [PATCH 050/250] GH-711: exhastive accounts outruling imperfections from precision hardly done right --- .../payment_adjuster/auxiliary_fns.rs | 233 +++++++++++++++++- node/src/accountant/payment_adjuster/inner.rs | 57 +++-- node/src/accountant/payment_adjuster/mod.rs | 191 ++++++-------- 3 files changed, 344 insertions(+), 137 deletions(-) diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index 30743f5fe..ae0788b84 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -2,7 +2,8 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ - AccountWithUncheckedAdjustment, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, }; use crate::diagnostics; use crate::sub_lib::wallet::Wallet; @@ -59,7 +60,7 @@ pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_su } pub fn find_disqualified_account_with_smallest_proposed_balance( - accounts: &[&AccountWithUncheckedAdjustment], + accounts: &[&AdjustedAccountBeforeFinalization], ) -> Wallet { let account_ref = accounts.iter().reduce(|previous, current| { if current.proposed_adjusted_balance <= previous.proposed_adjusted_balance { @@ -75,6 +76,81 @@ pub fn find_disqualified_account_with_smallest_proposed_balance( .clone() } +struct ExhaustionStatus { + remainder: u128, + already_finalized_accounts: Vec, +} + +impl ExhaustionStatus { + fn new(remainder: u128) -> Self { + Self { + remainder, + already_finalized_accounts: vec![], + } + } + + fn update( + mut self, + mut unfinalized_account_info: AdjustedAccountBeforeFinalization, + adjusted_balance_possible_addition: u128, + ) -> Self { + let corrected_adjusted_account_before_finalization = { + unfinalized_account_info.proposed_adjusted_balance = unfinalized_account_info + .proposed_adjusted_balance + + adjusted_balance_possible_addition; + unfinalized_account_info + }; + let finalized_account = PayableAccount::from(( + corrected_adjusted_account_before_finalization, + DecidedPayableAccountResolution::Finalize, + )); + self.remainder = self + .remainder + .checked_sub(adjusted_balance_possible_addition) + .unwrap_or(0); //TODO wait for overflow + self.already_finalized_accounts.push(finalized_account); + self + } +} + +pub fn exhaust_cw_balance_as_much_as_possible( + verified_accounts: Vec, + unallocated_cw_masq_balance: u128, +) -> Vec { + let adjusted_balances_total: u128 = sum_as(&verified_accounts, |account_info| { + account_info.proposed_adjusted_balance + }); + let remainder = unallocated_cw_masq_balance - adjusted_balances_total; + let init = ExhaustionStatus::new(remainder); + verified_accounts + .into_iter() + .sorted_by(|info_a, info_b| { + Ord::cmp( + &info_a.proposed_adjusted_balance, + &info_b.proposed_adjusted_balance, + ) + }) + .fold(init, |mut status, unfinalized_account_info| { + if status.remainder != 0 { + let balance_gap = unfinalized_account_info.original_account.balance_wei + - unfinalized_account_info.proposed_adjusted_balance; + eprintln!( + "balance gap: {}, unallocated_cw_balance {}", + balance_gap, status.remainder + ); + let adjusted_balance_possible_addition = if balance_gap < status.remainder { + balance_gap + } else { + status.remainder + }; + status.update(unfinalized_account_info, adjusted_balance_possible_addition) + } else { + status + } + }) + .already_finalized_accounts +} + pub fn drop_criteria_and_leave_accounts( accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { @@ -128,20 +204,22 @@ pub fn x_or_1(x: u128) -> u128 { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fractions_preventing_mul_coeff, - find_disqualified_account_with_smallest_proposed_balance, log_10, log_2, + compute_fractions_preventing_mul_coeff, exhaust_cw_balance_as_much_as_possible, + find_disqualified_account_with_smallest_proposed_balance, log_10, log_2, ExhaustionStatus, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - AccountWithUncheckedAdjustment, PaymentAdjusterReal, + AdjustedAccountBeforeFinalization, PaymentAdjusterReal, }; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; + use masq_lib::messages::ScanType::Payables; + use std::collections::HashMap; use std::time::{Duration, SystemTime}; #[test] @@ -309,7 +387,7 @@ mod tests { #[test] fn find_disqualified_account_with_smallest_proposed_balance_when_accounts_with_equal_balances() { - let account_info = AccountWithUncheckedAdjustment { + let account_info = AdjustedAccountBeforeFinalization { original_account: make_payable_account(111), proposed_adjusted_balance: 1_234_567_890, criteria_sum: 400_000_000, @@ -327,6 +405,149 @@ mod tests { assert_eq!(result, wallet_2) } + fn make_unfinalized_adjusted_account( + wallet: &Wallet, + original_balance: u128, + proposed_adjusted_balance: u128, + ) -> AdjustedAccountBeforeFinalization { + AdjustedAccountBeforeFinalization { + original_account: PayableAccount { + wallet: wallet.clone(), + balance_wei: original_balance, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }, + proposed_adjusted_balance, + criteria_sum: 123456, + } + } + + fn assert_correct_payable_accounts_after_finalization( + actual_accounts: Vec, + expected_parameters: Vec<(Wallet, u128)>, + ) { + let expected_in_map: HashMap = + HashMap::from_iter(expected_parameters.into_iter()); + actual_accounts.into_iter().for_each(|account| { + let wallet = &account.wallet; + let expected_balance = expected_in_map.get(wallet).unwrap_or_else(|| { + panic!("account for wallet {} is missing among the results", wallet) + }); + assert_eq!( + account.balance_wei, *expected_balance, + "account {} should've had adjusted balance {} but has {}", + wallet, expected_balance, account.balance_wei + ) + }) + } + + #[test] + fn exhaustive_status_is_constructed_properly() { + let cw_balance_remainder = 45678; + + let result = ExhaustionStatus::new(cw_balance_remainder); + + assert_eq!(result.remainder, cw_balance_remainder); + assert_eq!(result.already_finalized_accounts, vec![]) + } + + #[test] + fn exhaust_cw_balance_as_much_as_possible_for_three_non_exhaustive_accounts() { + // this can happen because some of the pre-qualified accounts could be + // eliminated for an insignificant pay and free the means for the other + // accounts and then we went through adjustment computation with some + // losses on precision, here we're gonna add in what was missing + let wallet_1 = make_wallet("abc"); + let original_requested_balance_1 = 45_000_000_000; + let proposed_adjusted_balance_1 = 44_999_897_000; + let wallet_2 = make_wallet("def"); + let original_requested_balance_2 = 33_500_000_000; + let proposed_adjusted_balance_2 = 33_487_999_999; + let wallet_3 = make_wallet("ghi"); + let original_requested_balance_3 = 41_000_000; + let proposed_adjusted_balance_3 = 40_980_000; + let unallocated_cw_balance = original_requested_balance_1 + + original_requested_balance_2 + + original_requested_balance_3 + + 5000; + let unfinalized_adjusted_accounts = vec![ + make_unfinalized_adjusted_account( + &wallet_1, + original_requested_balance_1, + proposed_adjusted_balance_1, + ), + make_unfinalized_adjusted_account( + &wallet_2, + original_requested_balance_2, + proposed_adjusted_balance_2, + ), + make_unfinalized_adjusted_account( + &wallet_3, + original_requested_balance_3, + proposed_adjusted_balance_3, + ), + ]; + + let result = exhaust_cw_balance_as_much_as_possible( + unfinalized_adjusted_accounts, + unallocated_cw_balance, + ); + + let expected_resulted_balances = vec![ + (wallet_1, original_requested_balance_1), + (wallet_2, original_requested_balance_2), + (wallet_3, original_requested_balance_3), + ]; + assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances) + } + + #[test] + fn exhaust_cw_balance_as_much_as_possible_for_three_non_exhaustive_with_not_enough_to_fulfil_them_all( + ) { + let wallet_1 = make_wallet("abc"); + let original_requested_balance_1 = 54_000_000_000; + let proposed_adjusted_balance_1 = 53_898_000_000; + let wallet_2 = make_wallet("def"); + let original_requested_balance_2 = 33_500_000_000; + let proposed_adjusted_balance_2 = 33_487_999_999; + let wallet_3 = make_wallet("ghi"); + let original_requested_balance_3 = 41_000_000; + let proposed_adjusted_balance_3 = 40_980_000; + let unallocated_cw_balance = original_requested_balance_2 + + original_requested_balance_3 + + proposed_adjusted_balance_1 + + 2_000_000; + let unfinalized_adjusted_accounts = vec![ + make_unfinalized_adjusted_account( + &wallet_1, + original_requested_balance_1, + proposed_adjusted_balance_1, + ), + make_unfinalized_adjusted_account( + &wallet_2, + original_requested_balance_2, + proposed_adjusted_balance_2, + ), + make_unfinalized_adjusted_account( + &wallet_3, + original_requested_balance_3, + proposed_adjusted_balance_3, + ), + ]; + + let result = exhaust_cw_balance_as_much_as_possible( + unfinalized_adjusted_accounts, + unallocated_cw_balance, + ); + + let expected_resulted_balances = vec![ + (wallet_1, 53_900_000_000), + (wallet_2, original_requested_balance_2), + (wallet_3, original_requested_balance_3), + ]; + assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances) + } + fn get_extreme_criteria_and_initial_accounts_order( months_of_debt_and_balances_matrix: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index eb14d1702..2378d7756 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -9,20 +9,20 @@ pub trait PaymentAdjusterInner { fn gas_limitation_opt(&self) -> Option { PaymentAdjusterInnerNull::panicking_operation("gas_limitation_opt()") } - fn cw_masq_balance(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("cw_masq_balance()") + fn unallocated_cw_masq_balance(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance()") } //TODO this method should use RefCell internally...and we could have PaymentAdjuster with &self instead of &mut self - fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation("lower_remaining_cw_balance()") + fn lower_unallocated_cw_balance(&mut self, subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation("lower_unallocated_cw_balance()") } } pub struct PaymentAdjusterInnerReal { now: SystemTime, gas_limitation_opt: Option, - cw_masq_balance: u128, + unallocated_cw_masq_balance: u128, } impl PaymentAdjusterInnerReal { @@ -30,7 +30,7 @@ impl PaymentAdjusterInnerReal { Self { now, gas_limitation_opt, - cw_masq_balance, + unallocated_cw_masq_balance: cw_masq_balance, } } } @@ -42,16 +42,16 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn gas_limitation_opt(&self) -> Option { self.gas_limitation_opt } - fn cw_masq_balance(&self) -> u128 { - self.cw_masq_balance + fn unallocated_cw_masq_balance(&self) -> u128 { + self.unallocated_cw_masq_balance } - fn lower_remaining_cw_balance(&mut self, subtrahend: u128) { + fn lower_unallocated_cw_balance(&mut self, subtrahend: u128) { let lowered_theoretical_cw_balance = self - .cw_masq_balance + .unallocated_cw_masq_balance .checked_sub(subtrahend) .expect("should always subtract a small enough amount"); - self.cw_masq_balance = lowered_theoretical_cw_balance + self.unallocated_cw_masq_balance = lowered_theoretical_cw_balance } } @@ -71,8 +71,21 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull {} #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInner, PaymentAdjusterInnerNull, + PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; + use std::time::SystemTime; + + #[test] + fn inner_real_is_constructed_correctly() { + let now = SystemTime::now(); + let gas_limitation_opt = Some(3); + let cw_masq_balance = 123_456_789; + let result = PaymentAdjusterInnerReal::new(now, gas_limitation_opt, cw_masq_balance); + + assert_eq!(result.now, now); + assert_eq!(result.gas_limitation_opt, gas_limitation_opt); + assert_eq!(result.unallocated_cw_masq_balance, cw_masq_balance) + } #[test] #[should_panic( @@ -86,31 +99,31 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the cw_masq_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the gas_limitation_opt() method in PaymentAdjusterInner" )] - fn inner_null_calling_cw_masq_balance() { + fn inner_null_calling_gas_limitation_opt() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.cw_masq_balance(); + let _ = subject.gas_limitation_opt(); } #[test] #[should_panic( - expected = "Called the null implementation of the gas_limitation_opt() method in PaymentAdjusterInner" + expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" )] - fn inner_null_calling_gas_limitation_opt() { - let subject = PaymentAdjusterInnerNull {}; + fn inner_null_calling_unallocated_cw_balance() { + let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.gas_limitation_opt(); + let _ = subject.unallocated_cw_masq_balance(); } #[test] #[should_panic( - expected = "Called the null implementation of the lower_remaining_cw_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the lower_unallocated_cw_balance() method in PaymentAdjusterInner" )] - fn inner_null_calling_lower_remaining_cw_balance() { + fn inner_null_calling_lower_unallocated_cw_balance() { let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.lower_remaining_cw_balance(123); + let _ = subject.lower_unallocated_cw_balance(123); } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0b67fbc43..4a195af95 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -10,6 +10,7 @@ mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ compute_fractions_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, + exhaust_cw_balance_as_much_as_possible, find_disqualified_account_with_smallest_proposed_balance, log_2, rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, x_or_1, }; @@ -282,9 +283,12 @@ impl PaymentAdjusterReal { fn run_full_adjustment_procedure( &mut self, unresolved_qualified_accounts: Vec, - resolved_qualified_accounts: Vec, + previously_resolved_qualified_accounts: Vec, ) -> Vec { - diagnostics_collective("RESOLVED QUALIFIED ACCOUNTS:", &resolved_qualified_accounts); + diagnostics_collective( + "RESOLVED QUALIFIED ACCOUNTS:", + &previously_resolved_qualified_accounts, + ); diagnostics_collective( "UNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, @@ -293,7 +297,7 @@ impl PaymentAdjusterReal { self.add_criteria_sums_to_accounts(unresolved_qualified_accounts); self.run_adjustment_by_criteria_recursively( sorted_accounts_with_individual_criteria, - resolved_qualified_accounts, + previously_resolved_qualified_accounts, ) } @@ -309,7 +313,7 @@ impl PaymentAdjusterReal { fn run_adjustment_by_criteria_recursively( &mut self, sorted_accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - resolved_qualified_accounts: Vec, + previously_resolved_qualified_accounts: Vec, ) -> Vec { let adjustment_result: AdjustmentIterationSummary = match self.give_job_to_adjustment_workers(sorted_accounts_with_individual_criteria) { @@ -333,7 +337,7 @@ impl PaymentAdjusterReal { }; let adjusted_accounts_iter = adjusted_accounts.into_iter(); - let result: Vec = resolved_qualified_accounts + let result: Vec = previously_resolved_qualified_accounts .into_iter() .chain(adjusted_accounts_iter) .collect(); @@ -472,19 +476,24 @@ impl PaymentAdjusterReal { match Self::check_need_of_masq_balances_adjustment( &self.logger, Either::Right(&weighted_accounts_cut_by_gas), - self.inner.cw_masq_balance(), + self.inner.unallocated_cw_masq_balance(), ) { - true => AdjustmentCompletion::Continue( - self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas), - ), - false => AdjustmentCompletion::Finished(rebuild_accounts( - weighted_accounts_cut_by_gas, - )), + true => { + let attempt_awaiting_verification = + self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas); + AdjustmentCompletion::Continue(attempt_awaiting_verification) + } + false => { + let finalized_accounts = rebuild_accounts(weighted_accounts_cut_by_gas); + AdjustmentCompletion::Finished(finalized_accounts) + } } } - None => AdjustmentCompletion::Continue( - self.handle_masq_token_adjustment(accounts_with_individual_criteria), - ), + None => { + let attempt_awaiting_verification = + self.handle_masq_token_adjustment(accounts_with_individual_criteria); + AdjustmentCompletion::Continue(attempt_awaiting_verification) + } } } @@ -492,7 +501,6 @@ impl PaymentAdjusterReal { &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationSummary { - // let required_balance_total = Self::balance_total(&accounts_with_individual_criteria); let criteria_total = criteria_total(&accounts_with_individual_criteria); //TODO simplify this...one of these enums is probably redundant match self.recreate_accounts_with_proportioned_balances( @@ -523,23 +531,6 @@ impl PaymentAdjusterReal { disqualified_account_opt: None, }, } - // match self.handle_possibly_outweighed_accounts( - // accounts_with_individual_criteria, - // required_balance_total, - // criteria_total, - // ) { - // Either::Left(accounts_with_individual_criteria) => { - // self.perform_adjustment_and_determine_adjustment_iteration_result( - // accounts_with_individual_criteria, - // criteria_total, - // ) - // } - // Either::Right((outweighed, remaining)) => AdjustmentIterationSummary { - // decided_accounts: outweighed, - // remaining_accounts: remaining, - // disqualified_accounts: vec![], - // }, - // } } fn recreate_accounts_with_proportioned_balances( @@ -547,35 +538,40 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AccountsRecreationResult { - let accounts_with_unchecked_adjustment = self.compute_accounts_with_unchecked_adjustment( + let unfinalized_adjusted_accounts = self.compute_unfinalized_adjusted_accounts( accounts_with_individual_criteria, criteria_total, ); let unchecked_for_disqualified = - match self.handle_possibly_outweighed_account(accounts_with_unchecked_adjustment) { + match self.handle_possibly_outweighed_account(unfinalized_adjusted_accounts) { Left(still_not_fully_checked) => still_not_fully_checked, Right(with_some_outweighed) => return with_some_outweighed, }; - let finalized_accounts = + //TODO this need to return the wider type used during the polishing + let verified_accounts = match Self::consider_account_disqualification_from_percentage_insignificance( unchecked_for_disqualified, &self.logger, ) { - Left(adjusted_accounts) => adjusted_accounts, + Left(verified_accounts) => verified_accounts, Right(with_some_disqualified) => return with_some_disqualified, }; - AccountsRecreationResult::AllAccountsCleanlyProcessed(finalized_accounts) + let unallocated_cw_masq_balance = self.inner.unallocated_cw_masq_balance(); + let verified_and_exhaustive = + exhaust_cw_balance_as_much_as_possible(verified_accounts, unallocated_cw_masq_balance); + + AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_and_exhaustive) } - fn compute_accounts_with_unchecked_adjustment( + fn compute_unfinalized_adjusted_accounts( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, - ) -> Vec { - let cw_masq_balance = self.inner.cw_masq_balance(); + ) -> Vec { + let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); let multiplication_coeff = compute_fractions_preventing_mul_coeff(cw_masq_balance, criteria_total); let cw_masq_balance_big_u256 = U256::from(cw_masq_balance); @@ -599,7 +595,7 @@ impl PaymentAdjusterReal { diagnostics!(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { proposed_adjusted_balance.separate_with_commas() }); - AccountWithUncheckedAdjustment::new( + AdjustedAccountBeforeFinalization::new( account, proposed_adjusted_balance, criteria_sum, @@ -609,31 +605,32 @@ impl PaymentAdjusterReal { } fn consider_account_disqualification_from_percentage_insignificance( - accounts_with_unchecked_adjustment: Vec, + unfinalized_adjusted_accounts: Vec, logger: &Logger, - ) -> Either, AccountsRecreationResult> { + ) -> Either, AccountsRecreationResult> { if let Some(disq_account_wallet) = Self::maybe_find_an_account_to_disqualify_in_this_iteration( - &accounts_with_unchecked_adjustment, + &unfinalized_adjusted_accounts, logger, ) { let init = ( None, - Vec::with_capacity(accounts_with_unchecked_adjustment.len() - 1), + Vec::with_capacity(unfinalized_adjusted_accounts.len() - 1), ); - let (single_disqualified, remaining) = - accounts_with_unchecked_adjustment.into_iter().fold( - init, - |(disqualified_acc_opt, mut remaining_accounts), current_account| { - if current_account.original_account.wallet == disq_account_wallet { - (Some(current_account), remaining_accounts) - } else { - remaining_accounts.push(current_account); - (disqualified_acc_opt, remaining_accounts) - } - }, - ); + + let (single_disqualified, remaining) = unfinalized_adjusted_accounts.into_iter().fold( + init, + |(disqualified_acc_opt, mut remaining_accounts), current_account| { + if current_account.original_account.wallet == disq_account_wallet { + (Some(current_account), remaining_accounts) + } else { + remaining_accounts.push(current_account); + (disqualified_acc_opt, remaining_accounts) + } + }, + ); + let debugable_disqualified = { let account_info = single_disqualified.expect("already verified disqualified account is gone"); @@ -643,6 +640,7 @@ impl PaymentAdjusterReal { account_info.proposed_adjusted_balance, ) }; + let remaining_stripped_off = remaining .into_iter() .map(|account_info| { @@ -654,20 +652,16 @@ impl PaymentAdjusterReal { remaining: remaining_stripped_off, }) } else { - let finalized_accounts = AccountWithUncheckedAdjustment::finalize_collection( - accounts_with_unchecked_adjustment, - DecidedPayableAccountResolution::Finalize, - ); - Left(finalized_accounts) + Left(unfinalized_adjusted_accounts) } } fn maybe_find_an_account_to_disqualify_in_this_iteration( - accounts_with_unchecked_adjustment: &[AccountWithUncheckedAdjustment], + unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], logger: &Logger, ) -> Option { - let disqualifiable_accounts: Vec<&AccountWithUncheckedAdjustment> = - accounts_with_unchecked_adjustment + let disqualifiable_accounts: Vec<&AdjustedAccountBeforeFinalization> = + unfinalized_adjusted_accounts .iter() .flat_map(|account_info| { let original_balance = account_info.original_account.balance_wei; @@ -707,14 +701,14 @@ impl PaymentAdjusterReal { fn handle_possibly_outweighed_account( &mut self, - accounts_with_unchecked_adjustment: Vec, - ) -> Either, AccountsRecreationResult> { + unfinalized_adjusted_accounts: Vec, + ) -> Either, AccountsRecreationResult> { let init: ( Vec<(u128, PayableAccount)>, - Vec, + Vec, ) = (vec![], vec![]); let (outweighed_with_already_made_criteria, passing_through) = - accounts_with_unchecked_adjustment.into_iter().fold( + unfinalized_adjusted_accounts.into_iter().fold( init, |(mut outweighed, mut passing_through), account_info| { if account_info.proposed_adjusted_balance @@ -749,7 +743,7 @@ impl PaymentAdjusterReal { .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( outweighed_with_already_made_criteria, ); - let remaining = AccountWithUncheckedAdjustment::finalize_collection( + let remaining = AdjustedAccountBeforeFinalization::finalize_collection( passing_through, DecidedPayableAccountResolution::Revert, ); @@ -758,24 +752,6 @@ impl PaymentAdjusterReal { remaining, }) } - - // None => Either::Left(account_with_individual_criteria), - // Some(wallets_of_outweighed) => Either::Right({ - // eprintln!("wallets: {:?}", wallets_of_outweighed); - // debug!( - // self.logger, - // "Found outweighed accounts that will have to be readjusted ({:?}).", - // wallets_of_outweighed - // ); - // let (unchecked_outweighed, remaining): (Vec, Vec) = - // account_with_individual_criteria - // .into_iter() - // .map(|(_, account)| account) - // .partition(|account| wallets_of_outweighed.contains(&account.wallet)); - // - // (outweighed, remaining) - // }), - // } } // fn check_for_outweighed_accounts( @@ -850,7 +826,7 @@ impl PaymentAdjusterReal { mut outweighed_with_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { //TODO is this special condition for the single guy necessary?? - let cw_masq_balance = self.inner.cw_masq_balance(); + let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); if outweighed_with_criteria.len() == 1 { let (_, only_account) = outweighed_with_criteria.remove(0); if only_account.balance_wei > cw_masq_balance { @@ -879,12 +855,12 @@ impl PaymentAdjusterReal { fn adjust_cw_balance_down_for_next_round(&mut self, processed_outweighed: &[PayableAccount]) { let subtrahend_total: u128 = sum_as(processed_outweighed, |account| account.balance_wei); - self.inner.lower_remaining_cw_balance(subtrahend_total); + self.inner.lower_unallocated_cw_balance(subtrahend_total); diagnostics!("LOWERED CW BALANCE", || format!( "lowered by {} to {}", subtrahend_total, - self.inner.cw_masq_balance() + self.inner.unallocated_cw_masq_balance() )) } } @@ -923,13 +899,13 @@ enum AdjustmentCompletion { impl From<( - AccountWithUncheckedAdjustment, + AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, )> for PayableAccount { fn from( (account_info, resolution): ( - AccountWithUncheckedAdjustment, + AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, ), ) -> Self { @@ -944,13 +920,13 @@ impl } #[derive(Debug, Clone)] -pub struct AccountWithUncheckedAdjustment { +pub struct AdjustedAccountBeforeFinalization { original_account: PayableAccount, proposed_adjusted_balance: u128, criteria_sum: u128, } -impl AccountWithUncheckedAdjustment { +impl AdjustedAccountBeforeFinalization { fn new( original_account: PayableAccount, proposed_adjusted_balance: u128, @@ -1025,7 +1001,7 @@ mod tests { get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - AccountWithUncheckedAdjustment, AccountsRecreationResult, Adjustment, AnalysisError, + AccountsRecreationResult, AdjustedAccountBeforeFinalization, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, @@ -1130,12 +1106,12 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the cw_masq_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" )] fn payment_adjuster_new_is_created_with_inner_null() { let result = PaymentAdjusterReal::new(); - let _ = result.inner.cw_masq_balance(); + let _ = result.inner.unallocated_cw_masq_balance(); } #[test] @@ -1462,14 +1438,13 @@ mod tests { let accounts_with_individual_criteria = subject.add_criteria_sums_to_accounts(vec![account_1, account_2, account_3]); let criteria_total = criteria_total(&accounts_with_individual_criteria); - let accounts_with_unchecked_adjustment = subject - .compute_accounts_with_unchecked_adjustment( - accounts_with_individual_criteria, - criteria_total, - ); + let unfinalized_adjusted_accounts = subject.compute_unfinalized_adjusted_accounts( + accounts_with_individual_criteria, + criteria_total, + ); let result = PaymentAdjusterReal::maybe_find_an_account_to_disqualify_in_this_iteration( - &accounts_with_unchecked_adjustment, + &unfinalized_adjusted_accounts, &logger, ); @@ -1522,8 +1497,7 @@ mod tests { account_2, //outweighed account takes the first place PayableAccount { wallet: make_wallet("blah"), - //precisely should be 1_500_000_000_000 - (25_000_000 - 25_000_000) but close enough - balance_wei: 1_499_949_999_252, + balance_wei: 1_499_950_000_000, last_paid_timestamp: last_paid_timestamp_1, pending_payable_opt: None, }, @@ -2263,9 +2237,8 @@ mod tests { pending_payable_opt: None, } ); - assert_eq!(result.accounts.len(), 1); + assert_eq!(result.accounts.len(), 0); assert_eq!(result.response_skeleton_opt, None); - //TODO if there is the only account remaining why don't we use the exact value...just the original balance..we would fit easily let log_msg = format!( "DEBUG: {test_name}: \n\ |Account wallet Balance wei From 417d6d2c4ca67e4ddf0404e61de39f396e06530d Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 27 Jul 2023 18:52:09 +0200 Subject: [PATCH 051/250] GH-711: shuffling through the broken tests; also a big clean-up; this is a savepoint before big rearrangement in the code defining criteria --- .../payment_adjuster/auxiliary_fns.rs | 42 +- .../payment_adjuster/diagnostics.rs | 38 +- node/src/accountant/payment_adjuster/inner.rs | 4 +- .../accountant/payment_adjuster/log_fns.rs | 10 +- node/src/accountant/payment_adjuster/mod.rs | 521 +++++++----------- node/src/accountant/scanners/mod.rs | 1 - 6 files changed, 242 insertions(+), 374 deletions(-) diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index ae0788b84..833bb4a69 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -3,9 +3,7 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, }; -use crate::diagnostics; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use std::iter::successors; @@ -50,10 +48,6 @@ pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_su .checked_sub(cw_balance_digits_count) .unwrap_or(0); let safe_mul_coeff = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; - eprintln!( - "criteria sum {}, safe mul coeff {}", - criteria_sum, safe_mul_coeff - ); 10_u128 .checked_pow(safe_mul_coeff as u32) .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) @@ -62,11 +56,11 @@ pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_su pub fn find_disqualified_account_with_smallest_proposed_balance( accounts: &[&AdjustedAccountBeforeFinalization], ) -> Wallet { - let account_ref = accounts.iter().reduce(|previous, current| { - if current.proposed_adjusted_balance <= previous.proposed_adjusted_balance { - current + let account_ref = accounts.iter().reduce(|smallest_so_far, current| { + if current.proposed_adjusted_balance > smallest_so_far.proposed_adjusted_balance { + smallest_so_far } else { - previous + current } }); account_ref @@ -130,14 +124,10 @@ pub fn exhaust_cw_balance_as_much_as_possible( &info_b.proposed_adjusted_balance, ) }) - .fold(init, |mut status, unfinalized_account_info| { + .fold(init, |status, unfinalized_account_info| { if status.remainder != 0 { let balance_gap = unfinalized_account_info.original_account.balance_wei - unfinalized_account_info.proposed_adjusted_balance; - eprintln!( - "balance gap: {}, unallocated_cw_balance {}", - balance_gap, status.remainder - ); let adjusted_balance_possible_addition = if balance_gap < status.remainder { balance_gap } else { @@ -151,15 +141,6 @@ pub fn exhaust_cw_balance_as_much_as_possible( .already_finalized_accounts } -pub fn drop_criteria_and_leave_accounts( - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, -) -> Vec { - accounts_with_individual_criteria - .into_iter() - .map(|(_, account)| account) - .collect() -} - pub fn sort_in_descendant_order_by_weights( unsorted: impl Iterator, ) -> Vec<(u128, PayableAccount)> { @@ -211,16 +192,13 @@ mod tests { use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::{ - AdjustedAccountBeforeFinalization, PaymentAdjusterReal, - }; + use crate::accountant::payment_adjuster::AdjustedAccountBeforeFinalization; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use masq_lib::messages::ScanType::Payables; use std::collections::HashMap; - use std::time::{Duration, SystemTime}; + use std::time::SystemTime; #[test] fn constants_are_correct() { @@ -330,7 +308,7 @@ mod tests { .collect::>(); eprintln!("results {:?}", results); - let mut initial_accounts_order_from_the_seeds_iter = + let initial_accounts_order_from_the_seeds_iter = initial_accounts_order_from_the_seeds.iter().enumerate(); let coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs = results .into_iter() @@ -377,7 +355,7 @@ mod tests { 2962501520859680498325341824 ) ] - }; + } assert_eq!( coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs, expected_result() @@ -559,7 +537,7 @@ mod tests { .collect(); let subject = make_initialized_subject(now, None, None); // when criteria are applied the collection will get sorted and will not necessarily have to match the initial order - let criteria_and_accounts = subject.add_criteria_sums_to_accounts(accounts); + let criteria_and_accounts = subject.pair_accounts_with_summed_criteria(accounts); eprintln!("wallets in order {:?}", wallets_in_order); (criteria_and_accounts, wallets_in_order) } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index e272f3935..fc3614884 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -4,8 +4,6 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::{ COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; -use crate::sub_lib::wallet::Wallet; -use itertools::Either; use std::sync::Once; use thousands::Separable; @@ -14,36 +12,38 @@ pub static BALANCE_SINGLETON: Once = Once::new(); pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 14] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25]; -pub const fn diagnostics_x_axis_exponents_len() -> usize { - EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len() -} -pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 40; +pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; #[macro_export] macro_rules! diagnostics { - ($description: literal, $value_renderer: expr) => { - diagnostics(|| Either::Left(""), $description, $value_renderer) + ($description: literal, $($arg: tt)*) => { + diagnostics(None::String>, $description, || format!($($arg)*)) }; - ($wallet_ref: expr, $description: expr, $value_renderer: expr) => { + ($wallet_ref: expr, $description: expr, $($arg: tt)*) => { diagnostics( - || Either::Right($wallet_ref.to_string()), + Some(||$wallet_ref.to_string()), $description, - $value_renderer, + || format!($($arg)*) ) }; } -pub fn diagnostics(subject_renderer: F1, description: &str, value_renderer: F2) +pub fn diagnostics(subject_renderer_opt: Option, description: &str, value_renderer: F2) where - F1: Fn() -> Either<&'static str, String>, + F1: Fn() -> String, F2: Fn() -> String, { if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { + let subject = if let Some(subject_renderer) = subject_renderer_opt { + subject_renderer() + } else { + "".to_string() + }; eprintln!( "{: (u128, PayableAccount) { let account = &self.account.wallet; let description = format!("COMPUTED {} CRITERIA", self.diagnostics.label); - let value_renderer = || self.criterion.separate_with_commas(); - diagnostics!(account, &description, value_renderer); + diagnostics!( + account, + &description, + "{}", + self.criterion.separate_with_commas() + ); self.diagnostics.compute_progressive_characteristics(); ( diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 2378d7756..3fd653a52 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -14,7 +14,7 @@ pub trait PaymentAdjusterInner { } //TODO this method should use RefCell internally...and we could have PaymentAdjuster with &self instead of &mut self - fn lower_unallocated_cw_balance(&mut self, subtrahend: u128) { + fn lower_unallocated_cw_balance(&mut self, _subtrahend: u128) { PaymentAdjusterInnerNull::panicking_operation("lower_unallocated_cw_balance()") } } @@ -112,7 +112,7 @@ mod tests { expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" )] fn inner_null_calling_unallocated_cw_balance() { - let mut subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull {}; let _ = subject.unallocated_cw_masq_balance(); } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 893aa97b8..3e533c935 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -163,15 +163,7 @@ pub fn log_insufficient_transaction_fee_balance( #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::log_fns::{ - log_info_for_disqualified_account, REFILL_RECOMMENDATION, - }; - use crate::accountant::payment_adjuster::DisqualifiedPayableAccount; - use crate::sub_lib::wallet::Wallet; - use crate::test_utils::make_wallet; - use masq_lib::logger::Logger; - use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use thousands::Separable; + use crate::accountant::payment_adjuster::log_fns::REFILL_RECOMMENDATION; #[test] fn constants_are_correct() { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 4a195af95..1d658e244 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -15,8 +15,8 @@ use crate::accountant::payment_adjuster::auxiliary_fns::{ sort_in_descendant_order_by_weights, sum_as, x_or_1, }; use crate::accountant::payment_adjuster::diagnostics::{ - diagnostics, diagnostics_collective, diagnostics_x_axis_exponents_len, CriteriaWithDiagnostics, - DiagnosticsSetting, AGE_SINGLETON, BALANCE_SINGLETON, + diagnostics, diagnostics_collective, CriteriaWithDiagnostics, DiagnosticsSetting, + AGE_SINGLETON, BALANCE_SINGLETON, EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS, }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -32,16 +32,15 @@ use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; -use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; +use itertools::Either; use itertools::Either::{Left, Right}; -use itertools::{Either, Itertools}; -use log::logger; use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; use std::collections::HashMap; -use std::iter::{once, successors}; +use std::iter::once; use std::ops::Not; use std::time::{Duration, SystemTime}; use thousands::Separable; @@ -91,7 +90,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(None) => (), Ok(Some(limited_count_from_gas)) => { - return Ok(Some(Adjustment::TransactionFeeDefinitelyOtherMaybe { + return Ok(Some(Adjustment::PriorityTransactionFee { limited_count_from_gas, })) } @@ -142,7 +141,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); - //TODO should also return an error...in case we run adjustment but the resulted collection is empty OutcomingPaymentsInstructions { accounts: adjusted_accounts, response_skeleton_opt, @@ -166,8 +164,6 @@ const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; // this parameter affects the steepness (sensitivity on increase in balance) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; -const BALANCE_TAIL_WEIGHT_MODULO_OPERAND: u128 = 1_000; -const BALANCE_TAIL_WEIGHT_EXPONENT: u32 = 2; // represents 50% const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = PercentageAccountInsignificance { @@ -208,7 +204,7 @@ impl PaymentAdjusterReal { now: SystemTime, ) { let gas_limitation_opt = match required_adjustment { - Adjustment::TransactionFeeDefinitelyOtherMaybe { + Adjustment::PriorityTransactionFee { limited_count_from_gas, } => Some(limited_count_from_gas), Adjustment::MasqToken => None, @@ -294,14 +290,14 @@ impl PaymentAdjusterReal { &unresolved_qualified_accounts, ); let sorted_accounts_with_individual_criteria = - self.add_criteria_sums_to_accounts(unresolved_qualified_accounts); + self.pair_accounts_with_summed_criteria(unresolved_qualified_accounts); self.run_adjustment_by_criteria_recursively( sorted_accounts_with_individual_criteria, previously_resolved_qualified_accounts, ) } - fn add_criteria_sums_to_accounts( + fn pair_accounts_with_summed_criteria( &self, accounts: Vec, ) -> Vec<(u128, PayableAccount)> { @@ -426,7 +422,7 @@ impl PaymentAdjusterReal { label: "BALANCE", diagnostics_adaptive_formula: |x: u128| formula(x), singleton_ref: &BALANCE_SINGLETON, - bonds_safe_count_to_print: diagnostics_x_axis_exponents_len(), + bonds_safe_count_to_print: EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len(), }, } .diagnose_and_sum() @@ -479,9 +475,9 @@ impl PaymentAdjusterReal { self.inner.unallocated_cw_masq_balance(), ) { true => { - let attempt_awaiting_verification = + let result_awaiting_verification = self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas); - AdjustmentCompletion::Continue(attempt_awaiting_verification) + AdjustmentCompletion::Continue(result_awaiting_verification) } false => { let finalized_accounts = rebuild_accounts(weighted_accounts_cut_by_gas); @@ -490,9 +486,9 @@ impl PaymentAdjusterReal { } } None => { - let attempt_awaiting_verification = + let result_awaiting_verification = self.handle_masq_token_adjustment(accounts_with_individual_criteria); - AdjustmentCompletion::Continue(attempt_awaiting_verification) + AdjustmentCompletion::Continue(result_awaiting_verification) } } } @@ -592,9 +588,13 @@ impl PaymentAdjusterReal { / multiplication_coeff_u256) .as_u128(); - diagnostics!(&account.wallet, "PROPOSED ADJUSTED BALANCE", || { + diagnostics!( + &account.wallet, + "PROPOSED ADJUSTED BALANCE", + "{}", proposed_adjusted_balance.separate_with_commas() - }); + ); + AdjustedAccountBeforeFinalization::new( account, proposed_adjusted_balance, @@ -660,43 +660,58 @@ impl PaymentAdjusterReal { unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], logger: &Logger, ) -> Option { - let disqualifiable_accounts: Vec<&AdjustedAccountBeforeFinalization> = - unfinalized_adjusted_accounts - .iter() - .flat_map(|account_info| { - let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; - let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; - if proposed_adjusted_balance <= balance_at_the_edge { - diagnostics!( - &account_info.original_account.wallet, - "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", - || format!( - "proposed: {}, qualification limit: {}", - proposed_adjusted_balance, balance_at_the_edge - ) - ); - - Some(&*account_info) - } else { - None - } - }) - .collect(); - disqualifiable_accounts.is_empty().not().then(|| { - let wallet = - find_disqualified_account_with_smallest_proposed_balance(&disqualifiable_accounts); - trace!( - logger, - "Found accounts {:?} whose proposed new, adjusted balances laid under \ + let disqualification_suspected_accounts = + Self::list_accounts_under_the_disqualification_limit(unfinalized_adjusted_accounts); + disqualification_suspected_accounts + .is_empty() + .not() + .then(|| { + let wallet = find_disqualified_account_with_smallest_proposed_balance( + &disqualification_suspected_accounts, + ); + diagnostics!( + "PICKED DISQUALIFIED ACCOUNT", + "From {:?} picked {}", + disqualification_suspected_accounts, + wallet + ); + trace!( + logger, + "Found accounts {:?} whose proposed new, adjusted balances laid under \ the limit for disqualification. Choose the least desirable proposal of account {} to \ be thrown away in this iteration.", - disqualifiable_accounts, + disqualification_suspected_accounts, + wallet + ); wallet - ); - wallet - }) + }) + } + + fn list_accounts_under_the_disqualification_limit( + unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], + ) -> Vec<&AdjustedAccountBeforeFinalization> { + unfinalized_adjusted_accounts + .iter() + .flat_map(|account_info| { + let original_balance = account_info.original_account.balance_wei; + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + if proposed_adjusted_balance <= balance_at_the_edge { + diagnostics!( + &account_info.original_account.wallet, + "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", + "proposed: {}, qualification limit: {}", + proposed_adjusted_balance, + balance_at_the_edge + ); + + Some(&*account_info) + } else { + None + } + }) + .collect() } fn handle_possibly_outweighed_account( @@ -718,11 +733,9 @@ impl PaymentAdjusterReal { diagnostics!( &account_info.original_account.wallet, "OUTWEIGHED ACCOUNT FOUND", - || format!( - "original balance: {}, proposed balance {}", - account_info.original_account.balance_wei, - account_info.proposed_adjusted_balance - ) + "original balance: {}, proposed balance {}", + account_info.original_account.balance_wei, + account_info.proposed_adjusted_balance ); let outweighed_record = @@ -754,72 +767,6 @@ impl PaymentAdjusterReal { } } - // fn check_for_outweighed_accounts( - // accounts_with_individual_criteria: &[(u128, PayableAccount)], - // required_balance_total: u128, - // criteria_total: u128, - // cw_masq_balance: u128, //TODO remove me after the experiment - // ) -> Option> { - // let coeff = PaymentAdjusterReal::compute_fractions_preventing_mul_coeff( - // cw_masq_balance, - // criteria_total, - // ); - // eprintln!("required bala {} coeff: {}", required_balance_total, coeff); - // let required_balance_total_for_safe_math = required_balance_total * 1000; - // let criteria_total_for_safe_math = criteria_total * 1000; - // let accounts_to_be_outweighed = accounts_with_individual_criteria - // .iter() - // .filter(|(criterion, account)| { - // //TODO maybe this part should have its own fn, where we could test closely the relation between the ration and the proposed balance - // let balance_ratio = - // required_balance_total_for_safe_math / (account.balance_wei * 1000); //TODO also try giving each parameter a diff const in order to mitigate the sensitiveness - // let criterion_ratio = criteria_total_for_safe_math / (criterion * 1000); //TODO try moving with this constant and watch the impact on this check - // // true means we would pay more than we were asked to pay at the beginning, - // // this happens when the debt size is quite small but its age is large and - // // plays the main factor - // // { - // // let balance_fragment = cw_masq_balance - // // .checked_mul(coeff) - // // .unwrap() - // // .checked_div(criteria_total) - // // .unwrap(); - // // eprintln!( - // // "account {} balance before {} and after {}", - // // account.wallet, - // // account.balance_wei.separate_with_commas(), - // // ((balance_fragment * criterion).checked_div(coeff).unwrap()) - // // .separate_with_commas() - // // ); - // // eprintln!( - // // "this is current balance ratio: {}, this is current criterion ratio {}", - // // balance_ratio, criterion_ratio - // // ) - // // }; - // let is_highly_significant = balance_ratio > criterion_ratio; - // if is_highly_significant { - // diagnostics( - // &account.wallet, - // "ACCOUNT PROPOSED WITH BALANCE HIGHER THAN THE ORIGIN ONE", - // || { - // format!( - // "balance_ration ({}) > criterion_ration ({})", - // balance_ratio, criterion_ratio - // ) - // }, - // ) - // } - // - // is_highly_significant - // }) - // .map(|(_, account)| account.wallet.clone()) - // .collect::>(); - // if !accounts_to_be_outweighed.is_empty() { - // Some(accounts_to_be_outweighed) - // } else { - // None - // } - // } - //TODO we probably want to drop the criteria before here where we've got no use for them........or maybe not...because the computation has always the same res fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( &mut self, @@ -841,6 +788,7 @@ impl PaymentAdjusterReal { account.balance_wei }) > cw_masq_balance { + todo!("will we ever be here again?"); self.run_adjustment_by_criteria_recursively(outweighed_with_criteria, vec![]) // // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { @@ -857,11 +805,12 @@ impl PaymentAdjusterReal { let subtrahend_total: u128 = sum_as(processed_outweighed, |account| account.balance_wei); self.inner.lower_unallocated_cw_balance(subtrahend_total); - diagnostics!("LOWERED CW BALANCE", || format!( - "lowered by {} to {}", + diagnostics!( + "LOWERED CW BALANCE", + "Unallocated balance lowered by {} to {}", subtrahend_total, self.inner.unallocated_cw_masq_balance() - )) + ) } } @@ -919,7 +868,7 @@ impl } } -#[derive(Debug, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { original_account: PayableAccount, proposed_adjusted_balance: u128, @@ -970,13 +919,7 @@ impl DisqualifiedPayableAccount { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - TransactionFeeDefinitelyOtherMaybe { limited_count_from_gas: u16 }, -} - -#[derive(Clone, Copy)] -struct GasLimitationContext { - limited_count_from_gas: u16, - is_masq_token_insufficient: bool, + PriorityTransactionFee { limited_count_from_gas: u16 }, } #[derive(Debug, PartialEq, Eq)] @@ -991,12 +934,9 @@ pub enum AnalysisError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - balance_total, compute_fractions_preventing_mul_coeff, criteria_total, log_2, + compute_fractions_preventing_mul_coeff, criteria_total, log_2, }; use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; - use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, - }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; @@ -1004,8 +944,10 @@ mod tests { AccountsRecreationResult, AdjustedAccountBeforeFinalization, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, - BALANCE_TAIL_WEIGHT_EXPONENT, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, + AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, + AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, + AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, + AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ @@ -1013,80 +955,20 @@ mod tests { }; use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::test_utils::make_payable_account; - use crate::accountant::{gwei_to_wei, wei_to_gwei, ResponseSkeleton}; + use crate::accountant::{gwei_to_wei, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::{ ConsumingWalletBalances, OutcomingPaymentsInstructions, }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; - use itertools::Either::Left; use itertools::{Either, Itertools}; - use lazy_static::lazy_static; - use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use std::iter::once; - use std::thread::current; use std::time::{Duration, SystemTime}; use std::vec; use thousands::Separable; use web3::types::U256; - fn make_payable_setup_msg_coming_from_blockchain_bridge( - q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, - gas_price_opt: Option, - ) -> PayablePaymentSetup { - let (qualified_payables_gwei, consuming_wallet_masq_gwei) = - q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); - - let ( - desired_gas_price, - number_of_payments, - estimated_gas_limit_per_tx, - cw_balance_gas_gwei, - ) = match gas_price_opt { - Some(conditions) => ( - conditions.desired_gas_price_gwei, - conditions.number_of_payments, - conditions.estimated_gas_limit_per_transaction, - conditions.consuming_wallet_gas_gwei, - ), - None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), - }; - - let qualified_payables: Vec<_> = match number_of_payments != qualified_payables_gwei.len() { - true => (0..number_of_payments) - .map(|idx| make_payable_account(idx as u64)) - .collect(), - false => qualified_payables_gwei - .into_iter() - .map(|balance| make_payable_account(balance)) - .collect(), - }; - - PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), - masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), - }, - estimated_gas_limit_per_transaction: estimated_gas_limit_per_tx, - desired_gas_price_gwei: desired_gas_price, - }, - )), - response_skeleton_opt: None, - } - } - - struct GasTestConditions { - desired_gas_price_gwei: u64, - number_of_payments: usize, - estimated_gas_limit_per_transaction: u64, - consuming_wallet_gas_gwei: u64, - } - #[test] fn constants_are_correct() { assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); @@ -1094,6 +976,11 @@ mod tests { assert_eq!(AGE_MAIN_EXPONENT, 4); assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); assert_eq!(AGE_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 9); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, @@ -1201,7 +1088,7 @@ mod tests { let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( result, - Ok(Some(Adjustment::TransactionFeeDefinitelyOtherMaybe { + Ok(Some(Adjustment::PriorityTransactionFee { limited_count_from_gas: expected_limiting_count })) ); @@ -1242,71 +1129,53 @@ mod tests { } #[test] - fn consider_account_disqualification_from_percentage_insignificance_adheres_to_the_manifest_consts_of_insignificance( - ) { - todo!("rewrite me when you have more energy") - // let cw_masq_balance = 1_000_000; - // let mut subject = make_initialized_subject(SystemTime::now(), Some(cw_masq_balance), None); - // let account_balance = 1_000_000; - // let prepare_account = |n: u64| { - // let mut account = make_payable_account(n); - // account.balance_wei = account_balance; - // account - // }; - // let payable_account_1 = prepare_account(1); - // let wallet_1 = payable_account_1.wallet.clone(); - // let payable_account_2 = prepare_account(2); - // let wallet_2 = payable_account_2.wallet.clone(); - // let payable_account_3 = prepare_account(3); - // let wallet_3 = payable_account_3.wallet.clone(); - // const IRRELEVANT_CRITERIA_SUM: u128 = 1111; - // let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - // * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; - // let proposed_ok_balance = edge + 1; - // let account_info_1 = AccountWithUncheckedAdjustment::new( - // payable_account_1, - // proposed_ok_balance, - // IRRELEVANT_CRITERIA_SUM, - // ); - // let proposed_bad_balance_because_equal = edge; - // let account_info_2 = AccountWithUncheckedAdjustment::new( - // payable_account_2, - // proposed_bad_balance_because_equal, - // IRRELEVANT_CRITERIA_SUM, - // ); - // let proposed_bad_balance_because_smaller = edge - 1; - // let account_info_3 = AccountWithUncheckedAdjustment::new( - // payable_account_3, - // proposed_bad_balance_because_smaller, - // IRRELEVANT_CRITERIA_SUM, - // ); - // let accounts_with_unchecked_adjustment = - // vec![account_info_1, account_info_2, account_info_3]; - // - // let result = - // PaymentAdjusterReal::consider_account_disqualification_from_percentage_insignificance( - // accounts_with_unchecked_adjustment, - // ) - // .right() - // .unwrap(); - // - // let (disqualified_account, remaining) = match result { - // AccountsRecreationResult::InsignificantAccounts { - // disqualified, - // remaining, - // } => (disqualified, remaining), - // x => panic!( - // "we expected some disqualified accounts but got this: {:?}", - // x - // ), - // }; - // let expected_disqualified_accounts = vec![wallet_2, wallet_3]; - // .iter().for_each(|account_info| { - // assert!(expected_disqualified_accounts.contains(&disqualified_account.wallet)) - // }); - // assert_eq!(remaining[0].wallet, wallet_1); - // assert_eq!(disqualified_account.len(), 2); - // assert_eq!(remaining.len(), 1); + fn list_accounts_under_the_disqualification_limit_employs_manifest_consts_of_insignificance() { + let cw_masq_balance = 1_000_000; + let account_balance = 1_000_000; + let prepare_account = |n: u64| { + let mut account = make_payable_account(n); + account.balance_wei = account_balance; + account + }; + let payable_account_1 = prepare_account(1); + let wallet_1 = payable_account_1.wallet.clone(); + let payable_account_2 = prepare_account(2); + let wallet_2 = payable_account_2.wallet.clone(); + let payable_account_3 = prepare_account(3); + let wallet_3 = payable_account_3.wallet.clone(); + const IRRELEVANT_CRITERIA_SUM: u128 = 1111; + let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + let proposed_ok_balance = edge + 1; + let account_info_1 = AdjustedAccountBeforeFinalization::new( + payable_account_1, + proposed_ok_balance, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_equal = edge; + let account_info_2 = AdjustedAccountBeforeFinalization::new( + payable_account_2, + proposed_bad_balance_because_equal, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_smaller = edge - 1; + let account_info_3 = AdjustedAccountBeforeFinalization::new( + payable_account_3, + proposed_bad_balance_because_smaller, + IRRELEVANT_CRITERIA_SUM, + ); + let accounts_with_unchecked_adjustment = vec![ + account_info_1, + account_info_2.clone(), + account_info_3.clone(), + ]; + + let result = PaymentAdjusterReal::list_accounts_under_the_disqualification_limit( + &accounts_with_unchecked_adjustment, + ); + + let expected_disqualified_accounts = vec![&account_info_2, &account_info_3]; + assert_eq!(result, expected_disqualified_accounts) } #[test] @@ -1332,7 +1201,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let weights_and_accounts = subject.add_criteria_sums_to_accounts(qualified_payables); + let weights_and_accounts = subject.pair_accounts_with_summed_criteria(qualified_payables); let only_accounts = weights_and_accounts .iter() @@ -1436,7 +1305,7 @@ mod tests { pending_payable_opt: None, }; let accounts_with_individual_criteria = - subject.add_criteria_sums_to_accounts(vec![account_1, account_2, account_3]); + subject.pair_accounts_with_summed_criteria(vec![account_1, account_2, account_3]); let criteria_total = criteria_total(&accounts_with_individual_criteria); let unfinalized_adjusted_accounts = subject.compute_unfinalized_adjusted_accounts( accounts_with_individual_criteria, @@ -1479,7 +1348,7 @@ mod tests { let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important - let criteria_and_accounts = subject.add_criteria_sums_to_accounts(qualified_payables); + let criteria_and_accounts = subject.pair_accounts_with_summed_criteria(qualified_payables); let criteria_total = criteria_total(&criteria_and_accounts); let account_2_criterion = criteria_and_accounts[1].0; let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; @@ -1537,8 +1406,8 @@ mod tests { pending_payable_opt: None, }; let accounts = vec![account_1.clone(), account_2.clone()]; - let accounts_with_individual_criteria = subject.add_criteria_sums_to_accounts(accounts); - let required_balance_total = balance_total(&accounts_with_individual_criteria); + let accounts_with_individual_criteria = + subject.pair_accounts_with_summed_criteria(accounts); let criteria_total = criteria_total(&accounts_with_individual_criteria); let result = subject.recreate_accounts_with_proportioned_balances( @@ -1753,7 +1622,7 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::TransactionFeeDefinitelyOtherMaybe { + adjustment: Adjustment::PriorityTransactionFee { limited_count_from_gas: 2, }, }; @@ -1763,7 +1632,7 @@ mod tests { assert_eq!( result, OutcomingPaymentsInstructions { - accounts: vec![account_2, account_3], + accounts: vec![account_3, account_2], response_skeleton_opt: None } ); @@ -1994,9 +1863,11 @@ mod tests { }; // scenario A let first_scenario_name = merge_test_name_and_study_description(test_name, "when equal"); - let expected_winning_account = &wallet_2; + // first we disqualify the smallest, but also last at that balance, account which is account 2, + // therefore only account 1 remains and soon after qualifies + let expected_winning_account = &wallet_1; - let mut result = test_competitive_accounts( + test_competitive_accounts( &first_scenario_name, CompetitiveAccountsTestInputFeeder { common: common_input, @@ -2013,7 +1884,7 @@ mod tests { merge_test_name_and_study_description(test_name, "first heavier by balance"); let expected_winning_account = &wallet_1; - let mut result = test_competitive_accounts( + test_competitive_accounts( &second_scenario_name, CompetitiveAccountsTestInputFeeder { common: common_input, @@ -2030,7 +1901,7 @@ mod tests { merge_test_name_and_study_description(test_name, "second heavier by age"); let expected_winning_account = &wallet_2; - let result = test_competitive_accounts( + test_competitive_accounts( &third_scenario_name, CompetitiveAccountsTestInputFeeder { common: common_input, @@ -2040,7 +1911,7 @@ mod tests { age_secs_account_2: age_secs_account_2 + 1, }, expected_winning_account, - ); + ) } //TODO do I really want to delete this test? Why? I probably don't @@ -2220,7 +2091,7 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::TransactionFeeDefinitelyOtherMaybe { + adjustment: Adjustment::PriorityTransactionFee { limited_count_from_gas: 2, }, }; @@ -2262,6 +2133,61 @@ mod tests { now.duration_since(timestamp).unwrap().as_secs() as u128 } + struct GasTestConditions { + desired_gas_price_gwei: u64, + number_of_payments: usize, + estimated_gas_limit_per_transaction: u64, + consuming_wallet_gas_gwei: u64, + } + + fn make_payable_setup_msg_coming_from_blockchain_bridge( + q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, + gas_price_opt: Option, + ) -> PayablePaymentSetup { + let (qualified_payables_gwei, consuming_wallet_masq_gwei) = + q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); + + let ( + desired_gas_price, + number_of_payments, + estimated_gas_limit_per_tx, + cw_balance_gas_gwei, + ) = match gas_price_opt { + Some(conditions) => ( + conditions.desired_gas_price_gwei, + conditions.number_of_payments, + conditions.estimated_gas_limit_per_transaction, + conditions.consuming_wallet_gas_gwei, + ), + None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), + }; + + let qualified_payables: Vec<_> = match number_of_payments != qualified_payables_gwei.len() { + true => (0..number_of_payments) + .map(|idx| make_payable_account(idx as u64)) + .collect(), + false => qualified_payables_gwei + .into_iter() + .map(|balance| make_payable_account(balance)) + .collect(), + }; + + PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), + masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), + }, + estimated_gas_limit_per_transaction: estimated_gas_limit_per_tx, + desired_gas_price_gwei: desired_gas_price, + }, + )), + response_skeleton_opt: None, + } + } + fn emulation_of_the_actual_adjustment_algorithm( account_1: PayableAccount, account_2: PayableAccount, @@ -2356,35 +2282,4 @@ mod tests { .map(|(_, account)| account) .collect() } - - fn compute_expected_balanced_portions_from_criteria( - final_criteria: Vec, - consuming_wallet_masq_balance_wei: u128, - ) -> Vec { - let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = compute_fractions_preventing_mul_coeff( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); - let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei - .checked_mul(multiplication_coeff) - .unwrap() - .checked_div(final_criteria_sum) - .unwrap(); - let new_balanced_portions = final_criteria - .iter() - .map(|criterion| { - in_ratio_fragment_of_available_balance * criterion / multiplication_coeff - }) - .collect::>(); - let new_total_amount_to_pay = new_balanced_portions.iter().sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); - assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, - "new total amount to pay: {}, consuming wallet masq balance: {}", - new_total_amount_to_pay, - consuming_wallet_masq_balance_wei - ); - new_balanced_portions - } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 4bb53547b..3ffbf2427 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1030,7 +1030,6 @@ mod tests { }; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; - use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; From 5db9267dce38cfd35710a29505f04e4d97e6771a Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 28 Jul 2023 12:24:54 +0200 Subject: [PATCH 052/250] GH-711: big step forward; more formal implementation of Claculator classes is going well --- .../big_int_db_processor.rs | 39 ++- .../big_int_processing/big_int_divider.rs | 42 ++- .../database_access_objects/receivable_dao.rs | 4 +- .../payment_adjuster/criteria_calculators.rs | 326 ++++++++++++++++++ .../payment_adjuster/diagnostics.rs | 117 ++++--- node/src/accountant/payment_adjuster/mod.rs | 228 +++++------- 6 files changed, 521 insertions(+), 235 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/criteria_calculators.rs diff --git a/node/src/accountant/big_int_processing/big_int_db_processor.rs b/node/src/accountant/big_int_processing/big_int_db_processor.rs index 5c57bc1b5..a3e3fa2e7 100644 --- a/node/src/accountant/big_int_processing/big_int_db_processor.rs +++ b/node/src/accountant/big_int_processing/big_int_db_processor.rs @@ -28,7 +28,7 @@ impl<'a, T: TableNameDAO> BigIntDbProcessor { let mut stm = Self::prepare_statement(conn, main_sql); let params = config .params - .merge_other_and_wei_params((&config.params.wei_change_params).into()); + .merge_with_main_clause_params((&config.params.wei_change_params).into()); match stm.execute(params.as_slice()) { Ok(1) => Ok(()), Ok(x) => Err(BigIntDbError(format!("Expected 1 row to be changed for the unique key {} but got this count: {}", config.key_param_value(), x))), @@ -110,17 +110,17 @@ impl UpdateOverflowHandler for UpdateOverflowHandlerReal former_low_bytes, requested_wei_change, ); - let wei_update_array: [(&str, &dyn ToSql); 2] = [ - ( - requested_wei_change.high.name.as_str(), - &high_bytes_corrected, - ), - (requested_wei_change.low.name.as_str(), &low_bytes_corrected), - ]; + + let wei_update_array = Self::wei_update_array( + requested_wei_change.high.name.as_str(), + &high_bytes_corrected, + requested_wei_change.low.name.as_str(), + &low_bytes_corrected, + ); let execute_params = config .params - .merge_other_and_wei_params_with_conditional_participants(wei_update_array); + .merge_with_overflow_clause_params(wei_update_array); Self::execute_update(conn, &config, &execute_params); Ok(()) @@ -175,6 +175,18 @@ impl UpdateOverflowHandlerReal { (high_bytes_correction, low_bytes_correction) } + fn wei_update_array<'a>( + high_byte_param_name: &'a str, + high_byte_value: &'a i64, + low_byte_param_name: &'a str, + low_byte_value: &'a i64, + ) -> [(&'a str, &'a dyn ToSql); 2] { + [ + (high_byte_param_name, high_byte_value), + (low_byte_param_name, low_byte_value), + ] + } + fn return_first_error(two_results: [rusqlite::Result; 2]) -> rusqlite::Result<()> { let cached = format!("{:?}", two_results); match two_results.into_iter().find(|result| result.is_err()) { @@ -439,14 +451,14 @@ impl<'a> From<&'a WeiChangeAsHighAndLowBytes> for [(&'a str, &'a dyn ToSql); 2] } impl<'a> SQLParams<'a> { - fn merge_other_and_wei_params( + fn merge_with_main_clause_params( &'a self, wei_change_params: [(&'a str, &'a dyn ToSql); 2], ) -> Vec<(&'a str, &'a dyn ToSql)> { Self::merge_params(self.params_except_wei_change.iter(), wei_change_params) } - fn merge_other_and_wei_params_with_conditional_participants( + fn merge_with_overflow_clause_params( &'a self, wei_change_params: [(&'a str, &'a dyn ToSql); 2], ) -> Vec<(&'a str, &'a dyn ToSql)> { @@ -711,8 +723,7 @@ mod tests { } #[test] - fn merge_other_and_wei_params_with_conditional_participants_can_filter_out_just_update_params() - { + fn merge_overflow_clause_params_can_filter_out_correct_params() { let tuple_matrix = [ ("blah", &456_i64 as &dyn ExtendedParamsMarker), ("super key", &"abcxy"), @@ -743,7 +754,7 @@ mod tests { ], }; - let result = subject.merge_other_and_wei_params_with_conditional_participants([ + let result = subject.merge_with_overflow_clause_params([ ("always_present_1", &12), ("always_present_2", &77), ]); diff --git a/node/src/accountant/big_int_processing/big_int_divider.rs b/node/src/accountant/big_int_processing/big_int_divider.rs index 40d5a15f1..31ebacb8f 100644 --- a/node/src/accountant/big_int_processing/big_int_divider.rs +++ b/node/src/accountant/big_int_processing/big_int_divider.rs @@ -63,7 +63,7 @@ impl BigIntDivider { } } - pub fn register_big_int_deconstruction_for_sqlite_connection( + pub fn register_big_int_deconstruction_fn_for_sqlite_connection( conn: &Connection, ) -> rusqlite::Result<()> { Self::register_deconstruct_guts(conn, "slope_drop_high_bytes", "slope_drop_low_bytes") @@ -300,20 +300,21 @@ mod tests { ); } - fn create_test_table_and_run_register_deconstruction_for_sqlite_connection( + fn create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( test_name: &str, ) -> Connection { let conn = create_new_empty_db("big_int_db_processor", test_name); - BigIntDivider::register_big_int_deconstruction_for_sqlite_connection(&conn).unwrap(); + BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection(&conn).unwrap(); conn.execute("create table test_table (computed_high_bytes int, computed_low_bytes int, database_parameter int not null)",[]).unwrap(); conn } #[test] fn register_deconstruct_for_sqlite_connection_works() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( - "register_deconstruct_for_sqlite_connection_works", - ); + let conn = + create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + "register_deconstruct_for_sqlite_connection_works", + ); let database_value_1: i64 = 12222; let database_value_2: i64 = 23333444; @@ -369,7 +370,7 @@ mod tests { #[test] fn register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_first_function() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( "register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_first_function", ); @@ -397,7 +398,7 @@ mod tests { #[test] fn register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_second_function() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( "register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_second_function", ); @@ -425,7 +426,7 @@ mod tests { #[test] fn our_sqlite_functions_are_specialized_and_thus_should_not_take_positive_number_for_the_second_parameter( ) { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( "our_sqlite_functions_are_specialized_and_thus_should_not_take_positive_number_for_the_second_parameter" ); let error_invoker = |bytes_type: &str| { @@ -472,7 +473,7 @@ mod tests { #[test] fn our_sqlite_functions_are_specialized_thus_should_not_take_negative_number_for_the_third_parameter( ) { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( "our_sqlite_functions_are_specialized_thus_should_not_take_negative_number_for_the_third_parameter" ); let error_invoker = |bytes_type: &str| { @@ -516,9 +517,10 @@ mod tests { #[test] fn third_argument_error() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( - "third_argument_error", - ); + let conn = + create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + "third_argument_error", + ); let result = conn .execute( @@ -538,9 +540,10 @@ mod tests { #[test] fn first_fn_returns_internal_error_from_create_scalar_function() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( - "first_fn_returns_internal_error_from_create_scalar_function", - ); + let conn = + create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + "first_fn_returns_internal_error_from_create_scalar_function", + ); let result = BigIntDivider::register_deconstruct_guts( &conn, @@ -559,9 +562,10 @@ mod tests { #[test] fn second_fn_returns_internal_error_from_create_scalar_function() { - let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( - "second_fn_returns_internal_error_from_create_scalar_function", - ); + let conn = + create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + "second_fn_returns_internal_error_from_create_scalar_function", + ); let result = BigIntDivider::register_deconstruct_guts( &conn, diff --git a/node/src/accountant/database_access_objects/receivable_dao.rs b/node/src/accountant/database_access_objects/receivable_dao.rs index db91cbe93..1427f1db0 100644 --- a/node/src/accountant/database_access_objects/receivable_dao.rs +++ b/node/src/accountant/database_access_objects/receivable_dao.rs @@ -96,7 +96,7 @@ pub trait ReceivableDaoFactory { impl ReceivableDaoFactory for DaoFactoryReal { fn make(&self) -> Box { let init_config = self.init_config.clone().add_special_conn_setup( - BigIntDivider::register_big_int_deconstruction_for_sqlite_connection, + BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection, ); let conn = connection_or_panic( &DbInitializerReal::default(), @@ -828,7 +828,7 @@ mod tests { home_dir: &Path, ) -> Box { let init_config = DbInitializationConfig::test_default().add_special_conn_setup( - BigIntDivider::register_big_int_deconstruction_for_sqlite_connection, + BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection, ); DbInitializerReal::default() .initialize(home_dir, init_config) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs new file mode 100644 index 000000000..fecabc190 --- /dev/null +++ b/node/src/accountant/payment_adjuster/criteria_calculators.rs @@ -0,0 +1,326 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics::{ + compute_progressive_characteristics, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, +}; +use libc::scanf; +use std::fmt::Debug; +use std::time::SystemTime; +use crate::accountant::payment_adjuster::auxiliary_fns::x_or_1; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; + +pub trait CriterionCalculator { + type Input; + fn formula(&self) -> fn(Self::Input) -> u128; + fn form_input(&self, account: &PayableAccount) -> Self::Input; + fn diagnostics_config_opt(&self) -> Option>; + + fn add_calculated_criterion( + &self, + (criteria_sum, account): (u128, PayableAccount), + ) -> (u128, PayableAccount) + where + ::Input: Debug, + { + self.diagnostics(); + let updated_criteria_sum = criteria_sum + self.formula()(self.form_input(&account)); + ( + updated_criteria_sum, + account, + ) + } + fn diagnostics(&self) + where + ::Input: Debug, + { + if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { + compute_progressive_characteristics(self.diagnostics_config_opt(), self.formula()) + } + } +} + +pub struct DiagnosticsConfig { + pub label: &'static str, + pub safe_index_at_examples: usize, + pub progressive_set_of_args: Vec, +} + +pub struct AgeCriterionCalculator<'a>{ + payment_adjuster: &'a PaymentAdjusterReal +} + +const AGE_MAIN_EXPONENT: u32 = 3; +// divisor^(numerator/denominator) +const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; +const AGE_MULTIPLIER: u128 = 150; +const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; +const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; +const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; + +impl <'a> AgeCriterionCalculator<'a>{ + pub fn new(payment_adjuster: &'a PaymentAdjusterReal)->Self{ + todo!() + } + + fn compute_divisor(elapsed_sec: u64) -> u128 { + (elapsed_sec as f64).sqrt().ceil() as u128 + } + + fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { + let fast_growing_argument = (elapsed_secs as u128) + .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) + .expect("pow blew up") as f64; + let log = fast_growing_argument.ln(); + let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) + * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; + let final_log_multiplier = (log_stressed + / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) + .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); + x_or_1(final_log_multiplier) + } +} + +impl CriterionCalculator for AgeCriterionCalculator<'_> { + type Input = SystemTime; + + fn formula(&self) -> fn(Self::Input) -> u128 { + todo!() + } + + fn form_input(&self, account: &PayableAccount) -> Self::Input { + todo!() + } + + fn diagnostics_config_opt(&self) -> Option> { + todo!() + } + + // let formula = |last_paid_timestamp: SystemTime| { + // let elapsed_secs: u64 = self + // .inner + // .now() + // .duration_since(last_paid_timestamp) + // .expect("time traveller") + // .as_secs(); + // let divisor = Self::compute_divisor(elapsed_secs); + // let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + // (elapsed_secs as u128) + // .checked_pow(AGE_MAIN_EXPONENT) + // .unwrap_or(u128::MAX) //TODO sensible and tested ???? + // .checked_div(divisor) + // .expect("div overflow") + // .checked_mul(log_multiplier) + // .expect("mul overflow") + // }; + // let criterion = formula(account.last_paid_timestamp); + // + // CriteriaWithDiagnostics { + // account, + // criterion, + // criteria_sum_so_far, + // diagnostics: DiagnosticsSetting { + // label: "AGE", + // diagnostics_adaptive_formula: |x: u128| { + // let secs_in_the_past = Duration::from_secs(x as u64); + // let approx_time_anchor = SystemTime::now() + // .checked_sub(secs_in_the_past) + // .expect("age formula characteristics blew up"); + // formula(approx_time_anchor) + // }, + // singleton_ref: &AGE_SINGLETON, + // bonds_safe_count_to_print: 10, + // }, + // } + // .diagnose_and_sum() +} + +pub struct BalanceCriterionCalculator<'a>{ + payment_adjuster:&'a PaymentAdjusterReal +} + +impl <'a> BalanceCriterionCalculator<'a>{ + pub fn new(payment_adjuster: &'a PaymentAdjusterReal)->Self{ + todo!() + } +} + +impl CriterionCalculator for BalanceCriterionCalculator<'_> { + type Input = u128; + + fn formula(&self) -> fn(Self::Input) -> u128 { + todo!() + } + + fn form_input(&self, account: &PayableAccount) -> Self::Input { + todo!() + } + + fn diagnostics_config_opt(&self) -> Option> { + todo!() + } + + // // constants used to keep the weights of balance and time balanced + // let formula = |balance_wei: u128| { + // let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); + // let multiplied = balance_wei + // .checked_mul(binary_weight as u128) + // .expect("mul overflow"); + // multiplied + // }; + // let criterion = formula(account.balance_wei); + // + // CriteriaWithDiagnostics { + // account, + // criterion, + // criteria_sum_so_far, + // diagnostics: DiagnosticsSetting { + // label: "BALANCE", + // diagnostics_adaptive_formula: |x: u128| formula(x), + // singleton_ref: &BALANCE_SINGLETON, + // bonds_safe_count_to_print: EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len(), + // }, + // } + // .diagnose_and_sum() +} + +pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { + iter: I, + calculator: C, +} + +impl Iterator for CriteriaIterator +where + I: Iterator, + C: CriterionCalculator, + ::Input: Debug, +{ + type Item = (u128, PayableAccount); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|item| self.calculator.add_calculated_criterion(item)) + } +} + +pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { + fn map_criteria(self, calculator: C) -> CriteriaIterator + where + Self: Sized; +} + +impl CriteriaIteratorAdaptor for I { + fn map_criteria(self, calculator: C) -> CriteriaIterator { + todo!() + } +} + +#[cfg(test)] +mod tests { + use std::time::{Duration, SystemTime}; + use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::criteria_calculators::{AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, AgeCriterionCalculator, CriterionCalculator}; + use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::test_utils::make_wallet; + + #[test] + fn constants_are_correct(){ + assert_eq!(AGE_MAIN_EXPONENT, 4); + assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); + assert_eq!(AGE_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); + } + + // let formula = |last_paid_timestamp: SystemTime| { + // let elapsed_secs: u64 = self + // .inner + // .now() + // .duration_since(last_paid_timestamp) + // .expect("time traveller") + // .as_secs(); + // let divisor = Self::compute_divisor(elapsed_secs); + // let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + // (elapsed_secs as u128) + // .checked_pow(AGE_MAIN_EXPONENT) + // .unwrap_or(u128::MAX) //TODO sensible and tested ???? + // .checked_div(divisor) + // .expect("div overflow") + // .checked_mul(log_multiplier) + // .expect("mul overflow") + // }; + + #[test] + fn compute_divisor_works() { + let result: Vec<_> = [100, 81, 82, 80] + .into_iter() + .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) + .collect(); + + assert_eq!(result, vec![10, 9, 10, 9]) + } + + #[test] + fn compute_descending_multiplier_works() { + let result: Vec<_> = EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS + .into_iter() + .take(12) + .map(|exp| 10_u64.pow(exp)) + .map(|seconds_elapsed| { + let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); + AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) + }) + .collect(); + + assert_eq!( + result, + vec![ + 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, + 1 + ] + ) + } + + + #[test] + fn age_criteria_calculation_works(){ + let now = SystemTime::now(); + let payment_adjuster = make_initialized_subject(now, None, None); + let subject = AgeCriterionCalculator::new(&payment_adjuster); + let input = SystemTime::now().checked_sub(Duration::from_secs(1500)).unwrap(); + + let result = subject.formula()(input); + + let expected_criterion = { + let elapsed_secs: u64 = subject.payment_adjuster.inner + .now() + .duration_since(input) + .unwrap() + .as_secs(); + let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); + let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) + .unwrap_or(u128::MAX) //TODO sensible and tested ???? + .checked_div(divisor) + .unwrap() + .checked_mul(log_multiplier) + .unwrap() + }; + assert_eq!(result, expected_criterion) + } + + #[test] + fn balance_criteria_calculation_works(){ + todo!() + } + +} diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index fc3614884..adab3e48e 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,14 +1,19 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::{ - COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, -}; -use std::sync::Once; +use crate::accountant::payment_adjuster::criteria_calculators::DiagnosticsConfig; +use crate::accountant::payment_adjuster::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; +use itertools::Itertools; +use std::fmt::Debug; +use std::sync::{Mutex, Once}; use thousands::Separable; pub static AGE_SINGLETON: Once = Once::new(); pub static BALANCE_SINGLETON: Once = Once::new(); + +pub const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = true; +pub const FORMULAS_DIAGNOSTICS_SINGLETON: Once = Once::new(); + pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 14] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25]; @@ -80,53 +85,65 @@ where pub bonds_safe_count_to_print: usize, } -impl<'a, F> DiagnosticsSetting<'a, F> -where - F: Fn(u128) -> u128, -{ - pub fn compute_progressive_characteristics(&self) { - if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { - self.singleton_ref.call_once(|| { - eprintln!("CHARACTERISTICS FOR {} FORMULA", self.label); - EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS - .into_iter() - .take(self.bonds_safe_count_to_print) - .map(|exponent| 10_u128.pow(exponent)) - .for_each(|input_num| { - let value = (self.diagnostics_adaptive_formula)(input_num); - eprintln!( - "x: {: CriteriaWithDiagnostics<'_, F> +// where +// F: Fn(u128) -> u128, +// { +// pub fn diagnose_and_sum(self) -> (u128, PayableAccount) { +// let account = &self.account.wallet; +// let description = format!("COMPUTED {} CRITERIA", self.diagnostics.label); +// diagnostics!( +// account, +// &description, +// "{}", +// self.criterion.separate_with_commas() +// ); +// self.diagnostics.compute_progressive_characteristics(); +// +// ( +// self.criteria_sum_so_far +// .checked_add(self.criterion) +// .expect("add overflow"), +// self.account, +// ) +// } +// } + +pub const STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); + +fn print_formulas_diagnostics() { + FORMULAS_DIAGNOSTICS_SINGLETON.call_once(|| { + let report = STRINGS_WITH_FORMULAS_CHARACTERISTICS + .lock() + .expect("diagnostics poisoned") + .join("\n\n"); + eprintln!("{}", report) + }) } -impl CriteriaWithDiagnostics<'_, F> -where - F: Fn(u128) -> u128, +pub fn compute_progressive_characteristics( + config_opt: Option>, + formula: fn(A) -> u128, +) where + A: Debug, { - pub fn diagnose_and_sum(self) -> (u128, PayableAccount) { - let account = &self.account.wallet; - let description = format!("COMPUTED {} CRITERIA", self.diagnostics.label); - diagnostics!( - account, - &description, - "{}", - self.criterion.separate_with_commas() - ); - self.diagnostics.compute_progressive_characteristics(); - - ( - self.criteria_sum_so_far - .checked_add(self.criterion) - .expect("add overflow"), - self.account, - ) - } + config_opt.map(|config| { + let characteristics = config + .progressive_set_of_args + .into_iter() + .map(|input| { + let input_print = format!("{:?}", input); + format!( + "x: {: u128 { - (elapsed_sec as f64).sqrt().ceil() as u128 - } - - //TODO this fn should later become property of the age criteria computing class e.g. "CriteriaComputer" - fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { - let fast_growing_argument = (elapsed_secs as u128) - .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) - .expect("pow blew up") as f64; - let log = fast_growing_argument.ln(); - let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) - * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; - let final_log_multiplier = (log_stressed - / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) - .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); - x_or_1(final_log_multiplier) - } - //TODO this fn should later become property of the balance criteria computing class e.g. "CriteriaComputer" fn compute_binary_argument(balance_wei: u128) -> u128 { x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) @@ -944,11 +922,7 @@ mod tests { AccountsRecreationResult, AdjustedAccountBeforeFinalization, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, - AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, - AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, - AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, - PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, + BALANCE_LOG_2_ARG_DIVISOR, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -968,19 +942,11 @@ mod tests { use std::vec; use thousands::Separable; use web3::types::U256; + use crate::accountant::payment_adjuster::criteria_calculators::AgeCriterionCalculator; #[test] fn constants_are_correct() { assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); - assert_eq!(COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, false); - assert_eq!(AGE_MAIN_EXPONENT, 4); - assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); - assert_eq!(AGE_MULTIPLIER, 10); - assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 9); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, @@ -1210,37 +1176,6 @@ mod tests { assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } - #[test] - fn compute_divisor_works() { - let result: Vec<_> = [100, 81, 82, 80] - .into_iter() - .map(|secs| PaymentAdjusterReal::compute_divisor(secs)) - .collect(); - - assert_eq!(result, vec![10, 9, 10, 9]) - } - - #[test] - fn compute_descending_multiplier_works() { - let result: Vec<_> = EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS - .into_iter() - .take(12) - .map(|exp| 10_u64.pow(exp)) - .map(|seconds_elapsed| { - let divisor = PaymentAdjusterReal::compute_divisor(seconds_elapsed); - PaymentAdjusterReal::compute_descending_multiplier(seconds_elapsed, divisor) - }) - .collect(); - - assert_eq!( - result, - vec![ - 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, - 1 - ] - ) - } - #[test] fn compute_binary_argument_works() { let inputs = [ @@ -2203,15 +2138,8 @@ mod tests { .into_iter() .flatten() .collect::>(); - let age_criteria = accounts - .iter() - .map(|account| { - let elapsed = secs_elapsed(account.last_paid_timestamp, now); - let divisor = - (((elapsed as f64).sqrt().ceil()) as u128).pow(AGE_DIVISOR_EXP_IN_NUMERATOR); - elapsed.pow(AGE_MAIN_EXPONENT) * AGE_MULTIPLIER / divisor - }) - .collect(); + //TODO continue here ... fix it so that, if possible, you don't have to use the whole subject here, which has its over-time varying inner state + let age_criteria = AgeCriterionCalculator::; let balance_criteria = accounts .iter() .map(|account| { From 0a085593b032561f5ed3ab18e343cd4563d4b0fa Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 31 Jul 2023 20:11:06 +0200 Subject: [PATCH 053/250] GH-711: just finished isolation of calculators as separate class etc; all tests but three passing --- .../payment_adjuster/auxiliary_fns.rs | 133 +++++--- .../payment_adjuster/criteria_calculators.rs | 302 ++++++++-------- .../payment_adjuster/diagnostics.rs | 179 +++++----- node/src/accountant/payment_adjuster/mod.rs | 323 +++++------------- 4 files changed, 428 insertions(+), 509 deletions(-) diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index 833bb4a69..e12d71572 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -41,7 +41,7 @@ pub fn cut_back_by_gas_count_limit( .collect() } -pub fn compute_fractions_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { +pub fn compute_fraction_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { let criteria_sum_digits_count = log_10(criteria_sum); let cw_balance_digits_count = log_10(cw_masq_balance); let positive_difference = criteria_sum_digits_count @@ -83,7 +83,7 @@ impl ExhaustionStatus { } } - fn update( + fn update_and_add( mut self, mut unfinalized_account_info: AdjustedAccountBeforeFinalization, adjusted_balance_possible_addition: u128, @@ -94,20 +94,24 @@ impl ExhaustionStatus { + adjusted_balance_possible_addition; unfinalized_account_info }; - let finalized_account = PayableAccount::from(( - corrected_adjusted_account_before_finalization, - DecidedPayableAccountResolution::Finalize, - )); self.remainder = self .remainder .checked_sub(adjusted_balance_possible_addition) .unwrap_or(0); //TODO wait for overflow + self.add(corrected_adjusted_account_before_finalization) + } + + fn add(mut self, unfinalized_account_info: AdjustedAccountBeforeFinalization) -> Self { + let finalized_account = PayableAccount::from(( + unfinalized_account_info, + DecidedPayableAccountResolution::Finalize, + )); self.already_finalized_accounts.push(finalized_account); self } } -pub fn exhaust_cw_balance_as_much_as_possible( +pub fn exhaust_cw_balance_totally( verified_accounts: Vec, unallocated_cw_masq_balance: u128, ) -> Vec { @@ -133,12 +137,15 @@ pub fn exhaust_cw_balance_as_much_as_possible( } else { status.remainder }; - status.update(unfinalized_account_info, adjusted_balance_possible_addition) + status.update_and_add(unfinalized_account_info, adjusted_balance_possible_addition) } else { - status + status.add(unfinalized_account_info) } }) .already_finalized_accounts + .into_iter() + .sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)) + .collect() } pub fn sort_in_descendant_order_by_weights( @@ -185,7 +192,7 @@ pub fn x_or_1(x: u128) -> u128 { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fractions_preventing_mul_coeff, exhaust_cw_balance_as_much_as_possible, + compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, log_10, log_2, ExhaustionStatus, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; @@ -255,9 +262,7 @@ mod tests { let result = consuming_wallet_balances .clone() .into_iter() - .map(|cw_balance| { - compute_fractions_preventing_mul_coeff(cw_balance, final_criteria_sum) - }) + .map(|cw_balance| compute_fraction_preventing_mul_coeff(cw_balance, final_criteria_sum)) .collect::>(); assert_eq!( @@ -299,7 +304,7 @@ mod tests { .map(|(criteria_sum, account)| { // scenario simplification: we asume there is always just one account in a time let final_criteria_total = criteria_sum; - let resulted_coeff = compute_fractions_preventing_mul_coeff( + let resulted_coeff = compute_fraction_preventing_mul_coeff( cw_balance_in_minor, final_criteria_total, ); @@ -404,19 +409,20 @@ mod tests { actual_accounts: Vec, expected_parameters: Vec<(Wallet, u128)>, ) { - let expected_in_map: HashMap = - HashMap::from_iter(expected_parameters.into_iter()); - actual_accounts.into_iter().for_each(|account| { - let wallet = &account.wallet; - let expected_balance = expected_in_map.get(wallet).unwrap_or_else(|| { - panic!("account for wallet {} is missing among the results", wallet) - }); - assert_eq!( - account.balance_wei, *expected_balance, - "account {} should've had adjusted balance {} but has {}", - wallet, expected_balance, account.balance_wei - ) - }) + let actual_accounts_simplified_and_sorted = actual_accounts + .into_iter() + .map(|account| (account.wallet.address(), account.balance_wei)) + .sorted() + .collect::>(); + let expected_parameters_sorted = expected_parameters + .into_iter() + .map(|(wallet, expected_balance)| (wallet.address(), expected_balance)) + .sorted() + .collect::>(); + assert_eq!( + actual_accounts_simplified_and_sorted, + expected_parameters_sorted + ) } #[test] @@ -430,7 +436,7 @@ mod tests { } #[test] - fn exhaust_cw_balance_as_much_as_possible_for_three_non_exhaustive_accounts() { + fn exhaust_cw_balance_totally_for_three_non_exhaustive_accounts_all_filled() { // this can happen because some of the pre-qualified accounts could be // eliminated for an insignificant pay and free the means for the other // accounts and then we went through adjustment computation with some @@ -466,10 +472,8 @@ mod tests { ), ]; - let result = exhaust_cw_balance_as_much_as_possible( - unfinalized_adjusted_accounts, - unallocated_cw_balance, - ); + let result = + exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); let expected_resulted_balances = vec![ (wallet_1, original_requested_balance_1), @@ -480,8 +484,63 @@ mod tests { } #[test] - fn exhaust_cw_balance_as_much_as_possible_for_three_non_exhaustive_with_not_enough_to_fulfil_them_all( + fn exhaust_cw_balance_totally_three_non_exhaustive_accounts_with_some_completely_filled_some_not( ) { + let wallet_1 = make_wallet("abc"); + let original_requested_balance_1 = 54_000_000_000; + let proposed_adjusted_balance_1 = 53_898_000_000; + let wallet_2 = make_wallet("def"); + let original_requested_balance_2 = 33_500_000_000; + let proposed_adjusted_balance_2 = 33_487_999_999; + let wallet_3 = make_wallet("ghi"); + let original_requested_balance_3 = 41_000_000; + let proposed_adjusted_balance_3 = 40_980_000; + let wallet_3 = make_wallet("ghi"); + // let original_requested_balance_4 = 41_000_000; + // let proposed_adjusted_balance_4 = 40_980_000; + let unallocated_cw_balance = original_requested_balance_2 + + original_requested_balance_3 + + proposed_adjusted_balance_1 + - 2_000_000; + let unfinalized_adjusted_accounts = vec![ + make_unfinalized_adjusted_account( + &wallet_1, + original_requested_balance_1, + proposed_adjusted_balance_1, + ), + make_unfinalized_adjusted_account( + &wallet_2, + original_requested_balance_2, + proposed_adjusted_balance_2, + ), + make_unfinalized_adjusted_account( + &wallet_3, + original_requested_balance_3, + proposed_adjusted_balance_3, + ), + ]; + + let result = + exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); + eprintln!("{:?}", result); + + let expected_resulted_balances = vec![ + (wallet_1, proposed_adjusted_balance_1), + (wallet_2, 33_498_000_000), + (wallet_3, original_requested_balance_3), + ]; + let check_sum: u128 = expected_resulted_balances + .iter() + .map(|(_, balance)| balance) + .sum(); + let is_equal = check_sum == unallocated_cw_balance; + assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances); + assert!(is_equal) + } + + #[test] + fn exhaust_cw_balance_totally_three_non_exhaustive_accounts_with_two_of_them_completely_filled() + { let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 54_000_000_000; let proposed_adjusted_balance_1 = 53_898_000_000; @@ -513,10 +572,8 @@ mod tests { ), ]; - let result = exhaust_cw_balance_as_much_as_possible( - unfinalized_adjusted_accounts, - unallocated_cw_balance, - ); + let result = + exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); let expected_resulted_balances = vec![ (wallet_1, 53_900_000_000), @@ -537,7 +594,7 @@ mod tests { .collect(); let subject = make_initialized_subject(now, None, None); // when criteria are applied the collection will get sorted and will not necessarily have to match the initial order - let criteria_and_accounts = subject.pair_accounts_with_summed_criteria(accounts); + let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(accounts); eprintln!("wallets in order {:?}", wallets_in_order); (criteria_and_accounts, wallets_in_order) } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs index fecabc190..1c4254daf 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators.rs @@ -1,20 +1,21 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::diagnostics::{ - compute_progressive_characteristics, COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS, +use crate::accountant::payment_adjuster::auxiliary_fns::{log_2, x_or_1}; +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + compute_progressive_characteristics, DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, + BALANCE_DIAGNOSTICS_CONFIG_OPT, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; -use libc::scanf; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; +use std::sync::Mutex; use std::time::SystemTime; -use crate::accountant::payment_adjuster::auxiliary_fns::x_or_1; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; +//caution: always remember to use checked math operations in the formula! pub trait CriterionCalculator { type Input; - fn formula(&self) -> fn(Self::Input) -> u128; - fn form_input(&self, account: &PayableAccount) -> Self::Input; - fn diagnostics_config_opt(&self) -> Option>; + fn formula(&self) -> &dyn Fn(Self::Input) -> u128; + fn input_from_account(&self, account: &PayableAccount) -> Self::Input; fn add_calculated_criterion( &self, @@ -23,33 +24,33 @@ pub trait CriterionCalculator { where ::Input: Debug, { + #[cfg(test)] self.diagnostics(); - let updated_criteria_sum = criteria_sum + self.formula()(self.form_input(&account)); - ( - updated_criteria_sum, - account, - ) + + let updated_criteria_sum = criteria_sum + self.formula()(self.input_from_account(&account)); + (updated_criteria_sum, account) + } + + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>>; + #[cfg(test)] + fn diagnostics_config_opt(&self) -> Option> { + self.diagnostics_config_location() + .lock() + .expect("diagnostics poisoned") + .take() } + #[cfg(test)] fn diagnostics(&self) where ::Input: Debug, { - if COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS { + if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { compute_progressive_characteristics(self.diagnostics_config_opt(), self.formula()) } } } -pub struct DiagnosticsConfig { - pub label: &'static str, - pub safe_index_at_examples: usize, - pub progressive_set_of_args: Vec, -} - -pub struct AgeCriterionCalculator<'a>{ - payment_adjuster: &'a PaymentAdjusterReal -} - const AGE_MAIN_EXPONENT: u32 = 3; // divisor^(numerator/denominator) const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; @@ -60,9 +61,29 @@ const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; -impl <'a> AgeCriterionCalculator<'a>{ - pub fn new(payment_adjuster: &'a PaymentAdjusterReal)->Self{ - todo!() +pub struct AgeCriterionCalculator { + formula: Box u128>, +} + +impl AgeCriterionCalculator { + pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { + let now = payment_adjuster.inner.now(); + let formula = Box::new(move |last_paid_timestamp: SystemTime| { + let elapsed_secs: u64 = now + .duration_since(last_paid_timestamp) + .expect("time traveller") + .as_secs(); + let divisor = Self::compute_divisor(elapsed_secs); + let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) + .unwrap_or(u128::MAX) //TODO sensible and tested ???? + .checked_div(divisor) + .expect("div overflow") + .checked_mul(log_multiplier) + .expect("mul overflow") + }); + Self { formula } } fn compute_divisor(elapsed_sec: u64) -> u128 { @@ -83,107 +104,61 @@ impl <'a> AgeCriterionCalculator<'a>{ } } -impl CriterionCalculator for AgeCriterionCalculator<'_> { +impl CriterionCalculator for AgeCriterionCalculator { type Input = SystemTime; - fn formula(&self) -> fn(Self::Input) -> u128 { - todo!() + fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { + self.formula.as_ref() } - fn form_input(&self, account: &PayableAccount) -> Self::Input { - todo!() + fn input_from_account(&self, account: &PayableAccount) -> Self::Input { + account.last_paid_timestamp } - fn diagnostics_config_opt(&self) -> Option> { - todo!() + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>> { + &AGE_DIAGNOSTICS_CONFIG_OPT } - - // let formula = |last_paid_timestamp: SystemTime| { - // let elapsed_secs: u64 = self - // .inner - // .now() - // .duration_since(last_paid_timestamp) - // .expect("time traveller") - // .as_secs(); - // let divisor = Self::compute_divisor(elapsed_secs); - // let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - // (elapsed_secs as u128) - // .checked_pow(AGE_MAIN_EXPONENT) - // .unwrap_or(u128::MAX) //TODO sensible and tested ???? - // .checked_div(divisor) - // .expect("div overflow") - // .checked_mul(log_multiplier) - // .expect("mul overflow") - // }; - // let criterion = formula(account.last_paid_timestamp); - // - // CriteriaWithDiagnostics { - // account, - // criterion, - // criteria_sum_so_far, - // diagnostics: DiagnosticsSetting { - // label: "AGE", - // diagnostics_adaptive_formula: |x: u128| { - // let secs_in_the_past = Duration::from_secs(x as u64); - // let approx_time_anchor = SystemTime::now() - // .checked_sub(secs_in_the_past) - // .expect("age formula characteristics blew up"); - // formula(approx_time_anchor) - // }, - // singleton_ref: &AGE_SINGLETON, - // bonds_safe_count_to_print: 10, - // }, - // } - // .diagnose_and_sum() } -pub struct BalanceCriterionCalculator<'a>{ - payment_adjuster:&'a PaymentAdjusterReal +// this parameter affects the steepness (sensitivity on increase in balance) +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; + +pub struct BalanceCriterionCalculator { + formula: Box u128>, } -impl <'a> BalanceCriterionCalculator<'a>{ - pub fn new(payment_adjuster: &'a PaymentAdjusterReal)->Self{ - todo!() +impl BalanceCriterionCalculator { + pub fn new() -> Self { + let formula = Box::new(|balance_wei: u128| { + let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); + balance_wei + .checked_mul(binary_weight as u128) + .expect("mul overflow") + }); + Self { formula } + } + + fn compute_binary_argument(balance_wei: u128) -> u128 { + x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) } } -impl CriterionCalculator for BalanceCriterionCalculator<'_> { +impl CriterionCalculator for BalanceCriterionCalculator { type Input = u128; - fn formula(&self) -> fn(Self::Input) -> u128 { - todo!() + fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { + self.formula.as_ref() } - fn form_input(&self, account: &PayableAccount) -> Self::Input { - todo!() + fn input_from_account(&self, account: &PayableAccount) -> Self::Input { + account.balance_wei } - fn diagnostics_config_opt(&self) -> Option> { - todo!() + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>> { + &BALANCE_DIAGNOSTICS_CONFIG_OPT } - - // // constants used to keep the weights of balance and time balanced - // let formula = |balance_wei: u128| { - // let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); - // let multiplied = balance_wei - // .checked_mul(binary_weight as u128) - // .expect("mul overflow"); - // multiplied - // }; - // let criterion = formula(account.balance_wei); - // - // CriteriaWithDiagnostics { - // account, - // criterion, - // criteria_sum_so_far, - // diagnostics: DiagnosticsSetting { - // label: "BALANCE", - // diagnostics_adaptive_formula: |x: u128| formula(x), - // singleton_ref: &BALANCE_SINGLETON, - // bonds_safe_count_to_print: EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len(), - // }, - // } - // .diagnose_and_sum() } pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { @@ -191,6 +166,12 @@ pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { calculator: C, } +impl CriteriaIterator { + fn new(iter: I, calculator: C) -> Self { + Self { iter, calculator } + } +} + impl Iterator for CriteriaIterator where I: Iterator, @@ -207,30 +188,36 @@ where } pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { - fn map_criteria(self, calculator: C) -> CriteriaIterator + fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator where Self: Sized; } impl CriteriaIteratorAdaptor for I { - fn map_criteria(self, calculator: C) -> CriteriaIterator { - todo!() + fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator { + CriteriaIterator::new(self, calculator) } } #[cfg(test)] mod tests { - use std::time::{Duration, SystemTime}; use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::{AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, AgeCriterionCalculator, CriterionCalculator}; - use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; - use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::payment_adjuster::auxiliary_fns::log_2; + use crate::accountant::payment_adjuster::criteria_calculators::{ + AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, + AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, + AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, + AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, + AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, + }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::test_utils::make_wallet; + use std::time::{Duration, SystemTime}; #[test] - fn constants_are_correct(){ - assert_eq!(AGE_MAIN_EXPONENT, 4); + fn constants_are_correct() { + assert_eq!(AGE_MAIN_EXPONENT, 3); assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); assert_eq!(AGE_MULTIPLIER, 10); assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); @@ -240,24 +227,6 @@ mod tests { assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); } - // let formula = |last_paid_timestamp: SystemTime| { - // let elapsed_secs: u64 = self - // .inner - // .now() - // .duration_since(last_paid_timestamp) - // .expect("time traveller") - // .as_secs(); - // let divisor = Self::compute_divisor(elapsed_secs); - // let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - // (elapsed_secs as u128) - // .checked_pow(AGE_MAIN_EXPONENT) - // .unwrap_or(u128::MAX) //TODO sensible and tested ???? - // .checked_div(divisor) - // .expect("div overflow") - // .checked_mul(log_multiplier) - // .expect("mul overflow") - // }; - #[test] fn compute_divisor_works() { let result: Vec<_> = [100, 81, 82, 80] @@ -270,7 +239,7 @@ mod tests { #[test] fn compute_descending_multiplier_works() { - let result: Vec<_> = EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS + let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18] .into_iter() .take(12) .map(|exp| 10_u64.pow(exp)) @@ -289,27 +258,25 @@ mod tests { ) } - #[test] - fn age_criteria_calculation_works(){ + fn age_criteria_calculation_works() { let now = SystemTime::now(); let payment_adjuster = make_initialized_subject(now, None, None); let subject = AgeCriterionCalculator::new(&payment_adjuster); - let input = SystemTime::now().checked_sub(Duration::from_secs(1500)).unwrap(); + let last_paid_timestamp = SystemTime::now() + .checked_sub(Duration::from_secs(1500)) + .unwrap(); - let result = subject.formula()(input); + let result = subject.formula()(last_paid_timestamp); let expected_criterion = { - let elapsed_secs: u64 = subject.payment_adjuster.inner - .now() - .duration_since(input) - .unwrap() - .as_secs(); + let elapsed_secs: u64 = now.duration_since(last_paid_timestamp).unwrap().as_secs(); let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); - let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); + let log_multiplier = + AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) .checked_pow(AGE_MAIN_EXPONENT) - .unwrap_or(u128::MAX) //TODO sensible and tested ???? + .unwrap() .checked_div(divisor) .unwrap() .checked_mul(log_multiplier) @@ -319,8 +286,47 @@ mod tests { } #[test] - fn balance_criteria_calculation_works(){ - todo!() + fn compute_binary_argument_works() { + let inputs = [ + 1, + BALANCE_LOG_2_ARG_DIVISOR - 1, + BALANCE_LOG_2_ARG_DIVISOR, + BALANCE_LOG_2_ARG_DIVISOR + 1, + BALANCE_LOG_2_ARG_DIVISOR + 1000, + ]; + + let result: Vec<_> = inputs + .into_iter() + .map(|arg| BalanceCriterionCalculator::compute_binary_argument(arg)) + .collect(); + + assert_eq!( + result, + vec![ + 1, + 1, + 1, + 1, + (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR + ] + ) } + #[test] + fn balance_criteria_calculation_works() { + let subject = BalanceCriterionCalculator::new(); + let balance_wei = 111_333_555_777; + + let result = subject.formula()(balance_wei); + + let expected_result = { + let binary_weight = log_2(BalanceCriterionCalculator::compute_binary_argument( + balance_wei, + )); + balance_wei + .checked_mul(binary_weight as u128) + .expect("mul overflow") + }; + assert_eq!(result, expected_result) + } } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index adab3e48e..604ecbd87 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,22 +1,15 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::DiagnosticsConfig; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; use itertools::Itertools; +use lazy_static::lazy_static; use std::fmt::Debug; use std::sync::{Mutex, Once}; +use std::time::{Duration, SystemTime}; use thousands::Separable; -pub static AGE_SINGLETON: Once = Once::new(); -pub static BALANCE_SINGLETON: Once = Once::new(); - -pub const COMPUTE_CRITERIA_PROGRESSIVE_CHARACTERISTICS: bool = true; -pub const FORMULAS_DIAGNOSTICS_SINGLETON: Once = Once::new(); - -pub const EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS: [u32; 14] = - [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25]; - pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; #[macro_export] @@ -64,86 +57,98 @@ pub fn diagnostics_collective(label: &str, accounts: &[PayableAccount]) { } } -//TODO kill this when you have CriteriaCumputers that can take characteristics tests on them -pub struct CriteriaWithDiagnostics<'a, F> -where - F: Fn(u128) -> u128, -{ - pub account: PayableAccount, - pub criterion: u128, - pub criteria_sum_so_far: u128, - pub diagnostics: DiagnosticsSetting<'a, F>, -} - -pub struct DiagnosticsSetting<'a, F> -where - F: Fn(u128) -> u128, -{ - pub label: &'static str, - pub diagnostics_adaptive_formula: F, - pub singleton_ref: &'a Once, - pub bonds_safe_count_to_print: usize, -} +#[cfg(test)] +pub mod formulas_progressive_characteristics { + use itertools::Itertools; + use lazy_static::lazy_static; + use std::fmt::Debug; + use std::sync::{Mutex, Once}; + use std::time::Duration; + use std::time::SystemTime; + use thousands::Separable; -// impl CriteriaWithDiagnostics<'_, F> -// where -// F: Fn(u128) -> u128, -// { -// pub fn diagnose_and_sum(self) -> (u128, PayableAccount) { -// let account = &self.account.wallet; -// let description = format!("COMPUTED {} CRITERIA", self.diagnostics.label); -// diagnostics!( -// account, -// &description, -// "{}", -// self.criterion.separate_with_commas() -// ); -// self.diagnostics.compute_progressive_characteristics(); -// -// ( -// self.criteria_sum_so_far -// .checked_add(self.criterion) -// .expect("add overflow"), -// self.account, -// ) -// } -// } + pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; + //mutex should be fine for debugging, no need for mut static + static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); + static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); -pub const STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); + pub struct DiagnosticsConfig { + label: &'static str, + progressive_x_axis_supply_non_native: Vec, + x_axis_native_type_formatter: Box A + Send>, + } -fn print_formulas_diagnostics() { - FORMULAS_DIAGNOSTICS_SINGLETON.call_once(|| { - let report = STRINGS_WITH_FORMULAS_CHARACTERISTICS - .lock() - .expect("diagnostics poisoned") - .join("\n\n"); - eprintln!("{}", report) - }) -} + lazy_static! { + pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + let now = SystemTime::now(); + let x_axis_supply = { + [1, 2, 3, 4, 5, 6, 7, 8, 9, 12] + .into_iter() + .map(|exp| 10_u128.pow(exp)) + .collect() + }; + Mutex::new(Some(DiagnosticsConfig { + label: "AGE", + progressive_x_axis_supply_non_native: x_axis_supply, + x_axis_native_type_formatter: Box::new(move |secs_since_last_paid_payable| { + now.checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling") + }), + })) + }; + pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + let x_axis_supply = { + let now = SystemTime::now(); + [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] + .into_iter() + .map(|exp| 10_u128.pow(exp)) + .collect() + }; + Mutex::new(Some(DiagnosticsConfig { + label: "BALANCE", + progressive_x_axis_supply_non_native: x_axis_supply, + x_axis_native_type_formatter: Box::new(|balance_wei| balance_wei), + })) + }; + } -pub fn compute_progressive_characteristics( - config_opt: Option>, - formula: fn(A) -> u128, -) where - A: Debug, -{ - config_opt.map(|config| { - let characteristics = config - .progressive_set_of_args - .into_iter() - .map(|input| { - let input_print = format!("{:?}", input); - format!( - "x: {:( + config_opt: Option>, + formula: &dyn Fn(A) -> u128, + ) where + A: Debug, + { + config_opt.map(|config| { + let config_x_axis_type_formatter = config.x_axis_native_type_formatter; + let characteristics = config + .progressive_x_axis_supply_non_native + .into_iter() + .map(|input| { + let correctly_formatted_input = config_x_axis_type_formatter(input); + format!( + "x: {:, ) -> Vec<(u128, PayableAccount)> { @@ -301,13 +295,13 @@ impl PaymentAdjusterReal { } //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_full_adjustment_procedure - fn run_adjustment_by_criteria_recursively( + fn perform_adjustment_recursively( &mut self, - sorted_accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, previously_resolved_qualified_accounts: Vec, ) -> Vec { let adjustment_result: AdjustmentIterationSummary = - match self.give_job_to_adjustment_workers(sorted_accounts_with_individual_criteria) { + match self.give_job_to_adjustment_workers(accounts_with_individual_criteria_sorted) { AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, AdjustmentCompletion::Continue(iteration_result) => iteration_result, }; @@ -339,14 +333,14 @@ impl PaymentAdjusterReal { fn initialize_zero_criteria( qualified_payables: Vec, ) -> impl Iterator { - fn just_zero_criteria_iterator(accounts_count: usize) -> impl Iterator { + fn only_zero_criteria_iterator(accounts_count: usize) -> impl Iterator { let one_element = once(0_u128); let endlessly_repeated = one_element.into_iter().cycle(); endlessly_repeated.take(accounts_count) } let accounts_count = qualified_payables.len(); - let criteria_iterator = just_zero_criteria_iterator(accounts_count); + let criteria_iterator = only_zero_criteria_iterator(accounts_count); criteria_iterator.zip(qualified_payables.into_iter()) } @@ -354,87 +348,16 @@ impl PaymentAdjusterReal { &self, accounts_with_zero_criteria: impl Iterator, ) -> Vec<(u128, PayableAccount)> { - //define individual criteria as closures to be used in a map() - - //caution: always remember to use checked math operations! - - // let age_criterion_closure: CriterionFormula = Box::new(|(criteria_sum_so_far, account)| { - // let formula = |last_paid_timestamp: SystemTime| { - // let elapsed_secs: u64 = self - // .inner - // .now() - // .duration_since(last_paid_timestamp) - // .expect("time traveller") - // .as_secs(); - // let divisor = Self::compute_divisor(elapsed_secs); - // let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - // (elapsed_secs as u128) - // .checked_pow(AGE_MAIN_EXPONENT) - // .unwrap_or(u128::MAX) //TODO sensible and tested ???? - // .checked_div(divisor) - // .expect("div overflow") - // .checked_mul(log_multiplier) - // .expect("mul overflow") - // }; - // let criterion = formula(account.last_paid_timestamp); - // - // todo!() - // // CriteriaWithDiagnostics { - // // account, - // // criterion, - // // criteria_sum_so_far, - // // diagnostics: DiagnosticsSetting { - // // label: "AGE", - // // diagnostics_adaptive_formula: |x: u128| { - // // let secs_in_the_past = Duration::from_secs(x as u64); - // // let approx_time_anchor = SystemTime::now() - // // .checked_sub(secs_in_the_past) - // // .expect("age formula characteristics blew up"); - // // formula(approx_time_anchor) - // // }, - // // singleton_ref: &AGE_SINGLETON, - // // bonds_safe_count_to_print: 10, - // // }, - // // } - // // .diagnose_and_sum() - // }); - // let balance_criterion_closure: CriterionFormula = - // Box::new(|(criteria_sum_so_far, account)| { - // // constants used to keep the weights of balance and time balanced - // let formula = |balance_wei: u128| { - // let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); - // let multiplied = balance_wei - // .checked_mul(binary_weight as u128) - // .expect("mul overflow"); - // multiplied - // }; - // let criterion = formula(account.balance_wei); - // - // todo!() - // // CriteriaWithDiagnostics { - // // account, - // // criterion, - // // criteria_sum_so_far, - // // diagnostics: DiagnosticsSetting { - // // label: "BALANCE", - // // diagnostics_adaptive_formula: |x: u128| formula(x), - // // singleton_ref: &BALANCE_SINGLETON, - // // bonds_safe_count_to_print: EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS.len(), - // // }, - // // } - // // .diagnose_and_sum() - // }); - let weights_and_accounts = accounts_with_zero_criteria - .map_criteria(AgeCriterionCalculator::new(self)) - .map_criteria(BalanceCriterionCalculator::new(self)); + .iterate_for_criteria(AgeCriterionCalculator::new(self)) + .iterate_for_criteria(BalanceCriterionCalculator::new()); + let collected_accounts_with_criteria = + sort_in_descendant_order_by_weights(weights_and_accounts); - sort_in_descendant_order_by_weights(weights_and_accounts) - } + // effective only if the iterator is collected + print_formulas_characteristics_for_diagnostics(); - //TODO this fn should later become property of the balance criteria computing class e.g. "CriteriaComputer" - fn compute_binary_argument(balance_wei: u128) -> u128 { - x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) + collected_accounts_with_criteria } fn give_job_to_adjustment_workers( @@ -535,7 +458,7 @@ impl PaymentAdjusterReal { let unallocated_cw_masq_balance = self.inner.unallocated_cw_masq_balance(); let verified_and_exhaustive = - exhaust_cw_balance_as_much_as_possible(verified_accounts, unallocated_cw_masq_balance); + exhaust_cw_balance_totally(verified_accounts, unallocated_cw_masq_balance); AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_and_exhaustive) } @@ -547,7 +470,7 @@ impl PaymentAdjusterReal { ) -> Vec { let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); let multiplication_coeff = - compute_fractions_preventing_mul_coeff(cw_masq_balance, criteria_total); + compute_fraction_preventing_mul_coeff(cw_masq_balance, criteria_total); let cw_masq_balance_big_u256 = U256::from(cw_masq_balance); let criteria_total_u256 = U256::from(criteria_total); let multiplication_coeff_u256 = U256::from(multiplication_coeff); @@ -767,7 +690,7 @@ impl PaymentAdjusterReal { }) > cw_masq_balance { todo!("will we ever be here again?"); - self.run_adjustment_by_criteria_recursively(outweighed_with_criteria, vec![]) + self.perform_adjustment_recursively(outweighed_with_criteria, vec![]) // // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { // todo!() @@ -912,9 +835,11 @@ pub enum AnalysisError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fractions_preventing_mul_coeff, criteria_total, log_2, + compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, log_2, + }; + use crate::accountant::payment_adjuster::criteria_calculators::{ + AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, }; - use crate::accountant::payment_adjuster::diagnostics::EXPONENTS_OF_10_AS_VALUES_FOR_X_AXIS; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; @@ -922,7 +847,7 @@ mod tests { AccountsRecreationResult, AdjustedAccountBeforeFinalization, Adjustment, AnalysisError, DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - BALANCE_LOG_2_ARG_DIVISOR, PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, + PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -942,12 +867,10 @@ mod tests { use std::vec; use thousands::Separable; use web3::types::U256; - use crate::accountant::payment_adjuster::criteria_calculators::AgeCriterionCalculator; #[test] fn constants_are_correct() { assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); - assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 9); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, PercentageAccountInsignificance { @@ -1167,7 +1090,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let weights_and_accounts = subject.pair_accounts_with_summed_criteria(qualified_payables); + let weights_and_accounts = subject.calculate_criteria_sums_for_accounts(qualified_payables); let only_accounts = weights_and_accounts .iter() @@ -1176,33 +1099,6 @@ mod tests { assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } - #[test] - fn compute_binary_argument_works() { - let inputs = [ - 1, - BALANCE_LOG_2_ARG_DIVISOR - 1, - BALANCE_LOG_2_ARG_DIVISOR, - BALANCE_LOG_2_ARG_DIVISOR + 1, - BALANCE_LOG_2_ARG_DIVISOR + 1000, - ]; - - let result: Vec<_> = inputs - .into_iter() - .map(|arg| PaymentAdjusterReal::compute_binary_argument(arg)) - .collect(); - - assert_eq!( - result, - vec![ - 1, - 1, - 1, - 1, - (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR - ] - ) - } - #[test] fn only_the_least_demanding_disqualified_account_is_picked_at_a_time_even_though_more_of_them_can_be_found( ) { @@ -1240,7 +1136,7 @@ mod tests { pending_payable_opt: None, }; let accounts_with_individual_criteria = - subject.pair_accounts_with_summed_criteria(vec![account_1, account_2, account_3]); + subject.calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3]); let criteria_total = criteria_total(&accounts_with_individual_criteria); let unfinalized_adjusted_accounts = subject.compute_unfinalized_adjusted_accounts( accounts_with_individual_criteria, @@ -1283,7 +1179,8 @@ mod tests { let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important - let criteria_and_accounts = subject.pair_accounts_with_summed_criteria(qualified_payables); + let criteria_and_accounts = + subject.calculate_criteria_sums_for_accounts(qualified_payables); let criteria_total = criteria_total(&criteria_and_accounts); let account_2_criterion = criteria_and_accounts[1].0; let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; @@ -1342,7 +1239,7 @@ mod tests { }; let accounts = vec![account_1.clone(), account_2.clone()]; let accounts_with_individual_criteria = - subject.pair_accounts_with_summed_criteria(accounts); + subject.calculate_criteria_sums_for_accounts(accounts); let criteria_total = criteria_total(&accounts_with_individual_criteria); let result = subject.recreate_accounts_with_proportioned_balances( @@ -1440,27 +1337,28 @@ mod tests { let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 4_444_444_444_444_444_444, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_234)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 6_666_666_666_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghk"), - balance_wei: 60_000_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), + balance_wei: 6_000_000_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let accounts_sum: u128 = - 4_444_444_444_444_444_444 + 6_666_666_666_000_000_000 + 60_000_000_000_000_000; //= 1_000_022_000_000_444_444 - let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 70_000_000_000_000_000); + 4_444_444_444_444_444_444 + 6_666_666_666_000_000_000 + 6_000_000_000_000_000_000; + let consuming_wallet_masq_balance_wei = + U256::from(accounts_sum - 2_000_000_000_000_000_000); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1482,13 +1380,12 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - let expected_criteria_computation_output = emulation_of_the_actual_adjustment_algorithm( - account_1, - account_2, - Some(account_3), - consuming_wallet_masq_balance_wei.as_u128(), - now, - ); + let expected_criteria_computation_output = + simplified_emulation_of_real_adjustment_algorithm( + vec![account_1, account_2, account_3], + consuming_wallet_masq_balance_wei.as_u128(), + now, + ); assert_eq!( result, OutcomingPaymentsInstructions { @@ -1504,11 +1401,11 @@ mod tests { | Adjusted | |0x0000000000000000000000000000000000646566 6666666666000000000 -| 6627452261727177476 +| 5833507422574361619 +|0x000000000000000000000000000000000067686b 6000000000000000000 +| 5381690760353303862 |0x0000000000000000000000000000000000616263 4444444444444444444 -| 4418301511592311819 -|0x000000000000000000000000000000000067686b 60000000000000000 -| 55357206066732915" +| 3895912927516778963" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1646,10 +1543,8 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - let expected_accounts_first_iteration = emulation_of_the_actual_adjustment_algorithm( - account_1.clone(), - account_2.clone(), - Some(account_3), + let expected_accounts_first_iteration = simplified_emulation_of_real_adjustment_algorithm( + vec![account_1.clone(), account_2.clone(), account_3], consuming_wallet_masq_balance_wei.as_u128(), now, ); @@ -1668,20 +1563,11 @@ mod tests { account_3_adjusted_balance.separate_with_commas(), minimum_allowed.separate_with_commas() ); - let expected_accounts = emulation_of_the_actual_adjustment_algorithm( - account_1, - account_2, - None, + let expected_accounts = simplified_emulation_of_real_adjustment_algorithm( + vec![account_1, account_2], consuming_wallet_masq_balance_wei.as_u128(), now, ); - //TODO delete this garbage - // let wallets_of_final_accounts = result - // .accounts - // .iter() - // .map(|account| account.wallet.clone()) - // .collect::>(); - // assert_eq!(wallets_of_final_accounts, vec![wallet_1, wallet_2]); assert_eq!(result.accounts, expected_accounts); assert_eq!( result.response_skeleton_opt, @@ -2123,43 +2009,18 @@ mod tests { } } - fn emulation_of_the_actual_adjustment_algorithm( - account_1: PayableAccount, - account_2: PayableAccount, - account_3_opt: Option, + fn simplified_emulation_of_real_adjustment_algorithm( + accounts: Vec, consuming_wallet_masq_balance_wei: u128, now: SystemTime, ) -> Vec { - let accounts = vec![ - Some(account_1.clone()), - Some(account_2.clone()), - account_3_opt.clone(), - ] - .into_iter() - .flatten() - .collect::>(); - //TODO continue here ... fix it so that, if possible, you don't have to use the whole subject here, which has its over-time varying inner state - let age_criteria = AgeCriterionCalculator::; - let balance_criteria = accounts + let subject = make_initialized_subject(now, Some(consuming_wallet_masq_balance_wei), None); + let final_criteria = subject.calculate_criteria_sums_for_accounts(accounts.clone()); + let final_criteria_sum = final_criteria .iter() - .map(|account| { - let balance = account.balance_wei; - let significance = log_2(balance / BALANCE_LOG_2_ARG_DIVISOR) as u128; - balance * significance - } as u128) - .collect(); - - let final_criteria = vec![age_criteria, balance_criteria].into_iter().fold( - vec![0, 0, 0], - |acc: Vec, current: Vec| { - acc.into_iter() - .zip(current.into_iter()) - .map(|(partial_acc, partial_current)| partial_acc + partial_current) - .collect() - }, - ); - let final_criteria_sum = final_criteria.iter().sum::(); - let multiplication_coeff = compute_fractions_preventing_mul_coeff( + .map(|(criteria_sum, _)| criteria_sum) + .sum::(); + let multiplication_coeff = compute_fraction_preventing_mul_coeff( consuming_wallet_masq_balance_wei, final_criteria_sum, ); @@ -2168,13 +2029,19 @@ mod tests { .unwrap() .checked_div(final_criteria_sum) .unwrap(); - let balanced_portions = final_criteria - .iter() - .map(|criterion| { - in_ratio_fragment_of_available_balance * criterion / multiplication_coeff + let balanced_portions_and_original_accounts = final_criteria + .into_iter() + .map(|(criterion, account)| { + ( + in_ratio_fragment_of_available_balance * criterion / multiplication_coeff, + account, + ) }) - .collect::>(); - let new_total_amount_to_pay = balanced_portions.iter().sum::(); + .collect::>(); + let new_total_amount_to_pay = balanced_portions_and_original_accounts + .iter() + .map(|(proposed_portion, _)| proposed_portion) + .sum::(); assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); assert!( new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, @@ -2182,32 +2049,16 @@ mod tests { new_total_amount_to_pay, consuming_wallet_masq_balance_wei ); - let mut account_1_adjusted = account_1; - account_1_adjusted.balance_wei = balanced_portions[0]; - let mut account_2_adjusted = account_2; - account_2_adjusted.balance_wei = balanced_portions[1]; - let account_3_adjusted_opt = { - match account_3_opt { - Some(mut account) => Some({ - account.balance_wei = balanced_portions[2]; - account - }), - None => None, - } - }; - - vec![ - Some((final_criteria[0], account_1_adjusted)), - Some((final_criteria[1], account_2_adjusted)), - match account_3_adjusted_opt { - Some(account) => Some((final_criteria[2], account)), - None => None, - }, - ] - .into_iter() - .flatten() - .sorted_by(|(criterion_a, _), (criterion_b, _)| Ord::cmp(&criterion_b, &criterion_a)) - .map(|(_, account)| account) - .collect() + let accounts_before_exhaustion = balanced_portions_and_original_accounts + .into_iter() + .map(|(proposed_balance, account)| { + //criteria left 0 because here irrelevant + AdjustedAccountBeforeFinalization::new(account, proposed_balance, 0) + }) + .collect(); + exhaust_cw_balance_totally( + accounts_before_exhaustion, + consuming_wallet_masq_balance_wei, + ) } } From ddc56e8dc7fa989be87119e605d43698003f1015 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 1 Aug 2023 11:29:28 +0200 Subject: [PATCH 054/250] GH-711: fixing warnings and logger that is newly handled in a different way --- node/src/accountant/mod.rs | 21 ++---- .../payment_adjuster/auxiliary_fns.rs | 34 +++++---- .../payment_adjuster/criteria_calculators.rs | 3 - .../payment_adjuster/diagnostics.rs | 52 +++++++------- node/src/accountant/payment_adjuster/mod.rs | 69 +++++++------------ node/src/accountant/scanners/mod.rs | 6 +- .../scanners/scan_mid_procedures.rs | 3 - node/src/accountant/test_utils.rs | 14 ++-- 8 files changed, 78 insertions(+), 124 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 7db4e2821..0fd9b358f 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -646,13 +646,13 @@ impl Accountant { } fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) { - let bb_instructions = match self.scanners.payable.try_softly(msg, &self.logger) { + let bb_instructions = match self.scanners.payable.try_softly(msg) { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 self.scanners .payable - .exacting_payments_instructions(unaccepted_msg, &self.logger) + .exacting_payments_instructions(unaccepted_msg) } Err(_e) => todo!("be completed by GH-711"), }; @@ -1427,8 +1427,7 @@ mod tests { search_for_indispensable_adjustment_params_arc .lock() .unwrap(); - let (payable_payment_setup_msg, logger_clone) = - search_for_indispensable_adjustment_params.remove(0); + let payable_payment_setup_msg = search_for_indispensable_adjustment_params.remove(0); assert!(search_for_indispensable_adjustment_params.is_empty()); assert_eq!( payable_payment_setup_msg, @@ -1445,17 +1444,6 @@ mod tests { }) } ); - test_use_of_the_same_logger(&logger_clone, test_name) - // adjust_payments() did not need a prepared result which means it wasn't reached - // because otherwise this test would've panicked - } - - fn test_use_of_the_same_logger(logger_clone: &Logger, test_name: &str) { - let experiment_msg = format!("DEBUG: {test_name}: hello world"); - let log_handler = TestLogHandler::default(); - log_handler.exists_no_log_containing(&experiment_msg); - debug!(logger_clone, "hello world"); - log_handler.exists_log_containing(&experiment_msg); } #[test] @@ -1528,7 +1516,7 @@ mod tests { assert_eq!(system.run(), 0); let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); - let (cwbqp_msg, captured_now, logger_clone) = adjust_payments_params.remove(0); + let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); assert_eq!( cwbqp_msg, AwaitedAdjustment { @@ -1546,7 +1534,6 @@ mod tests { response_skeleton_opt: Some(response_skeleton) } ); - test_use_of_the_same_logger(&logger_clone, test_name) } #[test] diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index e12d71572..6b7ddc896 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -1,13 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::{ AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, }; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use std::iter::successors; - const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; @@ -19,12 +19,6 @@ where collection.iter().map(arranger).sum::().into() } -pub fn balance_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { - sum_as(&accounts_with_individual_criteria, |(_, account)| { - account.balance_wei - }) -} - pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { sum_as(&accounts_with_individual_criteria, |(criteria, _)| { *criteria @@ -86,17 +80,16 @@ impl ExhaustionStatus { fn update_and_add( mut self, mut unfinalized_account_info: AdjustedAccountBeforeFinalization, - adjusted_balance_possible_addition: u128, + possible_extra_addition: u128, ) -> Self { let corrected_adjusted_account_before_finalization = { - unfinalized_account_info.proposed_adjusted_balance = unfinalized_account_info - .proposed_adjusted_balance - + adjusted_balance_possible_addition; + unfinalized_account_info.proposed_adjusted_balance = + unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition; unfinalized_account_info }; self.remainder = self .remainder - .checked_sub(adjusted_balance_possible_addition) + .checked_sub(possible_extra_addition) .unwrap_or(0); //TODO wait for overflow self.add(corrected_adjusted_account_before_finalization) } @@ -132,12 +125,21 @@ pub fn exhaust_cw_balance_totally( if status.remainder != 0 { let balance_gap = unfinalized_account_info.original_account.balance_wei - unfinalized_account_info.proposed_adjusted_balance; - let adjusted_balance_possible_addition = if balance_gap < status.remainder { + let possible_extra_addition = if balance_gap < status.remainder { balance_gap } else { status.remainder }; - status.update_and_add(unfinalized_account_info, adjusted_balance_possible_addition) + + diagnostics!( + "EXHAUSTING CW ON PAYMENT", + "For account {} from proposed {} to the possible maximum of {}", + unfinalized_account_info.original_account.wallet, + unfinalized_account_info.proposed_adjusted_balance, + unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition + ); + + status.update_and_add(unfinalized_account_info, possible_extra_addition) } else { status.add(unfinalized_account_info) } @@ -204,7 +206,6 @@ mod tests { use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use std::collections::HashMap; use std::time::SystemTime; #[test] @@ -495,9 +496,6 @@ mod tests { let wallet_3 = make_wallet("ghi"); let original_requested_balance_3 = 41_000_000; let proposed_adjusted_balance_3 = 40_980_000; - let wallet_3 = make_wallet("ghi"); - // let original_requested_balance_4 = 41_000_000; - // let proposed_adjusted_balance_4 = 40_980_000; let unallocated_cw_balance = original_requested_balance_2 + original_requested_balance_3 + proposed_adjusted_balance_1 diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs index 1c4254daf..d4fe7c4ae 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators.rs @@ -201,7 +201,6 @@ impl CriteriaIteratorAdaptor for I { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::log_2; use crate::accountant::payment_adjuster::criteria_calculators::{ AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, @@ -211,8 +210,6 @@ mod tests { AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; - use crate::accountant::payment_adjuster::PaymentAdjusterReal; - use crate::test_utils::make_wallet; use std::time::{Duration, SystemTime}; #[test] diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 604ecbd87..27bb4e679 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,14 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; -use itertools::Itertools; -use lazy_static::lazy_static; -use std::fmt::Debug; -use std::sync::{Mutex, Once}; -use std::time::{Duration, SystemTime}; -use thousands::Separable; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -62,6 +55,7 @@ pub mod formulas_progressive_characteristics { use itertools::Itertools; use lazy_static::lazy_static; use std::fmt::Debug; + use std::iter::once; use std::sync::{Mutex, Once}; use std::time::Duration; use std::time::SystemTime; @@ -97,13 +91,10 @@ pub mod formulas_progressive_characteristics { })) }; pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let x_axis_supply = { - let now = SystemTime::now(); - [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] - .into_iter() - .map(|exp| 10_u128.pow(exp)) - .collect() - }; + let x_axis_supply = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] + .into_iter() + .map(|exp| 10_u128.pow(exp)) + .collect(); Mutex::new(Some(DiagnosticsConfig { label: "BALANCE", progressive_x_axis_supply_non_native: x_axis_supply, @@ -132,23 +123,28 @@ pub mod formulas_progressive_characteristics { { config_opt.map(|config| { let config_x_axis_type_formatter = config.x_axis_native_type_formatter; - let characteristics = config - .progressive_x_axis_supply_non_native - .into_iter() - .map(|input| { - let correctly_formatted_input = config_x_axis_type_formatter(input); - format!( - "x: {: Result, AnalysisError>; fn adjust_payments( &mut self, setup: AwaitedAdjustment, now: SystemTime, - logger: &Logger, ) -> OutcomingPaymentsInstructions; declare_as_any!(); @@ -73,7 +71,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, - logger: &Logger, ) -> Result, AnalysisError> { let qualified_payables = msg.qualified_payables.as_slice(); let this_stage_data = match msg @@ -87,7 +84,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { match Self::determine_transactions_count_limit_by_gas( &this_stage_data, qualified_payables.len(), - logger, + &self.logger, ) { Ok(None) => (), Ok(Some(limited_count_from_gas)) => { @@ -99,7 +96,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; match Self::check_need_of_masq_balances_adjustment( - logger, + &self.logger, Either::Left(qualified_payables), this_stage_data .consuming_wallet_balances @@ -115,7 +112,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { &mut self, setup: AwaitedAdjustment, now: SystemTime, - logger: &Logger, //TODO fix this later ) -> OutcomingPaymentsInstructions { let msg = setup.original_setup_msg; let qualified_payables: Vec = msg.qualified_payables; @@ -170,8 +166,6 @@ struct PercentageAccountInsignificance { divisor: u128, } -type CriterionFormula<'a> = Box (u128, PayableAccount) + 'a>; - impl Default for PaymentAdjusterReal { fn default() -> Self { Self::new() @@ -237,6 +231,7 @@ impl PaymentAdjusterReal { } } + //TODO we should check there is at least one half of the smallest payment fn check_need_of_masq_balances_adjustment( logger: &Logger, qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, @@ -446,7 +441,6 @@ impl PaymentAdjusterReal { Right(with_some_outweighed) => return with_some_outweighed, }; - //TODO this need to return the wider type used during the polishing let verified_accounts = match Self::consider_account_disqualification_from_percentage_insignificance( unchecked_for_disqualified, @@ -835,10 +829,7 @@ pub enum AnalysisError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, log_2, - }; - use crate::accountant::payment_adjuster::criteria_calculators::{ - AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, + compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, @@ -860,7 +851,7 @@ mod tests { }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; - use itertools::{Either, Itertools}; + use itertools::Either; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; @@ -894,8 +885,9 @@ mod tests { fn search_for_indispensable_adjustment_negative_answer() { init_test_logging(); let test_name = "search_for_indispensable_adjustment_negative_answer"; - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); + subject.logger = logger; //masq balance > payments let msg_1 = make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 14], 100)), None); @@ -925,7 +917,7 @@ mod tests { [msg_1, msg_2, msg_3, msg_4].into_iter().for_each(|msg| { assert_eq!( - subject.search_for_indispensable_adjustment(&msg, &logger), + subject.search_for_indispensable_adjustment(&msg), Ok(None), "failed for msg {:?}", msg @@ -940,11 +932,12 @@ mod tests { init_test_logging(); let test_name = "search_for_indispensable_adjustment_positive_for_masq_token"; let logger = Logger::new(test_name); - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = logger; let msg = make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 16], 100)), None); - let result = subject.search_for_indispensable_adjustment(&msg, &logger); + let result = subject.search_for_indispensable_adjustment(&msg); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); let log_handler = TestLogHandler::new(); @@ -960,7 +953,8 @@ mod tests { init_test_logging(); let test_name = "search_for_indispensable_adjustment_positive_for_gas"; let logger = Logger::new(test_name); - let subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = logger; let number_of_payments = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( None, @@ -972,7 +966,7 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(&msg, &logger); + let result = subject.search_for_indispensable_adjustment(&msg); let expected_limiting_count = number_of_payments as u16 - 1; assert_eq!( @@ -1006,7 +1000,7 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(&msg, &Logger::new("test")); + let result = subject.search_for_indispensable_adjustment(&msg); assert_eq!( result, @@ -1019,7 +1013,6 @@ mod tests { #[test] fn list_accounts_under_the_disqualification_limit_employs_manifest_consts_of_insignificance() { - let cw_masq_balance = 1_000_000; let account_balance = 1_000_000; let prepare_account = |n: u64| { let mut account = make_payable_account(n); @@ -1027,11 +1020,8 @@ mod tests { account }; let payable_account_1 = prepare_account(1); - let wallet_1 = payable_account_1.wallet.clone(); let payable_account_2 = prepare_account(2); - let wallet_2 = payable_account_2.wallet.clone(); let payable_account_3 = prepare_account(3); - let wallet_3 = payable_account_3.wallet.clone(); const IRRELEVANT_CRITERIA_SUM: u128 = 1111; let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; @@ -1135,8 +1125,8 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - let accounts_with_individual_criteria = - subject.calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3]); + let accounts_with_individual_criteria = subject + .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); let criteria_total = criteria_total(&accounts_with_individual_criteria); let unfinalized_adjusted_accounts = subject.compute_unfinalized_adjusted_accounts( accounts_with_individual_criteria, @@ -1173,7 +1163,6 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), pending_payable_opt: None, }; - let logger = Logger::new("test"); let qualified_payables = vec![account_1, account_2.clone()]; let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); @@ -1303,7 +1292,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let result = subject.adjust_payments(adjustment_setup, now); //because the proposed final balances all all way lower than (at least) the half of the original balances assert_eq!(result.accounts, vec![]); @@ -1378,7 +1367,7 @@ mod tests { adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual gas balance limitations }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let result = subject.adjust_payments(adjustment_setup, now); let expected_criteria_computation_output = simplified_emulation_of_real_adjustment_algorithm( @@ -1459,7 +1448,7 @@ mod tests { }, }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let result = subject.adjust_payments(adjustment_setup, now); assert_eq!( result, @@ -1541,7 +1530,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let result = subject.adjust_payments(adjustment_setup, now); let expected_accounts_first_iteration = simplified_emulation_of_real_adjustment_algorithm( vec![account_1.clone(), account_2.clone(), account_3], @@ -1641,9 +1630,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let mut result = subject - .adjust_payments(adjustment_setup, now, &Logger::new(test_scenario_name)) - .accounts; + let mut result = subject.adjust_payments(adjustment_setup, now).accounts; let winning_account = result.remove(0); assert_eq!( @@ -1917,7 +1904,7 @@ mod tests { }, }; - let mut result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); + let mut result = subject.adjust_payments(adjustment_setup, now); let only_account = result.accounts.remove(0); assert_eq!( @@ -1950,10 +1937,6 @@ mod tests { TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } - fn secs_elapsed(timestamp: SystemTime, now: SystemTime) -> u128 { - now.duration_since(timestamp).unwrap().as_secs() as u128 - } - struct GasTestConditions { desired_gas_price_gwei: u64, number_of_payments: usize, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 3ffbf2427..a30f1ae09 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -258,12 +258,11 @@ impl PayableScannerMiddleProcedures for PayableScanner { fn try_softly( &self, msg: PayablePaymentSetup, - logger: &Logger, ) -> Result, String> { match self .payment_adjuster .borrow() - .search_for_indispensable_adjustment(&msg, logger) + .search_for_indispensable_adjustment(&msg) { Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { accounts: msg.qualified_payables, @@ -277,12 +276,11 @@ impl PayableScannerMiddleProcedures for PayableScanner { fn exacting_payments_instructions( &self, setup: AwaitedAdjustment, - logger: &Logger, ) -> OutcomingPaymentsInstructions { let now = SystemTime::now(); self.payment_adjuster .borrow_mut() - .adjust_payments(setup, now, logger) + .adjust_payments(setup, now) } } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 4daefcf6a..c1e36403f 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -6,7 +6,6 @@ use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use actix::Message; use itertools::Either; -use masq_lib::logger::Logger; pub trait PayableScannerWithMiddleProcedures: Scanner + PayableScannerMiddleProcedures @@ -20,14 +19,12 @@ pub trait PayableScannerMiddleProcedures { fn try_softly( &self, _msg: PayablePaymentSetup, - _logger: &Logger, ) -> Result, String> { intentionally_blank!() } fn exacting_payments_instructions( &self, _setup: AwaitedAdjustment, - _logger: &Logger, ) -> OutcomingPaymentsInstructions { intentionally_blank!() } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 311402fb2..a3225cef5 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1391,10 +1391,10 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - search_for_indispensable_adjustment_params: Arc>>, + search_for_indispensable_adjustment_params: Arc>>, search_for_indispensable_adjustment_results: RefCell, AnalysisError>>>, - adjust_payments_params: Arc>>, + adjust_payments_params: Arc>>, adjust_payments_results: RefCell>, } @@ -1402,12 +1402,11 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, - logger: &Logger, ) -> Result, AnalysisError> { self.search_for_indispensable_adjustment_params .lock() .unwrap() - .push((msg.clone(), logger.clone())); + .push(msg.clone()); self.search_for_indispensable_adjustment_results .borrow_mut() .remove(0) @@ -1417,12 +1416,11 @@ impl PaymentAdjuster for PaymentAdjusterMock { &mut self, setup: AwaitedAdjustment, now: SystemTime, - logger: &Logger, ) -> OutcomingPaymentsInstructions { self.adjust_payments_params .lock() .unwrap() - .push((setup, now, logger.clone())); + .push((setup, now)); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1430,7 +1428,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn search_for_indispensable_adjustment_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.search_for_indispensable_adjustment_params = params.clone(); self @@ -1448,7 +1446,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self From 0961a12072c6fc7320a57ebbe03742bcce823f6e Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 1 Aug 2023 12:10:59 +0200 Subject: [PATCH 055/250] GH-711: interim commit --- .../payment_adjuster/auxiliary_fns.rs | 13 +++++++-- node/src/accountant/payment_adjuster/mod.rs | 27 ++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/auxiliary_fns.rs index 6b7ddc896..d177265a0 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/auxiliary_fns.rs @@ -121,10 +121,19 @@ pub fn exhaust_cw_balance_totally( &info_b.proposed_adjusted_balance, ) }) + .inspect(|account_info|eprintln!("{:?}", account_info)) .fold(init, |status, unfinalized_account_info| { if status.remainder != 0 { - let balance_gap = unfinalized_account_info.original_account.balance_wei - - unfinalized_account_info.proposed_adjusted_balance; + let balance_gap = unfinalized_account_info + .original_account + .balance_wei + .checked_sub(unfinalized_account_info.proposed_adjusted_balance) + .unwrap_or_else(||panic!( + "proposed balance should never bigger than the original but proposed: {} \ + and original: {}", + unfinalized_account_info.proposed_adjusted_balance, + unfinalized_account_info.original_account.balance_wei + )); let possible_extra_addition = if balance_gap < status.remainder { balance_gap } else { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index aa08b0257..f2b053966 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -130,7 +130,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = self.run_full_adjustment_procedure(qualified_payables, vec![]); + let adjusted_accounts = self.run_adjustment_procedure(qualified_payables, vec![]); debug!( self.logger, @@ -260,7 +260,7 @@ impl PaymentAdjusterReal { } } - fn run_full_adjustment_procedure( + fn run_adjustment_procedure( &mut self, unresolved_qualified_accounts: Vec, previously_resolved_qualified_accounts: Vec, @@ -289,7 +289,7 @@ impl PaymentAdjusterReal { self.apply_criteria(zero_criteria_accounts) } - //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_full_adjustment_procedure + //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_adjustment_procedure fn perform_adjustment_recursively( &mut self, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, @@ -309,8 +309,11 @@ impl PaymentAdjusterReal { let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { adjustment_result.decided_accounts } else { - self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts); - return self.run_full_adjustment_procedure( + if adjustment_result.disqualified_account_opt.is_none() { + // meaning we found some outweighed accounts in the previous iteration + self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) + } + return self.run_adjustment_procedure( adjustment_result.remaining_accounts, adjustment_result.decided_accounts, ); @@ -675,7 +678,8 @@ impl PaymentAdjusterReal { vec![PayableAccount { balance_wei: cw_masq_balance, ..only_account - }] + }]; + todo!("untested!!!!") } else { vec![only_account] } @@ -1165,7 +1169,7 @@ mod tests { }; let qualified_payables = vec![account_1, account_2.clone()]; - let result = subject.run_full_adjustment_procedure(qualified_payables.clone(), vec![]); + let result = subject.run_adjustment_procedure(qualified_payables.clone(), vec![]); //first a presentation of why this test is important let criteria_and_accounts = @@ -1477,9 +1481,10 @@ mod tests { } #[test] - fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count() { + fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts( + ) { init_test_logging(); - let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count"; + let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts"; let now = SystemTime::now(); let wallet_1 = make_wallet("def"); let account_1 = PayableAccount { @@ -1488,6 +1493,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }; + // 1. account to be outweighed let wallet_2 = make_wallet("abc"); let account_2 = PayableAccount { wallet: wallet_2.clone(), @@ -1495,8 +1501,9 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), pending_payable_opt: None, }; + // 2. account to be outweighed let wallet_3 = make_wallet("ghk"); - let balance_3 = 600_000_000; + let balance_3 = 600_000_000_000; let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: balance_3, From 4860d41933d434603b947a4b6f21ded170db0894 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 3 Aug 2023 12:12:32 +0200 Subject: [PATCH 056/250] GH-711: digging into problems with failing test; refactoring when on the way --- .../payment_adjuster/criteria_calculators.rs | 4 +- .../payment_adjuster/diagnostics.rs | 20 +- node/src/accountant/payment_adjuster/inner.rs | 27 +- .../accountant/payment_adjuster/log_fns.rs | 2 +- .../bare_functions.rs} | 220 +++++-- .../miscellaneous/data_sructures.rs | 88 +++ .../payment_adjuster/miscellaneous/mod.rs | 4 + node/src/accountant/payment_adjuster/mod.rs | 543 +++++++++--------- 8 files changed, 556 insertions(+), 352 deletions(-) rename node/src/accountant/payment_adjuster/{auxiliary_fns.rs => miscellaneous/bare_functions.rs} (77%) create mode 100644 node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs create mode 100644 node/src/accountant/payment_adjuster/miscellaneous/mod.rs diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs index d4fe7c4ae..66748fdb6 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators.rs @@ -1,11 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::auxiliary_fns::{log_2, x_or_1}; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ compute_progressive_characteristics, DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, BALANCE_DIAGNOSTICS_CONFIG_OPT, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; +use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{log_2, x_or_1}; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; use std::sync::Mutex; @@ -201,7 +201,6 @@ impl CriteriaIteratorAdaptor for I { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::auxiliary_fns::log_2; use crate::accountant::payment_adjuster::criteria_calculators::{ AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, @@ -209,6 +208,7 @@ mod tests { AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, }; + use crate::accountant::payment_adjuster::miscellaneous::bare_functions::log_2; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 27bb4e679..d5799b700 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,7 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; +use std::fmt::Debug; + +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -41,7 +42,7 @@ where } } -pub fn diagnostics_collective(label: &str, accounts: &[PayableAccount]) { +pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { eprintln!("{}", label); accounts @@ -60,7 +61,6 @@ pub mod formulas_progressive_characteristics { use std::time::Duration; use std::time::SystemTime; use thousands::Separable; - pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; //mutex should be fine for debugging, no need for mut static static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); @@ -148,3 +148,15 @@ pub mod formulas_progressive_characteristics { }); } } + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS; + use crate::accountant::payment_adjuster::diagnostics::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; + + #[test] + fn constants_are_correct() { + assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); + assert_eq!(COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, false) + } +} diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 3fd653a52..1beea8596 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -9,10 +9,12 @@ pub trait PaymentAdjusterInner { fn gas_limitation_opt(&self) -> Option { PaymentAdjusterInnerNull::panicking_operation("gas_limitation_opt()") } + fn original_cw_masq_balance(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance()") + } fn unallocated_cw_masq_balance(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance()") } - //TODO this method should use RefCell internally...and we could have PaymentAdjuster with &self instead of &mut self fn lower_unallocated_cw_balance(&mut self, _subtrahend: u128) { PaymentAdjusterInnerNull::panicking_operation("lower_unallocated_cw_balance()") @@ -22,6 +24,7 @@ pub trait PaymentAdjusterInner { pub struct PaymentAdjusterInnerReal { now: SystemTime, gas_limitation_opt: Option, + original_cw_masq_balance: u128, unallocated_cw_masq_balance: u128, } @@ -30,6 +33,7 @@ impl PaymentAdjusterInnerReal { Self { now, gas_limitation_opt, + original_cw_masq_balance: cw_masq_balance, unallocated_cw_masq_balance: cw_masq_balance, } } @@ -42,16 +46,18 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn gas_limitation_opt(&self) -> Option { self.gas_limitation_opt } + fn original_cw_masq_balance(&self) -> u128 { + self.original_cw_masq_balance + } fn unallocated_cw_masq_balance(&self) -> u128 { self.unallocated_cw_masq_balance } - fn lower_unallocated_cw_balance(&mut self, subtrahend: u128) { - let lowered_theoretical_cw_balance = self + let updated_thought_cw_balance = self .unallocated_cw_masq_balance .checked_sub(subtrahend) - .expect("should always subtract a small enough amount"); - self.unallocated_cw_masq_balance = lowered_theoretical_cw_balance + .expect("subtracting a small enough number"); + self.unallocated_cw_masq_balance = updated_thought_cw_balance } } @@ -84,6 +90,7 @@ mod tests { assert_eq!(result.now, now); assert_eq!(result.gas_limitation_opt, gas_limitation_opt); + assert_eq!(result.original_cw_masq_balance, cw_masq_balance); assert_eq!(result.unallocated_cw_masq_balance, cw_masq_balance) } @@ -107,6 +114,16 @@ mod tests { let _ = subject.gas_limitation_opt(); } + #[test] + #[should_panic( + expected = "Called the null implementation of the original_cw_masq_balance() method in PaymentAdjusterInner" + )] + fn inner_null_calling_original_cw_masq_balance() { + let subject = PaymentAdjusterInnerNull {}; + + let _ = subject.original_cw_masq_balance(); + } + #[test] #[should_panic( expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 3e533c935..1bcd2b876 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::DisqualifiedPayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::data_sructures::DisqualifiedPayableAccount; use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; diff --git a/node/src/accountant/payment_adjuster/auxiliary_fns.rs b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs similarity index 77% rename from node/src/accountant/payment_adjuster/auxiliary_fns.rs rename to node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs index d177265a0..9cddbf39c 100644 --- a/node/src/accountant/payment_adjuster/auxiliary_fns.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs @@ -1,13 +1,15 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::diagnostics; -use crate::accountant::payment_adjuster::{ - AdjustedAccountBeforeFinalization, DecidedPayableAccountResolution, +use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ + AdjustedAccountBeforeFinalization, ResolutionAfterFullyDetermined, }; +use crate::accountant::payment_adjuster::{diagnostics, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE}; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use std::iter::successors; +use thousands::Separable; + const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; @@ -97,22 +99,83 @@ impl ExhaustionStatus { fn add(mut self, unfinalized_account_info: AdjustedAccountBeforeFinalization) -> Self { let finalized_account = PayableAccount::from(( unfinalized_account_info, - DecidedPayableAccountResolution::Finalize, + ResolutionAfterFullyDetermined::Finalize, )); self.already_finalized_accounts.push(finalized_account); self } } +pub fn sort_in_descendant_order_by_weights( + unsorted: impl Iterator, +) -> Vec<(u128, PayableAccount)> { + unsorted + .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .collect() +} + +pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { + criteria_and_accounts + .into_iter() + .map(|(_, account)| account) + .collect() +} + pub fn exhaust_cw_balance_totally( verified_accounts: Vec, - unallocated_cw_masq_balance: u128, + original_cw_masq_balance: u128, ) -> Vec { + fn fold_guts( + status: ExhaustionStatus, + unfinalized_account_info: AdjustedAccountBeforeFinalization, + ) -> ExhaustionStatus { + if status.remainder != 0 { + let balance_gap = unfinalized_account_info + .original_account + .balance_wei + .checked_sub(unfinalized_account_info.proposed_adjusted_balance) + .unwrap_or_else(|| { + panic!( + "proposed balance should never bigger than the original but proposed: {} \ + and original: {}", + unfinalized_account_info.proposed_adjusted_balance, + unfinalized_account_info.original_account.balance_wei + ) + }); + let possible_extra_addition = if balance_gap < status.remainder { + balance_gap + } else { + status.remainder + }; + + diagnostics!( + "EXHAUSTING CW ON PAYMENT", + "For account {} from proposed {} to the possible maximum of {}", + unfinalized_account_info.original_account.wallet, + unfinalized_account_info.proposed_adjusted_balance, + unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition + ); + + status.update_and_add(unfinalized_account_info, possible_extra_addition) + } else { + status.add(unfinalized_account_info) + } + } + let adjusted_balances_total: u128 = sum_as(&verified_accounts, |account_info| { account_info.proposed_adjusted_balance }); - let remainder = unallocated_cw_masq_balance - adjusted_balances_total; - let init = ExhaustionStatus::new(remainder); + + let cw_reminder = original_cw_masq_balance + .checked_sub(adjusted_balances_total) + .unwrap_or_else(|| { + panic!( + "remainder should've been a positive number but was not after {} - {}", + original_cw_masq_balance, adjusted_balances_total + ) + }); + + let init = ExhaustionStatus::new(cw_reminder); verified_accounts .into_iter() .sorted_by(|info_a, info_b| { @@ -121,56 +184,38 @@ pub fn exhaust_cw_balance_totally( &info_b.proposed_adjusted_balance, ) }) - .inspect(|account_info|eprintln!("{:?}", account_info)) - .fold(init, |status, unfinalized_account_info| { - if status.remainder != 0 { - let balance_gap = unfinalized_account_info - .original_account - .balance_wei - .checked_sub(unfinalized_account_info.proposed_adjusted_balance) - .unwrap_or_else(||panic!( - "proposed balance should never bigger than the original but proposed: {} \ - and original: {}", - unfinalized_account_info.proposed_adjusted_balance, - unfinalized_account_info.original_account.balance_wei - )); - let possible_extra_addition = if balance_gap < status.remainder { - balance_gap - } else { - status.remainder - }; - - diagnostics!( - "EXHAUSTING CW ON PAYMENT", - "For account {} from proposed {} to the possible maximum of {}", - unfinalized_account_info.original_account.wallet, - unfinalized_account_info.proposed_adjusted_balance, - unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition - ); - - status.update_and_add(unfinalized_account_info, possible_extra_addition) - } else { - status.add(unfinalized_account_info) - } - }) + .inspect(|account_info| eprintln!("{:?}", account_info)) //TODO delete me + .fold(init, fold_guts) .already_finalized_accounts .into_iter() .sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)) .collect() } -pub fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, -) -> Vec<(u128, PayableAccount)> { - unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) - .collect() -} +pub fn list_accounts_under_the_disqualification_limit( + unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], +) -> Vec<&AdjustedAccountBeforeFinalization> { + unfinalized_adjusted_accounts + .iter() + .flat_map(|account_info| { + let original_balance = account_info.original_account.balance_wei; + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + if proposed_adjusted_balance <= balance_at_the_edge { + diagnostics!( + &account_info.original_account.wallet, + "ACCOUNT DISQUALIFIED FOR INSIGNIFICANCE AFTER ADJUSTMENT", + "Proposed: {}, qualification limit: {}", + proposed_adjusted_balance.separate_with_commas(), + balance_at_the_edge.separate_with_commas() + ); -pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { - criteria_and_accounts - .into_iter() - .map(|(_, account)| account) + Some(&*account_info) + } else { + None + } + }) .collect() } @@ -199,18 +244,42 @@ pub fn x_or_1(x: u128) -> u128 { } } +impl + From<( + AdjustedAccountBeforeFinalization, + ResolutionAfterFullyDetermined, + )> for PayableAccount +{ + fn from( + (account_info, resolution): ( + AdjustedAccountBeforeFinalization, + ResolutionAfterFullyDetermined, + ), + ) -> Self { + match resolution { + ResolutionAfterFullyDetermined::Finalize => PayableAccount { + balance_wei: account_info.proposed_adjusted_balance, + ..account_info.original_account + }, + ResolutionAfterFullyDetermined::Revert => account_info.original_account, + } + } +} + #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::auxiliary_fns::{ + use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, - find_disqualified_account_with_smallest_proposed_balance, log_10, log_2, ExhaustionStatus, + find_disqualified_account_with_smallest_proposed_balance, + list_accounts_under_the_disqualification_limit, log_10, log_2, ExhaustionStatus, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; + use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -590,6 +659,51 @@ mod tests { assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances) } + #[test] + fn list_accounts_under_the_disqualification_limit_employs_manifest_consts_of_insignificance() { + let account_balance = 1_000_000; + let prepare_account = |n: u64| { + let mut account = make_payable_account(n); + account.balance_wei = account_balance; + account + }; + let payable_account_1 = prepare_account(1); + let payable_account_2 = prepare_account(2); + let payable_account_3 = prepare_account(3); + const IRRELEVANT_CRITERIA_SUM: u128 = 1111; + let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + let proposed_ok_balance = edge + 1; + let account_info_1 = AdjustedAccountBeforeFinalization::new( + payable_account_1, + proposed_ok_balance, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_equal = edge; + let account_info_2 = AdjustedAccountBeforeFinalization::new( + payable_account_2, + proposed_bad_balance_because_equal, + IRRELEVANT_CRITERIA_SUM, + ); + let proposed_bad_balance_because_smaller = edge - 1; + let account_info_3 = AdjustedAccountBeforeFinalization::new( + payable_account_3, + proposed_bad_balance_because_smaller, + IRRELEVANT_CRITERIA_SUM, + ); + let accounts_with_unchecked_adjustment = vec![ + account_info_1, + account_info_2.clone(), + account_info_3.clone(), + ]; + + let result = + list_accounts_under_the_disqualification_limit(&accounts_with_unchecked_adjustment); + + let expected_disqualified_accounts = vec![&account_info_2, &account_info_3]; + assert_eq!(result, expected_disqualified_accounts) + } + fn get_extreme_criteria_and_initial_accounts_order( months_of_debt_and_balances_matrix: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs new file mode 100644 index 000000000..5e4056f1a --- /dev/null +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs @@ -0,0 +1,88 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::sub_lib::wallet::Wallet; + +#[derive(Debug)] +pub enum AccountsRecreationResult { + AllAccountsCleanlyProcessed(Vec), + InsignificantAccounts { + disqualified: DisqualifiedPayableAccount, + remaining: Vec, + }, + OutweighedAccounts { + outweighed: Vec, + remaining: Vec, + }, +} + +#[derive(Debug)] +pub struct AdjustmentIterationSummary { + pub decided_accounts: Vec, + pub remaining_accounts: Vec, + pub disqualified_account_opt: Option, +} + +pub enum IterationCompletion { + Finished(Vec), + Continue(AdjustmentIterationSummary), +} + +pub enum AccountsFromEntireRecursion { + NeediningToExhaustCW(Vec), + Finalized(Vec), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct AdjustedAccountBeforeFinalization { + pub original_account: PayableAccount, + pub proposed_adjusted_balance: u128, + pub criteria_sum: u128, +} + +impl AdjustedAccountBeforeFinalization { + pub fn new( + original_account: PayableAccount, + proposed_adjusted_balance: u128, + criteria_sum: u128, + ) -> Self { + Self { + original_account, + proposed_adjusted_balance, + criteria_sum, + } + } + + pub fn finalize_collection_of_self( + account_infos: Vec, + resolution: ResolutionAfterFullyDetermined, + ) -> Vec { + account_infos + .into_iter() + .map(|account_info| PayableAccount::from((account_info, resolution))) + .collect() + } +} + +#[derive(Clone, Copy)] +pub enum ResolutionAfterFullyDetermined { + Finalize, + Revert, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct DisqualifiedPayableAccount { + pub wallet: Wallet, + pub proposed_adjusted_balance: u128, + pub original_balance: u128, +} + +impl DisqualifiedPayableAccount { + pub fn new(wallet: Wallet, original_balance: u128, proposed_adjusted_balance: u128) -> Self { + Self { + wallet, + proposed_adjusted_balance, + original_balance, + } + } +} diff --git a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs new file mode 100644 index 000000000..cfa442819 --- /dev/null +++ b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs @@ -0,0 +1,4 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod bare_functions; +pub mod data_sructures; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index f2b053966..2c237b12b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,24 +1,19 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. //keep these modules private -mod auxiliary_fns; mod criteria_calculators; mod diagnostics; mod inner; mod log_fns; +mod miscellaneous; mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::auxiliary_fns::{ - compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, - exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, - rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, -}; use crate::accountant::payment_adjuster::criteria_calculators::{ AgeCriterionCalculator, BalanceCriterionCalculator, CriteriaIteratorAdaptor, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; -use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_collective}; +use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_for_collections}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; @@ -26,6 +21,16 @@ use crate::accountant::payment_adjuster::log_fns::{ before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_account, log_insufficient_transaction_fee_balance, }; +use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ + compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, + exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, + list_accounts_under_the_disqualification_limit, rebuild_accounts, + sort_in_descendant_order_by_weights, sum_as, +}; +use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ + AccountsRecreationResult, AdjustedAccountBeforeFinalization, AdjustmentIterationSummary, + DisqualifiedPayableAccount, IterationCompletion, ResolutionAfterFullyDetermined, +}; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; @@ -130,7 +135,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = self.run_adjustment_procedure(qualified_payables, vec![]); + let adjusted_accounts = self.run_adjustment(qualified_payables); debug!( self.logger, @@ -147,8 +152,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { implement_as_any!(); } -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; - // represents 50% const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = PercentageAccountInsignificance { @@ -260,27 +263,72 @@ impl PaymentAdjusterReal { } } - fn run_adjustment_procedure( + fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { + match self.calculate_criteria_and_propose_adjustment_recursively( + qualified_accounts, + vec![], + MasqAndTransactionFeeAdjuster {}, + ) { + Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( + non_exhausted_accounts, + self.inner.original_cw_masq_balance(), + ), + Either::Right(finalized_accounts) => finalized_accounts, + } + } + + fn calculate_criteria_and_propose_adjustment_recursively( &mut self, unresolved_qualified_accounts: Vec, - previously_resolved_qualified_accounts: Vec, - ) -> Vec { - diagnostics_collective( - "RESOLVED QUALIFIED ACCOUNTS:", + previously_resolved_qualified_accounts: Vec, + purpose_specific_adjuster: A, + ) -> R + where + A: PurposeSpecificAdjuster, + { + diagnostics_for_collections( + "\nRESOLVED QUALIFIED ACCOUNTS:", &previously_resolved_qualified_accounts, ); - diagnostics_collective( + diagnostics_for_collections( "UNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, ); + //TODO find the place where else we need to use this when diving in recursion let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); - self.perform_adjustment_recursively( + + purpose_specific_adjuster.adjust( + self, accounts_with_individual_criteria_sorted, previously_resolved_qualified_accounts, ) } + fn begin_with_adjustment_by_gas( + &mut self, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + count_limit_by_gas: u16, + ) -> Either, Vec> { + let weighted_accounts_cut_by_gas = + cut_back_by_gas_count_limit(accounts_with_individual_criteria, count_limit_by_gas); + match Self::check_need_of_masq_balances_adjustment( + &self.logger, + Either::Right(&weighted_accounts_cut_by_gas), + self.inner.unallocated_cw_masq_balance(), + ) { + true => { + let result_awaiting_verification = + self.propose_adjustment_recursively(weighted_accounts_cut_by_gas, vec![]); + Either::Left(result_awaiting_verification) + } + false => { + let finalized_accounts = rebuild_accounts(weighted_accounts_cut_by_gas); + Either::Right(finalized_accounts) + } + } + } + fn calculate_criteria_sums_for_accounts( &self, accounts: Vec, @@ -290,16 +338,13 @@ impl PaymentAdjusterReal { } //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_adjustment_procedure - fn perform_adjustment_recursively( + fn propose_adjustment_recursively( &mut self, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - previously_resolved_qualified_accounts: Vec, - ) -> Vec { + previously_resolved_qualified_accounts: Vec, + ) -> Vec { let adjustment_result: AdjustmentIterationSummary = - match self.give_job_to_adjustment_workers(accounts_with_individual_criteria_sorted) { - AdjustmentCompletion::Finished(accounts_adjusted) => return accounts_adjusted, - AdjustmentCompletion::Continue(iteration_result) => iteration_result, - }; + self.handle_masq_token_adjustment(accounts_with_individual_criteria_sorted); log_info_for_disqualified_account( &self.logger, @@ -313,18 +358,19 @@ impl PaymentAdjusterReal { // meaning we found some outweighed accounts in the previous iteration self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) } - return self.run_adjustment_procedure( + self.calculate_criteria_and_propose_adjustment_recursively( adjustment_result.remaining_accounts, adjustment_result.decided_accounts, - ); + MasqOnlyAdjuster {}, + ) }; let adjusted_accounts_iter = adjusted_accounts.into_iter(); - let result: Vec = previously_resolved_qualified_accounts + let result: Vec = previously_resolved_qualified_accounts .into_iter() .chain(adjusted_accounts_iter) .collect(); - diagnostics_collective("FINAL ADJUSTED ACCOUNTS:", &result); + diagnostics_for_collections("\nFINAL ADJUSTED ACCOUNTS:", &result); result } @@ -358,40 +404,6 @@ impl PaymentAdjusterReal { collected_accounts_with_criteria } - fn give_job_to_adjustment_workers( - &mut self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> AdjustmentCompletion { - match self.inner.gas_limitation_opt() { - Some(limitation_by_gas) => { - let weighted_accounts_cut_by_gas = cut_back_by_gas_count_limit( - accounts_with_individual_criteria, - limitation_by_gas, - ); - match Self::check_need_of_masq_balances_adjustment( - &self.logger, - Either::Right(&weighted_accounts_cut_by_gas), - self.inner.unallocated_cw_masq_balance(), - ) { - true => { - let result_awaiting_verification = - self.handle_masq_token_adjustment(weighted_accounts_cut_by_gas); - AdjustmentCompletion::Continue(result_awaiting_verification) - } - false => { - let finalized_accounts = rebuild_accounts(weighted_accounts_cut_by_gas); - AdjustmentCompletion::Finished(finalized_accounts) - } - } - } - None => { - let result_awaiting_verification = - self.handle_masq_token_adjustment(accounts_with_individual_criteria); - AdjustmentCompletion::Continue(result_awaiting_verification) - } - } - } - fn handle_masq_token_adjustment( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, @@ -453,11 +465,12 @@ impl PaymentAdjusterReal { Right(with_some_disqualified) => return with_some_disqualified, }; - let unallocated_cw_masq_balance = self.inner.unallocated_cw_masq_balance(); - let verified_and_exhaustive = - exhaust_cw_balance_totally(verified_accounts, unallocated_cw_masq_balance); + //TODO decide what about this + //let unallocated_cw_masq_balance = self.inner.unallocated_cw_masq_balance(); + // let verified_and_exhaustive = + // exhaust_cw_balance_totally(verified_accounts, unallocated_cw_masq_balance); - AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_and_exhaustive) + AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_accounts) } fn compute_unfinalized_adjusted_accounts( @@ -539,15 +552,15 @@ impl PaymentAdjusterReal { ) }; - let remaining_stripped_off = remaining + let remaining_reverted = remaining .into_iter() .map(|account_info| { - PayableAccount::from((account_info, DecidedPayableAccountResolution::Revert)) + PayableAccount::from((account_info, ResolutionAfterFullyDetermined::Revert)) }) .collect(); Right(AccountsRecreationResult::InsignificantAccounts { disqualified: debugable_disqualified, - remaining: remaining_stripped_off, + remaining: remaining_reverted, }) } else { Left(unfinalized_adjusted_accounts) @@ -559,7 +572,7 @@ impl PaymentAdjusterReal { logger: &Logger, ) -> Option { let disqualification_suspected_accounts = - Self::list_accounts_under_the_disqualification_limit(unfinalized_adjusted_accounts); + list_accounts_under_the_disqualification_limit(unfinalized_adjusted_accounts); disqualification_suspected_accounts .is_empty() .not() @@ -585,39 +598,12 @@ impl PaymentAdjusterReal { }) } - fn list_accounts_under_the_disqualification_limit( - unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], - ) -> Vec<&AdjustedAccountBeforeFinalization> { - unfinalized_adjusted_accounts - .iter() - .flat_map(|account_info| { - let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; - let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; - if proposed_adjusted_balance <= balance_at_the_edge { - diagnostics!( - &account_info.original_account.wallet, - "ACCOUNT DISQUALIFIED BASED ON THE PROPOSED BALANCE", - "proposed: {}, qualification limit: {}", - proposed_adjusted_balance, - balance_at_the_edge - ); - - Some(&*account_info) - } else { - None - } - }) - .collect() - } - fn handle_possibly_outweighed_account( &mut self, unfinalized_adjusted_accounts: Vec, ) -> Either, AccountsRecreationResult> { let init: ( - Vec<(u128, PayableAccount)>, + Vec, Vec, ) = (vec![], vec![]); let (outweighed_with_already_made_criteria, passing_through) = @@ -631,14 +617,22 @@ impl PaymentAdjusterReal { diagnostics!( &account_info.original_account.wallet, "OUTWEIGHED ACCOUNT FOUND", - "original balance: {}, proposed balance {}", - account_info.original_account.balance_wei, - account_info.proposed_adjusted_balance + "Original balance: {}, proposed balance: {}", + account_info + .original_account + .balance_wei + .separate_with_commas(), + account_info + .proposed_adjusted_balance + .separate_with_commas() ); - let outweighed_record = - (account_info.criteria_sum, account_info.original_account); - outweighed.push(outweighed_record); + let new_account_info = AdjustedAccountBeforeFinalization { + proposed_adjusted_balance: account_info.original_account.balance_wei, + ..account_info + }; + + outweighed.push(new_account_info); (outweighed, passing_through) } else { passing_through.push(account_info); @@ -654,9 +648,9 @@ impl PaymentAdjusterReal { .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( outweighed_with_already_made_criteria, ); - let remaining = AdjustedAccountBeforeFinalization::finalize_collection( + let remaining = AdjustedAccountBeforeFinalization::finalize_collection_of_self( passing_through, - DecidedPayableAccountResolution::Revert, + ResolutionAfterFullyDetermined::Revert, ); Right(AccountsRecreationResult::OutweighedAccounts { outweighed: clean_outweighed_accounts, @@ -668,27 +662,29 @@ impl PaymentAdjusterReal { //TODO we probably want to drop the criteria before here where we've got no use for them........or maybe not...because the computation has always the same res fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( &mut self, - mut outweighed_with_criteria: Vec<(u128, PayableAccount)>, - ) -> Vec { + mut outweighed_with_criteria: Vec, + ) -> Vec { //TODO is this special condition for the single guy necessary?? let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); if outweighed_with_criteria.len() == 1 { - let (_, only_account) = outweighed_with_criteria.remove(0); - if only_account.balance_wei > cw_masq_balance { - vec![PayableAccount { - balance_wei: cw_masq_balance, - ..only_account + let only_account_info = outweighed_with_criteria.remove(0); + if only_account_info.original_account.balance_wei > cw_masq_balance { + return vec![AdjustedAccountBeforeFinalization { + proposed_adjusted_balance: cw_masq_balance, + ..only_account_info }]; todo!("untested!!!!") } else { - vec![only_account] + return vec![only_account_info]; } - } else if sum_as::(&outweighed_with_criteria, |(_, account)| { - account.balance_wei - }) > cw_masq_balance - { + } + let proposed_outweighed_balances_total = + sum_as::(&outweighed_with_criteria, |account_info| { + account_info.proposed_adjusted_balance + }); + if proposed_outweighed_balances_total > cw_masq_balance { todo!("will we ever be here again?"); - self.perform_adjustment_recursively(outweighed_with_criteria, vec![]) + //self.propose_adjustment_recursively(outweighed_with_criteria, vec![]) // // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { // todo!() @@ -700,8 +696,13 @@ impl PaymentAdjusterReal { } } - fn adjust_cw_balance_down_for_next_round(&mut self, processed_outweighed: &[PayableAccount]) { - let subtrahend_total: u128 = sum_as(processed_outweighed, |account| account.balance_wei); + fn adjust_cw_balance_down_for_next_round( + &mut self, + processed_outweighed: &[AdjustedAccountBeforeFinalization], + ) { + let subtrahend_total: u128 = sum_as(processed_outweighed, |account| { + account.proposed_adjusted_balance + }); self.inner.lower_unallocated_cw_balance(subtrahend_total); diagnostics!( @@ -713,136 +714,91 @@ impl PaymentAdjusterReal { } } -#[derive(Debug)] -enum AccountsRecreationResult { - AllAccountsCleanlyProcessed(Vec), - InsignificantAccounts { - disqualified: DisqualifiedPayableAccount, - remaining: Vec, - }, - OutweighedAccounts { - outweighed: Vec, - remaining: Vec, - }, +#[derive(Debug, PartialEq, Eq)] +pub enum Adjustment { + MasqToken, + PriorityTransactionFee { limited_count_from_gas: u16 }, } -#[derive(Debug)] -struct AdjustmentIterationSummary { - decided_accounts: Vec, - remaining_accounts: Vec, - disqualified_account_opt: Option, +#[derive(Debug, PartialEq, Eq)] +pub enum AnalysisError { + BalanceBelowSingleTxFee { + one_transaction_requirement: u64, + cw_balance: u64, + }, } -//TODO rename??? -#[derive(Clone, Copy)] -enum DecidedPayableAccountResolution { - Finalize, - Revert, -} +trait PurposeSpecificAdjuster { + type ReturnType; -enum AdjustmentCompletion { - Finished(Vec), - Continue(AdjustmentIterationSummary), + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + previously_resolved_qualified_accounts: Vec, + ) -> Self::ReturnType; } -impl - From<( - AdjustedAccountBeforeFinalization, - DecidedPayableAccountResolution, - )> for PayableAccount -{ - fn from( - (account_info, resolution): ( - AdjustedAccountBeforeFinalization, - DecidedPayableAccountResolution, - ), - ) -> Self { - match resolution { - DecidedPayableAccountResolution::Finalize => PayableAccount { - balance_wei: account_info.proposed_adjusted_balance, - ..account_info.original_account - }, - DecidedPayableAccountResolution::Revert => account_info.original_account, - } - } -} +struct MasqAndTransactionFeeAdjuster {} -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct AdjustedAccountBeforeFinalization { - original_account: PayableAccount, - proposed_adjusted_balance: u128, - criteria_sum: u128, -} +impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { + type ReturnType = Either, Vec>; -impl AdjustedAccountBeforeFinalization { - fn new( - original_account: PayableAccount, - proposed_adjusted_balance: u128, - criteria_sum: u128, - ) -> Self { - Self { - original_account, - proposed_adjusted_balance, - criteria_sum, - } - } + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + previously_resolved_qualified_accounts: Vec, + ) -> Self::ReturnType { + match payment_adjuster.inner.gas_limitation_opt() { + Some(limit) => { + return payment_adjuster + .begin_with_adjustment_by_gas(accounts_with_individual_criteria_sorted, limit) + } + None => (), + }; - fn finalize_collection( - account_infos: Vec, - resolution: DecidedPayableAccountResolution, - ) -> Vec { - account_infos - .into_iter() - .map(|account_info| PayableAccount::from((account_info, resolution))) - .collect() + Either::Left(payment_adjuster.propose_adjustment_recursively( + accounts_with_individual_criteria_sorted, + previously_resolved_qualified_accounts, + )) } } -#[derive(Debug, PartialEq, Eq)] -pub struct DisqualifiedPayableAccount { - wallet: Wallet, - proposed_adjusted_balance: u128, - original_balance: u128, -} - -impl DisqualifiedPayableAccount { - fn new(wallet: Wallet, original_balance: u128, proposed_adjusted_balance: u128) -> Self { - Self { - wallet, - proposed_adjusted_balance, - original_balance, - } - } -} +struct MasqOnlyAdjuster {} -#[derive(Debug, PartialEq, Eq)] -pub enum Adjustment { - MasqToken, - PriorityTransactionFee { limited_count_from_gas: u16 }, -} +impl PurposeSpecificAdjuster for MasqOnlyAdjuster { + type ReturnType = Vec; -#[derive(Debug, PartialEq, Eq)] -pub enum AnalysisError { - BalanceBelowSingleTxFee { - one_transaction_requirement: u64, - cw_balance: u64, - }, + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + previously_resolved_qualified_accounts: Vec, + ) -> Self::ReturnType { + payment_adjuster.propose_adjustment_recursively( + accounts_with_individual_criteria_sorted, + previously_resolved_qualified_accounts, + ) + } } #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::auxiliary_fns::{ + use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, }; + use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ + AccountsRecreationResult, AdjustedAccountBeforeFinalization, DisqualifiedPayableAccount, + }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - AccountsRecreationResult, AdjustedAccountBeforeFinalization, Adjustment, AnalysisError, - DisqualifiedPayableAccount, PaymentAdjuster, PaymentAdjusterReal, - PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, + Adjustment, AnalysisError, MasqAndTransactionFeeAdjuster, MasqOnlyAdjuster, + PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, + PurposeSpecificAdjuster, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -865,7 +821,6 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, PercentageAccountInsignificance { @@ -1015,52 +970,6 @@ mod tests { ); } - #[test] - fn list_accounts_under_the_disqualification_limit_employs_manifest_consts_of_insignificance() { - let account_balance = 1_000_000; - let prepare_account = |n: u64| { - let mut account = make_payable_account(n); - account.balance_wei = account_balance; - account - }; - let payable_account_1 = prepare_account(1); - let payable_account_2 = prepare_account(2); - let payable_account_3 = prepare_account(3); - const IRRELEVANT_CRITERIA_SUM: u128 = 1111; - let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; - let proposed_ok_balance = edge + 1; - let account_info_1 = AdjustedAccountBeforeFinalization::new( - payable_account_1, - proposed_ok_balance, - IRRELEVANT_CRITERIA_SUM, - ); - let proposed_bad_balance_because_equal = edge; - let account_info_2 = AdjustedAccountBeforeFinalization::new( - payable_account_2, - proposed_bad_balance_because_equal, - IRRELEVANT_CRITERIA_SUM, - ); - let proposed_bad_balance_because_smaller = edge - 1; - let account_info_3 = AdjustedAccountBeforeFinalization::new( - payable_account_3, - proposed_bad_balance_because_smaller, - IRRELEVANT_CRITERIA_SUM, - ); - let accounts_with_unchecked_adjustment = vec![ - account_info_1, - account_info_2.clone(), - account_info_3.clone(), - ]; - - let result = PaymentAdjusterReal::list_accounts_under_the_disqualification_limit( - &accounts_with_unchecked_adjustment, - ); - - let expected_disqualified_accounts = vec![&account_info_2, &account_info_3]; - assert_eq!(result, expected_disqualified_accounts) - } - #[test] fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { let now = SystemTime::now(); @@ -1146,7 +1055,48 @@ mod tests { } #[test] - fn small_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { + fn masq_only_adjuster_is_not_meant_to_adjust_by_gas() { + let now = SystemTime::now(); + let cw_balance = 10_001_000; + let details = FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(0), + masq_tokens_wei: U256::from(cw_balance), + }, + desired_gas_price_gwei: 30, + estimated_gas_limit_per_transaction: 100, + }; + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 5_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let mut account_2 = account_1.clone(); + account_2.wallet = wallet_2.clone(); + let accounts = vec![account_1, account_2]; + let adjustment = Adjustment::PriorityTransactionFee { + limited_count_from_gas: 1, + }; + let mut payment_adjuster = PaymentAdjusterReal::new(); + payment_adjuster.set_up_new_inner(details, adjustment, now); + let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); + let purpose_specific_adjuster = MasqOnlyAdjuster {}; + + let result = purpose_specific_adjuster.adjust(&mut payment_adjuster, seeds, vec![]); + + let returned_accounts_accounts = result + .into_iter() + .map(|account| account.original_account.wallet) + .collect::>(); + assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) + //if the gas adjustment had been available, only one account would've been returned, the test passes + } + + #[test] + fn smaller_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { const SAFETY_MULTIPLIER: u128 = 1_000_000_000_000_000; let now = SystemTime::now(); let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000; @@ -1167,9 +1117,16 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2.clone()]; + let qualified_payables = vec![account_1.clone(), account_2.clone()]; - let result = subject.run_adjustment_procedure(qualified_payables.clone(), vec![]); + let mut result = subject + .calculate_criteria_and_propose_adjustment_recursively( + qualified_payables.clone(), + vec![], + MasqAndTransactionFeeAdjuster {}, + ) + .left() + .unwrap(); //first a presentation of why this test is important let criteria_and_accounts = @@ -1184,19 +1141,31 @@ mod tests { //consequences are that redistributing the new balances according to the computed weights would've attributed //the second account with more tokens to pay than it'd had before the test started; //to prevent it, we've got a rule that no account can ever demand more than its 100% - assert!(proposed_adjusted_balance_2 > 10 * balance_2, "we expected the proposed balance much bigger than the original which is {} but it was {}", balance_2, proposed_adjusted_balance_2); - assert_eq!( - result, - vec![ - account_2, //outweighed account takes the first place - PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 1_499_950_000_000, - last_paid_timestamp: last_paid_timestamp_1, - pending_payable_opt: None, - }, - ] + assert!( + proposed_adjusted_balance_2 > 10 * balance_2, + "we expected the proposed balance \ + much bigger than the original which is {} but it was {}", + balance_2, + proposed_adjusted_balance_2 + ); + let first_account = result.remove(0); + //outweighed account takes the first place + assert_eq!(first_account.original_account, account_2); + assert_eq!(first_account.proposed_adjusted_balance, balance_2); + let second_account = result.remove(0); + assert_eq!(second_account.original_account, account_1); + let upper_limit = ((1_500_000_000_000_u128 - 25_000_000 - 25_000_000) * 999_999_999_999) + / 1_000_000_000_000; + let lower_limit = (upper_limit * 9) / 10; + assert!( + lower_limit < second_account.proposed_adjusted_balance + && second_account.proposed_adjusted_balance < upper_limit, + "we expected the roughly adjusted account to be between {} and {} but was {}", + lower_limit, + upper_limit, + second_account.proposed_adjusted_balance ); + assert!(result.is_empty()); } #[test] From 6158d9da89c7ed80747be8d6518fe0ce5e402b5b Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 3 Aug 2023 21:53:44 +0200 Subject: [PATCH 057/250] GH-711: interim commit --- .../miscellaneous/bare_functions.rs | 7 ++- node/src/accountant/payment_adjuster/mod.rs | 58 +++++++------------ 2 files changed, 24 insertions(+), 41 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs index 9cddbf39c..e38d5dba4 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs @@ -162,6 +162,7 @@ pub fn exhaust_cw_balance_totally( } } + eprintln!("accounts before exhaustion {:?}", verified_accounts); let adjusted_balances_total: u128 = sum_as(&verified_accounts, |account_info| { account_info.proposed_adjusted_balance }); @@ -184,7 +185,7 @@ pub fn exhaust_cw_balance_totally( &info_b.proposed_adjusted_balance, ) }) - .inspect(|account_info| eprintln!("{:?}", account_info)) //TODO delete me + .inspect(|account_info| eprintln!("\n\n{:?}", account_info)) //TODO delete me .fold(init, fold_guts) .already_finalized_accounts .into_iter() @@ -199,9 +200,9 @@ pub fn list_accounts_under_the_disqualification_limit( .iter() .flat_map(|account_info| { let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance * 10) //TODO what about these 10s? + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; - let proposed_adjusted_balance = account_info.proposed_adjusted_balance * 10; + let proposed_adjusted_balance = account_info.proposed_adjusted_balance; if proposed_adjusted_balance <= balance_at_the_edge { diagnostics!( &account_info.original_account.wallet, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 2c237b12b..bf81cb442 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -266,7 +266,7 @@ impl PaymentAdjusterReal { fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, - vec![], + // vec![], MasqAndTransactionFeeAdjuster {}, ) { Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( @@ -280,18 +280,13 @@ impl PaymentAdjusterReal { fn calculate_criteria_and_propose_adjustment_recursively( &mut self, unresolved_qualified_accounts: Vec, - previously_resolved_qualified_accounts: Vec, purpose_specific_adjuster: A, ) -> R where A: PurposeSpecificAdjuster, { diagnostics_for_collections( - "\nRESOLVED QUALIFIED ACCOUNTS:", - &previously_resolved_qualified_accounts, - ); - diagnostics_for_collections( - "UNRESOLVED QUALIFIED ACCOUNTS:", + "\nUNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, ); //TODO find the place where else we need to use this when diving in recursion @@ -300,8 +295,7 @@ impl PaymentAdjusterReal { purpose_specific_adjuster.adjust( self, - accounts_with_individual_criteria_sorted, - previously_resolved_qualified_accounts, + accounts_with_individual_criteria_sorted ) } @@ -319,7 +313,7 @@ impl PaymentAdjusterReal { ) { true => { let result_awaiting_verification = - self.propose_adjustment_recursively(weighted_accounts_cut_by_gas, vec![]); + self.propose_adjustment_recursively(weighted_accounts_cut_by_gas); Either::Left(result_awaiting_verification) } false => { @@ -341,7 +335,6 @@ impl PaymentAdjusterReal { fn propose_adjustment_recursively( &mut self, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - previously_resolved_qualified_accounts: Vec, ) -> Vec { let adjustment_result: AdjustmentIterationSummary = self.handle_masq_token_adjustment(accounts_with_individual_criteria_sorted); @@ -351,27 +344,26 @@ impl PaymentAdjusterReal { adjustment_result.disqualified_account_opt.as_ref(), ); - let adjusted_accounts = if adjustment_result.remaining_accounts.is_empty() { - adjustment_result.decided_accounts + let (here_decided_accounts, downstream_decided_accounts) = if adjustment_result.remaining_accounts.is_empty() { + (adjustment_result.decided_accounts, vec![]) } else { if adjustment_result.disqualified_account_opt.is_none() { // meaning we found some outweighed accounts in the previous iteration self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) } + (adjustment_result.decided_accounts, self.calculate_criteria_and_propose_adjustment_recursively( adjustment_result.remaining_accounts, - adjustment_result.decided_accounts, MasqOnlyAdjuster {}, - ) + )) }; - - let adjusted_accounts_iter = adjusted_accounts.into_iter(); - let result: Vec = previously_resolved_qualified_accounts - .into_iter() - .chain(adjusted_accounts_iter) + let here_decided_iter = here_decided_accounts.into_iter(); + let downstream_decided_iter = downstream_decided_accounts.into_iter(); + let merged: Vec = here_decided_iter + .chain(downstream_decided_iter) .collect(); - diagnostics_for_collections("\nFINAL ADJUSTED ACCOUNTS:", &result); - result + diagnostics_for_collections("\nFINAL ADJUSTED ACCOUNTS:", &merged); + merged } fn initialize_zero_criteria( @@ -465,11 +457,6 @@ impl PaymentAdjusterReal { Right(with_some_disqualified) => return with_some_disqualified, }; - //TODO decide what about this - //let unallocated_cw_masq_balance = self.inner.unallocated_cw_masq_balance(); - // let verified_and_exhaustive = - // exhaust_cw_balance_totally(verified_accounts, unallocated_cw_masq_balance); - AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_accounts) } @@ -735,7 +722,6 @@ trait PurposeSpecificAdjuster { &self, payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - previously_resolved_qualified_accounts: Vec, ) -> Self::ReturnType; } @@ -748,7 +734,6 @@ impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { &self, payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - previously_resolved_qualified_accounts: Vec, ) -> Self::ReturnType { match payment_adjuster.inner.gas_limitation_opt() { Some(limit) => { @@ -760,7 +745,6 @@ impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { Either::Left(payment_adjuster.propose_adjustment_recursively( accounts_with_individual_criteria_sorted, - previously_resolved_qualified_accounts, )) } } @@ -774,11 +758,9 @@ impl PurposeSpecificAdjuster for MasqOnlyAdjuster { &self, payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - previously_resolved_qualified_accounts: Vec, ) -> Self::ReturnType { payment_adjuster.propose_adjustment_recursively( accounts_with_individual_criteria_sorted, - previously_resolved_qualified_accounts, ) } } @@ -1085,7 +1067,7 @@ mod tests { let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); let purpose_specific_adjuster = MasqOnlyAdjuster {}; - let result = purpose_specific_adjuster.adjust(&mut payment_adjuster, seeds, vec![]); + let result = purpose_specific_adjuster.adjust(&mut payment_adjuster, seeds); let returned_accounts_accounts = result .into_iter() @@ -1122,7 +1104,6 @@ mod tests { let mut result = subject .calculate_criteria_and_propose_adjustment_recursively( qualified_payables.clone(), - vec![], MasqAndTransactionFeeAdjuster {}, ) .left() @@ -1476,7 +1457,7 @@ mod tests { let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; @@ -1509,10 +1490,11 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now); let expected_accounts_first_iteration = simplified_emulation_of_real_adjustment_algorithm( - vec![account_1.clone(), account_2.clone(), account_3], + vec![account_1.clone(), account_2.clone(), account_3.clone()], consuming_wallet_masq_balance_wei.as_u128(), now, ); + eprintln!("expected: {:?}", expected_accounts_first_iteration); let account_3_adjusted_balance = expected_accounts_first_iteration .iter() .find(|account| account.wallet == wallet_3) @@ -1529,8 +1511,8 @@ mod tests { minimum_allowed.separate_with_commas() ); let expected_accounts = simplified_emulation_of_real_adjustment_algorithm( - vec![account_1, account_2], - consuming_wallet_masq_balance_wei.as_u128(), + vec![account_1, account_3], + consuming_wallet_masq_balance_wei.as_u128() - account_2.balance_wei, now, ); assert_eq!(result.accounts, expected_accounts); From 151fdccc577860cf82b617a5c9155a1aeff5c775 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 5 Aug 2023 00:26:10 +0200 Subject: [PATCH 058/250] GH-711: purging; tests passing good --- .../miscellaneous/bare_functions.rs | 3 +- .../miscellaneous/data_sructures.rs | 10 - node/src/accountant/payment_adjuster/mod.rs | 313 ++++++------------ 3 files changed, 108 insertions(+), 218 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs index e38d5dba4..da512f36e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs @@ -200,7 +200,8 @@ pub fn list_accounts_under_the_disqualification_limit( .iter() .flat_map(|account_info| { let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * original_balance) + let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier + * original_balance) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; let proposed_adjusted_balance = account_info.proposed_adjusted_balance; if proposed_adjusted_balance <= balance_at_the_edge { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs index 5e4056f1a..9da9e759f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs @@ -23,16 +23,6 @@ pub struct AdjustmentIterationSummary { pub disqualified_account_opt: Option, } -pub enum IterationCompletion { - Finished(Vec), - Continue(AdjustmentIterationSummary), -} - -pub enum AccountsFromEntireRecursion { - NeediningToExhaustCW(Vec), - Finalized(Vec), -} - #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { pub original_account: PayableAccount, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bf81cb442..7018cdaac 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -29,7 +29,7 @@ use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ }; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ AccountsRecreationResult, AdjustedAccountBeforeFinalization, AdjustmentIterationSummary, - DisqualifiedPayableAccount, IterationCompletion, ResolutionAfterFullyDetermined, + DisqualifiedPayableAccount, ResolutionAfterFullyDetermined, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -266,7 +266,7 @@ impl PaymentAdjusterReal { fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, - // vec![], + // vec![], MasqAndTransactionFeeAdjuster {}, ) { Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( @@ -289,14 +289,11 @@ impl PaymentAdjusterReal { "\nUNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, ); - //TODO find the place where else we need to use this when diving in recursion + let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); - purpose_specific_adjuster.adjust( - self, - accounts_with_individual_criteria_sorted - ) + purpose_specific_adjuster.adjust(self, accounts_with_individual_criteria_sorted) } fn begin_with_adjustment_by_gas( @@ -344,24 +341,26 @@ impl PaymentAdjusterReal { adjustment_result.disqualified_account_opt.as_ref(), ); - let (here_decided_accounts, downstream_decided_accounts) = if adjustment_result.remaining_accounts.is_empty() { - (adjustment_result.decided_accounts, vec![]) - } else { - if adjustment_result.disqualified_account_opt.is_none() { - // meaning we found some outweighed accounts in the previous iteration - self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) - } - (adjustment_result.decided_accounts, - self.calculate_criteria_and_propose_adjustment_recursively( - adjustment_result.remaining_accounts, - MasqOnlyAdjuster {}, - )) - }; + let (here_decided_accounts, downstream_decided_accounts) = + if adjustment_result.remaining_accounts.is_empty() { + (adjustment_result.decided_accounts, vec![]) + } else { + if adjustment_result.disqualified_account_opt.is_none() { + // meaning we found some outweighed accounts in the previous iteration + self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) + } + ( + adjustment_result.decided_accounts, + self.calculate_criteria_and_propose_adjustment_recursively( + adjustment_result.remaining_accounts, + MasqOnlyAdjuster {}, + ), + ) + }; let here_decided_iter = here_decided_accounts.into_iter(); let downstream_decided_iter = downstream_decided_accounts.into_iter(); - let merged: Vec = here_decided_iter - .chain(downstream_decided_iter) - .collect(); + let merged: Vec = + here_decided_iter.chain(downstream_decided_iter).collect(); diagnostics_for_collections("\nFINAL ADJUSTED ACCOUNTS:", &merged); merged } @@ -593,96 +592,55 @@ impl PaymentAdjusterReal { Vec, Vec, ) = (vec![], vec![]); - let (outweighed_with_already_made_criteria, passing_through) = - unfinalized_adjusted_accounts.into_iter().fold( - init, - |(mut outweighed, mut passing_through), account_info| { - if account_info.proposed_adjusted_balance - > account_info.original_account.balance_wei - //TODO test the operator against <= - { - diagnostics!( - &account_info.original_account.wallet, - "OUTWEIGHED ACCOUNT FOUND", - "Original balance: {}, proposed balance: {}", - account_info - .original_account - .balance_wei - .separate_with_commas(), - account_info - .proposed_adjusted_balance - .separate_with_commas() - ); - - let new_account_info = AdjustedAccountBeforeFinalization { - proposed_adjusted_balance: account_info.original_account.balance_wei, - ..account_info - }; - - outweighed.push(new_account_info); - (outweighed, passing_through) - } else { - passing_through.push(account_info); - (outweighed, passing_through) - } - }, - ); - if outweighed_with_already_made_criteria.is_empty() { + let (outweighed, passing_through) = unfinalized_adjusted_accounts.into_iter().fold( + init, + |(mut outweighed, mut passing_through), account_info| { + if account_info.proposed_adjusted_balance + > account_info.original_account.balance_wei + //TODO test the operator against <= + { + diagnostics!( + &account_info.original_account.wallet, + "OUTWEIGHED ACCOUNT FOUND", + "Original balance: {}, proposed balance: {}", + account_info + .original_account + .balance_wei + .separate_with_commas(), + account_info + .proposed_adjusted_balance + .separate_with_commas() + ); + + let new_account_info = AdjustedAccountBeforeFinalization { + proposed_adjusted_balance: account_info.original_account.balance_wei, + ..account_info + }; + + outweighed.push(new_account_info); + (outweighed, passing_through) + } else { + passing_through.push(account_info); + (outweighed, passing_through) + } + }, + ); + + if outweighed.is_empty() { Left(passing_through) } else { - let clean_outweighed_accounts = self - .adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( - outweighed_with_already_made_criteria, - ); let remaining = AdjustedAccountBeforeFinalization::finalize_collection_of_self( passing_through, ResolutionAfterFullyDetermined::Revert, ); Right(AccountsRecreationResult::OutweighedAccounts { - outweighed: clean_outweighed_accounts, + outweighed, remaining, }) } } - //TODO we probably want to drop the criteria before here where we've got no use for them........or maybe not...because the computation has always the same res - fn adjust_balance_of_outweighed_accounts_to_cw_balance_if_necessary( - &mut self, - mut outweighed_with_criteria: Vec, - ) -> Vec { - //TODO is this special condition for the single guy necessary?? - let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); - if outweighed_with_criteria.len() == 1 { - let only_account_info = outweighed_with_criteria.remove(0); - if only_account_info.original_account.balance_wei > cw_masq_balance { - return vec![AdjustedAccountBeforeFinalization { - proposed_adjusted_balance: cw_masq_balance, - ..only_account_info - }]; - todo!("untested!!!!") - } else { - return vec![only_account_info]; - } - } - let proposed_outweighed_balances_total = - sum_as::(&outweighed_with_criteria, |account_info| { - account_info.proposed_adjusted_balance - }); - if proposed_outweighed_balances_total > cw_masq_balance { - todo!("will we ever be here again?"); - //self.propose_adjustment_recursively(outweighed_with_criteria, vec![]) - // - // if !adjustment_result.decided_accounts.is_empty() && adjustment_result.remaining_accounts.is_empty() && adjustment_result.disqualified_accounts.is_empty() { - // todo!() - // } else { - // todo!("{:?}", adjustment_result) - // } - } else { - todo!() - } - } - fn adjust_cw_balance_down_for_next_round( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], @@ -743,9 +701,10 @@ impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { None => (), }; - Either::Left(payment_adjuster.propose_adjustment_recursively( - accounts_with_individual_criteria_sorted, - )) + Either::Left( + payment_adjuster + .propose_adjustment_recursively(accounts_with_individual_criteria_sorted), + ) } } @@ -759,20 +718,16 @@ impl PurposeSpecificAdjuster for MasqOnlyAdjuster { payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { - payment_adjuster.propose_adjustment_recursively( - accounts_with_individual_criteria_sorted, - ) + payment_adjuster.propose_adjustment_recursively(accounts_with_individual_criteria_sorted) } } #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, - }; + use crate::accountant::payment_adjuster::miscellaneous::bare_functions::criteria_total; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ - AccountsRecreationResult, AdjustedAccountBeforeFinalization, DisqualifiedPayableAccount, + AccountsRecreationResult, DisqualifiedPayableAccount, }; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, @@ -1039,7 +994,7 @@ mod tests { #[test] fn masq_only_adjuster_is_not_meant_to_adjust_by_gas() { let now = SystemTime::now(); - let cw_balance = 10_001_000; + let cw_balance = 9_000_000; let details = FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: U256::from(0), @@ -1078,10 +1033,11 @@ mod tests { } #[test] - fn smaller_debt_with_extreme_age_is_paid_outweighed_but_not_with_more_money_than_required() { + fn smaller_debt_with_extreme_age_is_picked_prioritized_as_outweighed_but_not_with_more_money_than_required( + ) { const SAFETY_MULTIPLIER: u128 = 1_000_000_000_000_000; let now = SystemTime::now(); - let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000; + let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); let balance_1 = 1_500_000_000_000; let balance_2 = 25_000_000; @@ -1135,7 +1091,8 @@ mod tests { assert_eq!(first_account.proposed_adjusted_balance, balance_2); let second_account = result.remove(0); assert_eq!(second_account.original_account, account_1); - let upper_limit = ((1_500_000_000_000_u128 - 25_000_000 - 25_000_000) * 999_999_999_999) + let upper_limit = ((1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000) + * 999_999_999_999) / 1_000_000_000_000; let lower_limit = (upper_limit * 9) / 10; assert!( @@ -1150,13 +1107,17 @@ mod tests { } #[test] - fn an_accounts_never_becomes_outweighed_while_cw_balance_does_not_have_enough_balance_for_it_because_disqualified_accounts_come_handled_first( + fn an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first( ) { - //NOTE that the same applies for more than one outweighed accounts that would originally request more than the cw balance, - //therefore there is no such a test either + // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test either. + // This test answers what is happening when the cw MASQ balance cannot cover the outweighed accounts at the first try but if this condition holds it also means + // that there will be another account in the set that will meet the requirements for the disqualified one. + // With enough money, the other attached account doesn't need to be picked for the disqualification which means the concern about outweighed account that + // couldn't have been paid in its full volume turns out groundless. Meaning also the algorithm that knocks the balance in full size down to these accounts + // is not going to lead to just differently formed balance insufficient, found at the time of executing the transactions const SECONDS_IN_3_DAYS: u64 = 259_200; let test_name = - "an_accounts_never_becomes_outweighed_while_cw_balance_does_not_have_enough_balance_for_it_because_disqualified_accounts_come_handled_first"; + "an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first"; let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; let mut subject = make_initialized_subject( @@ -1323,12 +1284,21 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now); - let expected_criteria_computation_output = - simplified_emulation_of_real_adjustment_algorithm( - vec![account_1, account_2, account_3], - consuming_wallet_masq_balance_wei.as_u128(), - now, - ); + let expected_criteria_computation_output = { + let account_1_adjusted = PayableAccount { + balance_wei: 3_895_912_927_516_778_963, + ..account_1 + }; + let account_2_adjusted = PayableAccount { + balance_wei: 5_833_507_422_574_361_619, + ..account_2 + }; + let account_3_adjusted = PayableAccount { + balance_wei: 5_381_690_760_353_303_862, + ..account_3 + }; + vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] + }; assert_eq!( result, OutcomingPaymentsInstructions { @@ -1437,13 +1407,14 @@ mod tests { let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts"; let now = SystemTime::now(); let wallet_1 = make_wallet("def"); + // account to be adjusted up to maximum let account_1 = PayableAccount { wallet: wallet_1.clone(), balance_wei: 333_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }; - // 1. account to be outweighed + // account to be outweighed and fully taken let wallet_2 = make_wallet("abc"); let account_2 = PayableAccount { wallet: wallet_2.clone(), @@ -1451,7 +1422,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), pending_payable_opt: None, }; - // 2. account to be outweighed + // account to be disqualified let wallet_3 = make_wallet("ghk"); let balance_3 = 600_000_000_000; let account_3 = PayableAccount { @@ -1489,32 +1460,13 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now); - let expected_accounts_first_iteration = simplified_emulation_of_real_adjustment_algorithm( - vec![account_1.clone(), account_2.clone(), account_3.clone()], - consuming_wallet_masq_balance_wei.as_u128(), - now, - ); - eprintln!("expected: {:?}", expected_accounts_first_iteration); - let account_3_adjusted_balance = expected_accounts_first_iteration - .iter() - .find(|account| account.wallet == wallet_3) - .unwrap() - .balance_wei; - let minimum_allowed = ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * balance_3 * 10 - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; - assert!( - account_3_adjusted_balance * 10 < minimum_allowed, - "balance for account 3 after adjustment from the first iteration is {} but we need it \ - smaller than {} to exercise what happens if the proposed balance is smaller than half \ - the original one", - account_3_adjusted_balance.separate_with_commas(), - minimum_allowed.separate_with_commas() - ); - let expected_accounts = simplified_emulation_of_real_adjustment_algorithm( - vec![account_1, account_3], - consuming_wallet_masq_balance_wei.as_u128() - account_2.balance_wei, - now, - ); + let expected_accounts = { + let account_1_adjusted = PayableAccount { + balance_wei: 272_000_000_000, + ..account_1 + }; + vec![account_1_adjusted, account_2] + }; assert_eq!(result.accounts, expected_accounts); assert_eq!( result.response_skeleton_opt, @@ -1525,8 +1477,8 @@ mod tests { ); TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Consuming wallet \ low in MASQ balance. Recently qualified payable for wallet 0x00000000000000000000000000000\ - 0000067686b will not be paid as the consuming wallet handles to provide only 56,554,286 wei \ - which is not at least more than a half of the original debt 600,000,000")); + 0000067686b will not be paid as the consuming wallet handles to provide only 73,839,651,271 \ + wei which is not at least more than a half of the original debt 600,000,000")); } struct CompetitiveAccountsTestInputFeeder<'a> { @@ -1949,57 +1901,4 @@ mod tests { response_skeleton_opt: None, } } - - fn simplified_emulation_of_real_adjustment_algorithm( - accounts: Vec, - consuming_wallet_masq_balance_wei: u128, - now: SystemTime, - ) -> Vec { - let subject = make_initialized_subject(now, Some(consuming_wallet_masq_balance_wei), None); - let final_criteria = subject.calculate_criteria_sums_for_accounts(accounts.clone()); - let final_criteria_sum = final_criteria - .iter() - .map(|(criteria_sum, _)| criteria_sum) - .sum::(); - let multiplication_coeff = compute_fraction_preventing_mul_coeff( - consuming_wallet_masq_balance_wei, - final_criteria_sum, - ); - let in_ratio_fragment_of_available_balance = consuming_wallet_masq_balance_wei - .checked_mul(multiplication_coeff) - .unwrap() - .checked_div(final_criteria_sum) - .unwrap(); - let balanced_portions_and_original_accounts = final_criteria - .into_iter() - .map(|(criterion, account)| { - ( - in_ratio_fragment_of_available_balance * criterion / multiplication_coeff, - account, - ) - }) - .collect::>(); - let new_total_amount_to_pay = balanced_portions_and_original_accounts - .iter() - .map(|(proposed_portion, _)| proposed_portion) - .sum::(); - assert!(new_total_amount_to_pay <= consuming_wallet_masq_balance_wei); - assert!( - new_total_amount_to_pay >= (consuming_wallet_masq_balance_wei * 100) / 102, - "new total amount to pay: {}, consuming wallet masq balance: {}", - new_total_amount_to_pay, - consuming_wallet_masq_balance_wei - ); - let accounts_before_exhaustion = balanced_portions_and_original_accounts - .into_iter() - .map(|(proposed_balance, account)| { - //criteria left 0 because here irrelevant - AdjustedAccountBeforeFinalization::new(account, proposed_balance, 0) - }) - .collect(); - exhaust_cw_balance_totally( - accounts_before_exhaustion, - consuming_wallet_masq_balance_wei, - ) - } } From 338b9c92bfc49b404814003d572cf23f5b273390 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 12:08:58 +0200 Subject: [PATCH 059/250] GH-711: illustrative logging improved --- .../payment_adjuster/criteria_calculators.rs | 4 +- .../accountant/payment_adjuster/log_fns.rs | 28 +- .../miscellaneous/data_sructures.rs | 19 +- ...{bare_functions.rs => helper_functions.rs} | 66 ++++- .../payment_adjuster/miscellaneous/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 262 +++++++++--------- 6 files changed, 209 insertions(+), 172 deletions(-) rename node/src/accountant/payment_adjuster/miscellaneous/{bare_functions.rs => helper_functions.rs} (92%) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs index 66748fdb6..049f4d626 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators.rs @@ -5,7 +5,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara compute_progressive_characteristics, DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, BALANCE_DIAGNOSTICS_CONFIG_OPT, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; -use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{log_2, x_or_1}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2, x_or_1}; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; use std::sync::Mutex; @@ -208,7 +208,7 @@ mod tests { AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, }; - use crate::accountant::payment_adjuster::miscellaneous::bare_functions::log_2; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 1bcd2b876..af699b758 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -17,7 +17,7 @@ const REFILL_RECOMMENDATION: &str = "\ In order to continue using services of other Nodes and avoid delinquency \ bans you will need to put more funds into your consuming wallet."; -const NO_CHARS: &str = ""; +const BLANK_SPACE: &str = ""; pub fn format_brief_adjustment_summary( original_account_balances_mapped: HashMap, @@ -39,7 +39,7 @@ pub fn format_brief_adjustment_summary( original_account_balances_mapped .get(&account.wallet) .expectv("initial balance"), - NO_CHARS, + BLANK_SPACE, account.balance_wei, length = WALLET_ADDRESS_LENGTH ) @@ -49,7 +49,7 @@ pub fn format_brief_adjustment_summary( fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { let title = once(format!( "\n{:, adjusted_accounts: &[PayableAccount], @@ -96,28 +98,25 @@ pub fn before_and_after_debug_msg( format!( "\n\ {:, -) { - disqualified_account_opt.map(|account| { - info!( +pub fn log_info_for_disqualified_account(logger: &Logger, account: &DisqualifiedPayableAccount) { + info!( logger, "Consuming wallet low in MASQ balance. Recently qualified \ payable for wallet {} will not be paid as the consuming wallet handles to provide only {} wei \ @@ -126,7 +125,6 @@ pub fn log_info_for_disqualified_account( account.proposed_adjusted_balance.separate_with_commas(), account.original_balance.separate_with_commas() ) - }); } pub fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_masq_balance: u128) { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs index 9da9e759f..754ed315c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs @@ -4,23 +4,18 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::sub_lib::wallet::Wallet; #[derive(Debug)] -pub enum AccountsRecreationResult { - AllAccountsCleanlyProcessed(Vec), - InsignificantAccounts { - disqualified: DisqualifiedPayableAccount, - remaining: Vec, - }, - OutweighedAccounts { - outweighed: Vec, +pub enum AdjustmentIterationResult { + AllAccountsProcessedSmoothly(Vec), + SpecialTreatmentNeeded { + special_case: SpecialTreatment, remaining: Vec, }, } #[derive(Debug)] -pub struct AdjustmentIterationSummary { - pub decided_accounts: Vec, - pub remaining_accounts: Vec, - pub disqualified_account_opt: Option, +pub enum SpecialTreatment { + TreatInsignificantAccount(DisqualifiedPayableAccount), + TreatOutweighedAccounts(Vec), } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs similarity index 92% rename from node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs rename to node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index da512f36e..47aead64c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/bare_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -49,6 +49,43 @@ pub fn compute_fraction_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) } +pub fn possibly_outweighed_accounts_fold_guts( + (mut outweighed, mut passing_through): ( + Vec, + Vec, + ), + account_info: AdjustedAccountBeforeFinalization, +) -> ( + Vec, + Vec, +) { + if account_info.proposed_adjusted_balance > account_info.original_account.balance_wei { + diagnostics!( + &account_info.original_account.wallet, + "OUTWEIGHED ACCOUNT FOUND", + "Original balance: {}, proposed balance: {}", + account_info + .original_account + .balance_wei + .separate_with_commas(), + account_info + .proposed_adjusted_balance + .separate_with_commas() + ); + + let new_account_info = AdjustedAccountBeforeFinalization { + proposed_adjusted_balance: account_info.original_account.balance_wei, + ..account_info + }; + + outweighed.push(new_account_info); + (outweighed, passing_through) + } else { + passing_through.push(account_info); + (outweighed, passing_through) + } +} + pub fn find_disqualified_account_with_smallest_proposed_balance( accounts: &[&AdjustedAccountBeforeFinalization], ) -> Wallet { @@ -271,13 +308,14 @@ impl #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ + use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, - list_accounts_under_the_disqualification_limit, log_10, log_2, ExhaustionStatus, - EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, + list_accounts_under_the_disqualification_limit, log_10, log_2, + possibly_outweighed_accounts_fold_guts, ExhaustionStatus, EMPIRIC_PRECISION_COEFFICIENT, + MAX_EXPONENT_FOR_10_IN_U128, }; - use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; @@ -469,6 +507,26 @@ mod tests { assert_eq!(result, wallet_2) } + #[test] + fn algorithm_is_left_cold_by_accounts_with_original_balance_equal_to_proposed_one() { + let account_info = AdjustedAccountBeforeFinalization { + original_account: PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 9_000_000_000, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }, + proposed_adjusted_balance: 9_000_000_000, + criteria_sum: 123456789, + }; + + let (outweighed, ok) = + possibly_outweighed_accounts_fold_guts((vec![], vec![]), account_info.clone()); + + assert_eq!(outweighed, vec![]); + assert_eq!(ok, vec![account_info]) + } + fn make_unfinalized_adjusted_account( wallet: &Wallet, original_balance: u128, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs index cfa442819..65eec000a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs @@ -1,4 +1,4 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod bare_functions; pub mod data_sructures; +pub mod helper_functions; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 7018cdaac..5487dfbe5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -21,15 +21,18 @@ use crate::accountant::payment_adjuster::log_fns::{ before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_account, log_insufficient_transaction_fee_balance, }; -use crate::accountant::payment_adjuster::miscellaneous::bare_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, - exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, - list_accounts_under_the_disqualification_limit, rebuild_accounts, - sort_in_descendant_order_by_weights, sum_as, +use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::{ + TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ - AccountsRecreationResult, AdjustedAccountBeforeFinalization, AdjustmentIterationSummary, - DisqualifiedPayableAccount, ResolutionAfterFullyDetermined, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DisqualifiedPayableAccount, + ResolutionAfterFullyDetermined, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, + exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, + list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, + rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -266,7 +269,6 @@ impl PaymentAdjusterReal { fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, - // vec![], MasqAndTransactionFeeAdjuster {}, ) { Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( @@ -328,35 +330,16 @@ impl PaymentAdjusterReal { self.apply_criteria(zero_criteria_accounts) } - //TODO if it turns out I don't need this next fn alone it should be merged back with the outer envelope run_adjustment_procedure fn propose_adjustment_recursively( &mut self, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, ) -> Vec { - let adjustment_result: AdjustmentIterationSummary = - self.handle_masq_token_adjustment(accounts_with_individual_criteria_sorted); - - log_info_for_disqualified_account( - &self.logger, - adjustment_result.disqualified_account_opt.as_ref(), - ); + let adjustment_iteration_result = + self.perform_masq_token_adjustment(accounts_with_individual_criteria_sorted); let (here_decided_accounts, downstream_decided_accounts) = - if adjustment_result.remaining_accounts.is_empty() { - (adjustment_result.decided_accounts, vec![]) - } else { - if adjustment_result.disqualified_account_opt.is_none() { - // meaning we found some outweighed accounts in the previous iteration - self.adjust_cw_balance_down_for_next_round(&adjustment_result.decided_accounts) - } - ( - adjustment_result.decided_accounts, - self.calculate_criteria_and_propose_adjustment_recursively( - adjustment_result.remaining_accounts, - MasqOnlyAdjuster {}, - ), - ) - }; + self.resolve_iteration_result(adjustment_iteration_result); + let here_decided_iter = here_decided_accounts.into_iter(); let downstream_decided_iter = downstream_decided_accounts.into_iter(); let merged: Vec = @@ -365,6 +348,57 @@ impl PaymentAdjusterReal { merged } + fn resolve_iteration_result( + &mut self, + adjustment_iteration_result: AdjustmentIterationResult, + ) -> ( + Vec, + Vec, + ) { + match adjustment_iteration_result { + AdjustmentIterationResult::AllAccountsProcessedSmoothly(decided_accounts) => { + (decided_accounts, vec![]) + } + AdjustmentIterationResult::SpecialTreatmentNeeded { + special_case, + remaining, + } => { + let here_decided_accounts = match special_case { + TreatInsignificantAccount(disqualified) => { + log_info_for_disqualified_account(&self.logger, &disqualified); + + if remaining.is_empty() { + // Meaning that the processing reached a niche and the performed combination + // eliminated all accounts even though there was at least one debt at + // the beginning that we could have paid out. + // The preceding check for the need of an adjustment is supposed to prevent vast + // majory of such situations. We wouldn't have proceeded to that adjustment + // at all if we'd had a catch. + // + // we want to avoid another iteration; probably wouldn't be fatal, but it's better + // to be certain about the behavior than letting it go on + todo!() + } + + vec![] + } + TreatOutweighedAccounts(outweighed) => { + self.adjust_cw_balance_down_for_next_round(&outweighed); + outweighed + } + }; + + let down_stream_decided_accounts = self + .calculate_criteria_and_propose_adjustment_recursively( + remaining, + MasqOnlyAdjuster {}, + ); + + (here_decided_accounts, down_stream_decided_accounts) + } + } + } + fn initialize_zero_criteria( qualified_payables: Vec, ) -> impl Iterator { @@ -395,47 +429,22 @@ impl PaymentAdjusterReal { collected_accounts_with_criteria } - fn handle_masq_token_adjustment( + fn perform_masq_token_adjustment( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - ) -> AdjustmentIterationSummary { + ) -> AdjustmentIterationResult { let criteria_total = criteria_total(&accounts_with_individual_criteria); - //TODO simplify this...one of these enums is probably redundant - match self.recreate_accounts_with_proportioned_balances( + self.recreate_accounts_with_proportioned_balances( accounts_with_individual_criteria, criteria_total, - ) { - AccountsRecreationResult::AllAccountsCleanlyProcessed(decided_accounts) => { - AdjustmentIterationSummary { - decided_accounts, - remaining_accounts: vec![], - disqualified_account_opt: None, - } - } - AccountsRecreationResult::InsignificantAccounts { - disqualified, - remaining, - } => AdjustmentIterationSummary { - decided_accounts: vec![], - remaining_accounts: remaining, - disqualified_account_opt: Some(disqualified), - }, - AccountsRecreationResult::OutweighedAccounts { - outweighed, - remaining, - } => AdjustmentIterationSummary { - decided_accounts: outweighed, - remaining_accounts: remaining, - disqualified_account_opt: None, - }, - } + ) } fn recreate_accounts_with_proportioned_balances( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, - ) -> AccountsRecreationResult { + ) -> AdjustmentIterationResult { let unfinalized_adjusted_accounts = self.compute_unfinalized_adjusted_accounts( accounts_with_individual_criteria, criteria_total, @@ -456,7 +465,7 @@ impl PaymentAdjusterReal { Right(with_some_disqualified) => return with_some_disqualified, }; - AccountsRecreationResult::AllAccountsCleanlyProcessed(verified_accounts) + AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) } fn compute_unfinalized_adjusted_accounts( @@ -467,10 +476,10 @@ impl PaymentAdjusterReal { let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); let multiplication_coeff = compute_fraction_preventing_mul_coeff(cw_masq_balance, criteria_total); - let cw_masq_balance_big_u256 = U256::from(cw_masq_balance); + let cw_masq_balance_u256 = U256::from(cw_masq_balance); let criteria_total_u256 = U256::from(criteria_total); let multiplication_coeff_u256 = U256::from(multiplication_coeff); - let proportional_piece_of_cw_balance = cw_masq_balance_big_u256 + let proportional_piece_of_cw_balance = cw_masq_balance_u256 .checked_mul(multiplication_coeff_u256) .unwrap() //TODO try killing this unwrap() in a test // TODO how to give the criteria some kind of ceiling? We don't want to exceed certain dangerous limit @@ -504,7 +513,7 @@ impl PaymentAdjusterReal { fn consider_account_disqualification_from_percentage_insignificance( unfinalized_adjusted_accounts: Vec, logger: &Logger, - ) -> Either, AccountsRecreationResult> { + ) -> Either, AdjustmentIterationResult> { if let Some(disq_account_wallet) = Self::maybe_find_an_account_to_disqualify_in_this_iteration( &unfinalized_adjusted_accounts, @@ -516,17 +525,24 @@ impl PaymentAdjusterReal { Vec::with_capacity(unfinalized_adjusted_accounts.len() - 1), ); - let (single_disqualified, remaining) = unfinalized_adjusted_accounts.into_iter().fold( - init, - |(disqualified_acc_opt, mut remaining_accounts), current_account| { - if current_account.original_account.wallet == disq_account_wallet { - (Some(current_account), remaining_accounts) - } else { - remaining_accounts.push(current_account); - (disqualified_acc_opt, remaining_accounts) - } - }, + type FoldAccumulator = ( + Option, + Vec, ); + let fold_guts = |(disqualified_acc_opt, mut remaining_accounts): FoldAccumulator, + current_account: AdjustedAccountBeforeFinalization| + -> FoldAccumulator { + if current_account.original_account.wallet == disq_account_wallet { + (Some(current_account), remaining_accounts) + } else { + remaining_accounts.push(current_account); + (disqualified_acc_opt, remaining_accounts) + } + }; + + let (single_disqualified, remaining) = unfinalized_adjusted_accounts + .into_iter() + .fold(init, fold_guts); let debugable_disqualified = { let account_info = @@ -544,8 +560,8 @@ impl PaymentAdjusterReal { PayableAccount::from((account_info, ResolutionAfterFullyDetermined::Revert)) }) .collect(); - Right(AccountsRecreationResult::InsignificantAccounts { - disqualified: debugable_disqualified, + Right(AdjustmentIterationResult::SpecialTreatmentNeeded { + special_case: TreatInsignificantAccount(debugable_disqualified), remaining: remaining_reverted, }) } else { @@ -587,45 +603,10 @@ impl PaymentAdjusterReal { fn handle_possibly_outweighed_account( &mut self, unfinalized_adjusted_accounts: Vec, - ) -> Either, AccountsRecreationResult> { - let init: ( - Vec, - Vec, - ) = (vec![], vec![]); - - let (outweighed, passing_through) = unfinalized_adjusted_accounts.into_iter().fold( - init, - |(mut outweighed, mut passing_through), account_info| { - if account_info.proposed_adjusted_balance - > account_info.original_account.balance_wei - //TODO test the operator against <= - { - diagnostics!( - &account_info.original_account.wallet, - "OUTWEIGHED ACCOUNT FOUND", - "Original balance: {}, proposed balance: {}", - account_info - .original_account - .balance_wei - .separate_with_commas(), - account_info - .proposed_adjusted_balance - .separate_with_commas() - ); - - let new_account_info = AdjustedAccountBeforeFinalization { - proposed_adjusted_balance: account_info.original_account.balance_wei, - ..account_info - }; - - outweighed.push(new_account_info); - (outweighed, passing_through) - } else { - passing_through.push(account_info); - (outweighed, passing_through) - } - }, - ); + ) -> Either, AdjustmentIterationResult> { + let (outweighed, passing_through) = unfinalized_adjusted_accounts + .into_iter() + .fold((vec![], vec![]), possibly_outweighed_accounts_fold_guts); if outweighed.is_empty() { Left(passing_through) @@ -634,8 +615,8 @@ impl PaymentAdjusterReal { passing_through, ResolutionAfterFullyDetermined::Revert, ); - Right(AccountsRecreationResult::OutweighedAccounts { - outweighed, + Right(AdjustmentIterationResult::SpecialTreatmentNeeded { + special_case: TreatOutweighedAccounts(outweighed), remaining, }) } @@ -725,10 +706,11 @@ impl PurposeSpecificAdjuster for MasqOnlyAdjuster { #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::bare_functions::criteria_total; + use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ - AccountsRecreationResult, DisqualifiedPayableAccount, + AdjustmentIterationResult, DisqualifiedPayableAccount, }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; use crate::accountant::payment_adjuster::test_utils::{ get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; @@ -1152,8 +1134,8 @@ mod tests { ); let (disqualified, remaining) = match result { - AccountsRecreationResult::InsignificantAccounts { - disqualified, + AdjustmentIterationResult::SpecialTreatmentNeeded { + special_case: TreatInsignificantAccount(disqualified), remaining, } => (disqualified, remaining), x => panic!("we expected to see a disqualified account but got: {:?}", x), @@ -1168,10 +1150,10 @@ mod tests { } #[test] - fn trying_to_go_through_the_complete_process_under_the_extremest_debt_conditions_without_getting_killed( + fn loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations( ) { init_test_logging(); - let test_name = "trying_to_go_through_the_complete_process_under_the_extremest_debt_conditions_without_getting_killed"; + let test_name = "loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations"; let now = SystemTime::now(); //each of 3 accounts contains half of the full supply and a 10-years-old debt which generates extremely big numbers in the criteria let qualified_payables = { @@ -1308,9 +1290,9 @@ mod tests { ); let log_msg = format!( "DEBUG: {test_name}: \n\ -|Account wallet Balance wei -| -|Adjusted payables Original +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original | Adjusted | |0x0000000000000000000000000000000000646566 6666666666000000000 @@ -1383,9 +1365,9 @@ mod tests { ); let log_msg = format!( "DEBUG: {test_name}: \n\ -|Account wallet Balance wei -| -|Adjusted payables Original +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original | Adjusted | |0x0000000000000000000000000000000000646566 333000000000000 @@ -1393,7 +1375,7 @@ mod tests { |0x000000000000000000000000000000000067686b 222000000000000 | 222000000000000 | -|Ignored minor payables Original +|Ruled Out in Favor of the Others Original | |0x0000000000000000000000000000000000616263 111000000000000" ); @@ -1581,8 +1563,8 @@ mod tests { }; // scenario A let first_scenario_name = merge_test_name_and_study_description(test_name, "when equal"); - // first we disqualify the smallest, but also last at that balance, account which is account 2, - // therefore only account 1 remains and soon after qualifies + // first we disqualify the smallest, but also last of that balance, account which is account 2 here, + // therefore only account 1 remains and wins let expected_winning_account = &wallet_1; test_competitive_accounts( @@ -1830,23 +1812,27 @@ mod tests { assert_eq!(result.response_skeleton_opt, None); let log_msg = format!( "DEBUG: {test_name}: \n\ -|Account wallet Balance wei -| -|Adjusted payables Original +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original | Adjusted | |0x000000000000000000000000000000000067686b 333000000000000 | 300000000000000 | -|Ignored minor payables Original +|Ruled Out in Favor of the Others Original | |0x0000000000000000000000000000000000616263 10000000000000 |0x0000000000000000000000000000000000646566 55000000000" ); - //TODO we shouldn't call them "minor payables"...they sometimes are huge but disqualified TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } + #[test] + fn testing_reliability_by_long_loading_payment_adjuster_with_randomly_generated_accounts() { + todo!("write this occasional test") + } + struct GasTestConditions { desired_gas_price_gwei: u64, number_of_payments: usize, From fe401d6be3326d170e1514fa799abf8246226d2a Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 12:27:42 +0200 Subject: [PATCH 060/250] GH-11: small TODO fix --- .../miscellaneous/helper_functions.rs | 78 +++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 69 ++++++++-------- 2 files changed, 76 insertions(+), 71 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 47aead64c..c7cac67a5 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -118,13 +118,13 @@ impl ExhaustionStatus { fn update_and_add( mut self, - mut unfinalized_account_info: AdjustedAccountBeforeFinalization, + mut non_finalized_account_info: AdjustedAccountBeforeFinalization, possible_extra_addition: u128, ) -> Self { let corrected_adjusted_account_before_finalization = { - unfinalized_account_info.proposed_adjusted_balance = - unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition; - unfinalized_account_info + non_finalized_account_info.proposed_adjusted_balance = + non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition; + non_finalized_account_info }; self.remainder = self .remainder @@ -133,9 +133,9 @@ impl ExhaustionStatus { self.add(corrected_adjusted_account_before_finalization) } - fn add(mut self, unfinalized_account_info: AdjustedAccountBeforeFinalization) -> Self { + fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { let finalized_account = PayableAccount::from(( - unfinalized_account_info, + non_finalized_account_info, ResolutionAfterFullyDetermined::Finalize, )); self.already_finalized_accounts.push(finalized_account); @@ -164,19 +164,19 @@ pub fn exhaust_cw_balance_totally( ) -> Vec { fn fold_guts( status: ExhaustionStatus, - unfinalized_account_info: AdjustedAccountBeforeFinalization, + non_finalized_account_info: AdjustedAccountBeforeFinalization, ) -> ExhaustionStatus { if status.remainder != 0 { - let balance_gap = unfinalized_account_info + let balance_gap = non_finalized_account_info .original_account .balance_wei - .checked_sub(unfinalized_account_info.proposed_adjusted_balance) + .checked_sub(non_finalized_account_info.proposed_adjusted_balance) .unwrap_or_else(|| { panic!( "proposed balance should never bigger than the original but proposed: {} \ and original: {}", - unfinalized_account_info.proposed_adjusted_balance, - unfinalized_account_info.original_account.balance_wei + non_finalized_account_info.proposed_adjusted_balance, + non_finalized_account_info.original_account.balance_wei ) }); let possible_extra_addition = if balance_gap < status.remainder { @@ -188,14 +188,14 @@ pub fn exhaust_cw_balance_totally( diagnostics!( "EXHAUSTING CW ON PAYMENT", "For account {} from proposed {} to the possible maximum of {}", - unfinalized_account_info.original_account.wallet, - unfinalized_account_info.proposed_adjusted_balance, - unfinalized_account_info.proposed_adjusted_balance + possible_extra_addition + non_finalized_account_info.original_account.wallet, + non_finalized_account_info.proposed_adjusted_balance, + non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition ); - status.update_and_add(unfinalized_account_info, possible_extra_addition) + status.update_and_add(non_finalized_account_info, possible_extra_addition) } else { - status.add(unfinalized_account_info) + status.add(non_finalized_account_info) } } @@ -231,9 +231,9 @@ pub fn exhaust_cw_balance_totally( } pub fn list_accounts_under_the_disqualification_limit( - unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], + non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], ) -> Vec<&AdjustedAccountBeforeFinalization> { - unfinalized_adjusted_accounts + non_finalized_adjusted_accounts .iter() .flat_map(|account_info| { let original_balance = account_info.original_account.balance_wei; @@ -399,12 +399,12 @@ mod tests { #[test] fn multiplication_coeff_showing_extreme_feeding_and_safety_ceiling() { - // the coeff is how many multiples of 10 we need to use to multiply the cw balance - // in order to get at a number bigger than the criteria sum (and the bigger the more - // accurate numbers calculated afterwards, so we add some for better precision) + // the coeff is multiples of 10 we need to multiply the cw balance with + // in order to get at a number bigger than the total criteria sum (and the more extra 10s we add, the more + // accurate numbers we can expect at the results of the entire payment adjuster machinery) // - // we cannot say by heart what the criteria sum of a debt of these parameters evaluates to, - // so we also can hardly put them in an order + // we cannot say by heart which of the criteria sums of these parameters evaluates to is for sure bigger than another, + // therefore we could hardly put them into an order let accounts_as_months_and_balances = vec![ (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), (5, 10_u128.pow(18)), @@ -527,7 +527,7 @@ mod tests { assert_eq!(ok, vec![account_info]) } - fn make_unfinalized_adjusted_account( + fn make_non_finalized_adjusted_account( wallet: &Wallet, original_balance: u128, proposed_adjusted_balance: u128, @@ -593,18 +593,18 @@ mod tests { + original_requested_balance_2 + original_requested_balance_3 + 5000; - let unfinalized_adjusted_accounts = vec![ - make_unfinalized_adjusted_account( + let non_finalized_adjusted_accounts = vec![ + make_non_finalized_adjusted_account( &wallet_1, original_requested_balance_1, proposed_adjusted_balance_1, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_2, original_requested_balance_2, proposed_adjusted_balance_2, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_3, original_requested_balance_3, proposed_adjusted_balance_3, @@ -612,7 +612,7 @@ mod tests { ]; let result = - exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); + exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); let expected_resulted_balances = vec![ (wallet_1, original_requested_balance_1), @@ -638,18 +638,18 @@ mod tests { + original_requested_balance_3 + proposed_adjusted_balance_1 - 2_000_000; - let unfinalized_adjusted_accounts = vec![ - make_unfinalized_adjusted_account( + let non_finalized_adjusted_accounts = vec![ + make_non_finalized_adjusted_account( &wallet_1, original_requested_balance_1, proposed_adjusted_balance_1, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_2, original_requested_balance_2, proposed_adjusted_balance_2, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_3, original_requested_balance_3, proposed_adjusted_balance_3, @@ -657,7 +657,7 @@ mod tests { ]; let result = - exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); + exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); eprintln!("{:?}", result); let expected_resulted_balances = vec![ @@ -690,18 +690,18 @@ mod tests { + original_requested_balance_3 + proposed_adjusted_balance_1 + 2_000_000; - let unfinalized_adjusted_accounts = vec![ - make_unfinalized_adjusted_account( + let non_finalized_adjusted_accounts = vec![ + make_non_finalized_adjusted_account( &wallet_1, original_requested_balance_1, proposed_adjusted_balance_1, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_2, original_requested_balance_2, proposed_adjusted_balance_2, ), - make_unfinalized_adjusted_account( + make_non_finalized_adjusted_account( &wallet_3, original_requested_balance_3, proposed_adjusted_balance_3, @@ -709,7 +709,7 @@ mod tests { ]; let result = - exhaust_cw_balance_totally(unfinalized_adjusted_accounts, unallocated_cw_balance); + exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); let expected_resulted_balances = vec![ (wallet_1, 53_900_000_000), diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 5487dfbe5..d3551a711 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -445,13 +445,13 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AdjustmentIterationResult { - let unfinalized_adjusted_accounts = self.compute_unfinalized_adjusted_accounts( + let non_finalized_adjusted_accounts = self.compute_non_finalized_adjusted_accounts( accounts_with_individual_criteria, criteria_total, ); let unchecked_for_disqualified = - match self.handle_possibly_outweighed_account(unfinalized_adjusted_accounts) { + match self.handle_possibly_outweighed_account(non_finalized_adjusted_accounts) { Left(still_not_fully_checked) => still_not_fully_checked, Right(with_some_outweighed) => return with_some_outweighed, }; @@ -468,7 +468,7 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) } - fn compute_unfinalized_adjusted_accounts( + fn compute_non_finalized_adjusted_accounts( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, @@ -479,18 +479,22 @@ impl PaymentAdjusterReal { let cw_masq_balance_u256 = U256::from(cw_masq_balance); let criteria_total_u256 = U256::from(criteria_total); let multiplication_coeff_u256 = U256::from(multiplication_coeff); - let proportional_piece_of_cw_balance = cw_masq_balance_u256 + + let proportional_fragment_of_cw_balance = cw_masq_balance_u256 .checked_mul(multiplication_coeff_u256) - .unwrap() //TODO try killing this unwrap() in a test - // TODO how to give the criteria some kind of ceiling? We don't want to exceed certain dangerous limit + .unwrap_or_else(|| { + panic!( + "mul overflow from {} * {}", + criteria_total_u256, multiplication_coeff_u256 + ) + }) .checked_div(criteria_total_u256) .expect("div overflow"); - accounts_with_individual_criteria - .into_iter() - .map(|(criteria_sum, account)| { + let turn_account_into_adjusted_account_before_finalization = + |(criteria_sum, account): (u128, PayableAccount)| { let proposed_adjusted_balance = (U256::from(criteria_sum) - * proportional_piece_of_cw_balance + * proportional_fragment_of_cw_balance / multiplication_coeff_u256) .as_u128(); @@ -506,23 +510,27 @@ impl PaymentAdjusterReal { proposed_adjusted_balance, criteria_sum, ) - }) + }; + + accounts_with_individual_criteria + .into_iter() + .map(turn_account_into_adjusted_account_before_finalization) .collect() } fn consider_account_disqualification_from_percentage_insignificance( - unfinalized_adjusted_accounts: Vec, + non_finalized_adjusted_accounts: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { if let Some(disq_account_wallet) = Self::maybe_find_an_account_to_disqualify_in_this_iteration( - &unfinalized_adjusted_accounts, + &non_finalized_adjusted_accounts, logger, ) { let init = ( None, - Vec::with_capacity(unfinalized_adjusted_accounts.len() - 1), + Vec::with_capacity(non_finalized_adjusted_accounts.len() - 1), ); type FoldAccumulator = ( @@ -540,7 +548,7 @@ impl PaymentAdjusterReal { } }; - let (single_disqualified, remaining) = unfinalized_adjusted_accounts + let (single_disqualified, remaining) = non_finalized_adjusted_accounts .into_iter() .fold(init, fold_guts); @@ -565,16 +573,16 @@ impl PaymentAdjusterReal { remaining: remaining_reverted, }) } else { - Left(unfinalized_adjusted_accounts) + Left(non_finalized_adjusted_accounts) } } fn maybe_find_an_account_to_disqualify_in_this_iteration( - unfinalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], + non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], logger: &Logger, ) -> Option { let disqualification_suspected_accounts = - list_accounts_under_the_disqualification_limit(unfinalized_adjusted_accounts); + list_accounts_under_the_disqualification_limit(non_finalized_adjusted_accounts); disqualification_suspected_accounts .is_empty() .not() @@ -602,9 +610,9 @@ impl PaymentAdjusterReal { fn handle_possibly_outweighed_account( &mut self, - unfinalized_adjusted_accounts: Vec, + non_finalized_adjusted_accounts: Vec, ) -> Either, AdjustmentIterationResult> { - let (outweighed, passing_through) = unfinalized_adjusted_accounts + let (outweighed, passing_through) = non_finalized_adjusted_accounts .into_iter() .fold((vec![], vec![]), possibly_outweighed_accounts_fold_guts); @@ -960,13 +968,13 @@ mod tests { let accounts_with_individual_criteria = subject .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); let criteria_total = criteria_total(&accounts_with_individual_criteria); - let unfinalized_adjusted_accounts = subject.compute_unfinalized_adjusted_accounts( + let non_finalized_adjusted_accounts = subject.compute_non_finalized_adjusted_accounts( accounts_with_individual_criteria, criteria_total, ); let result = PaymentAdjusterReal::maybe_find_an_account_to_disqualify_in_this_iteration( - &unfinalized_adjusted_accounts, + &non_finalized_adjusted_accounts, &logger, ); @@ -1053,9 +1061,9 @@ mod tests { let criteria_total = criteria_total(&criteria_and_accounts); let account_2_criterion = criteria_and_accounts[1].0; let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; - let proportional_piece_of_cw_balance = cw_balance_fractional_safe / criteria_total; + let proportional_fragment_of_cw_balance = cw_balance_fractional_safe / criteria_total; let proposed_adjusted_balance_2 = - (account_2_criterion * proportional_piece_of_cw_balance) / SAFETY_MULTIPLIER; + (account_2_criterion * proportional_fragment_of_cw_balance) / SAFETY_MULTIPLIER; //the weight of the second account grew very progressively due to the effect of the long age; //consequences are that redistributing the new balances according to the computed weights would've attributed //the second account with more tokens to pay than it'd had before the test started; @@ -1155,16 +1163,13 @@ mod tests { init_test_logging(); let test_name = "loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations"; let now = SystemTime::now(); - //each of 3 accounts contains half of the full supply and a 10-years-old debt which generates extremely big numbers in the criteria + //each of 3 accounts contains the full token supply and a 10-years-old debt which generates extremely big numbers in the criteria let qualified_payables = { - let mut accounts = get_extreme_accounts( - Either::Left((vec![120, 120, 120], *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), + let debt_age_in_months = vec![120, 120, 120]; + get_extreme_accounts( + Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), now, - ); - accounts - .iter_mut() - .for_each(|account| account.balance_wei = account.balance_wei / 2); - accounts + ) }; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); From bf10736cb19abfb82dede281379a37c6f3a6d4e0 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 13:02:01 +0200 Subject: [PATCH 061/250] GH-711: improving mutability restrictions --- node/src/accountant/payment_adjuster/inner.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 18 +++++++++--------- node/src/accountant/scanners/mod.rs | 11 ++++------- .../accountant/scanners/scan_mid_procedures.rs | 2 +- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 1beea8596..3c8e7c573 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -15,7 +15,7 @@ pub trait PaymentAdjusterInner { fn unallocated_cw_masq_balance(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance()") } - //TODO this method should use RefCell internally...and we could have PaymentAdjuster with &self instead of &mut self + fn lower_unallocated_cw_balance(&mut self, _subtrahend: u128) { PaymentAdjusterInnerNull::panicking_operation("lower_unallocated_cw_balance()") } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d3551a711..e4670159e 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -129,7 +129,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let required_adjustment = setup.adjustment; - self.set_up_new_inner(current_stage_data, required_adjustment, now); + self.initialize_inner(current_stage_data, required_adjustment, now); let debug_info_opt = self.logger.debug_enabled().then(|| { qualified_payables @@ -186,7 +186,7 @@ impl PaymentAdjusterReal { } } - fn set_up_new_inner( + fn initialize_inner( &mut self, setup: FinancialAndTechDetails, required_adjustment: Adjustment, @@ -430,7 +430,7 @@ impl PaymentAdjusterReal { } fn perform_masq_token_adjustment( - &mut self, + &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { let criteria_total = criteria_total(&accounts_with_individual_criteria); @@ -441,7 +441,7 @@ impl PaymentAdjusterReal { } fn recreate_accounts_with_proportioned_balances( - &mut self, + &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AdjustmentIterationResult { @@ -469,7 +469,7 @@ impl PaymentAdjusterReal { } fn compute_non_finalized_adjusted_accounts( - &mut self, + &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> Vec { @@ -609,7 +609,7 @@ impl PaymentAdjusterReal { } fn handle_possibly_outweighed_account( - &mut self, + &self, non_finalized_adjusted_accounts: Vec, ) -> Either, AdjustmentIterationResult> { let (outweighed, passing_through) = non_finalized_adjusted_accounts @@ -936,7 +936,7 @@ mod tests { let now = SystemTime::now(); let cw_masq_balance = 1_000_000_000_000_000_000; let logger = Logger::new(test_name); - let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); + let subject = make_initialized_subject(now, Some(cw_masq_balance), None); let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { wallet: wallet_1.clone(), @@ -1008,7 +1008,7 @@ mod tests { limited_count_from_gas: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.set_up_new_inner(details, adjustment, now); + payment_adjuster.initialize_inner(details, adjustment, now); let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); let purpose_specific_adjuster = MasqOnlyAdjuster {}; @@ -1110,7 +1110,7 @@ mod tests { "an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first"; let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; - let mut subject = make_initialized_subject( + let subject = make_initialized_subject( now, Some(consuming_wallet_balance), Some(Logger::new(test_name)), diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index a30f1ae09..b5fbd2033 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -180,7 +180,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_threshold_gauge: Box, - pub payment_adjuster: RefCell>, + pub payment_adjuster: Box, } impl Scanner for PayableScanner { @@ -261,7 +261,6 @@ impl PayableScannerMiddleProcedures for PayableScanner { ) -> Result, String> { match self .payment_adjuster - .borrow() .search_for_indispensable_adjustment(&msg) { Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { @@ -274,13 +273,11 @@ impl PayableScannerMiddleProcedures for PayableScanner { } fn exacting_payments_instructions( - &self, + &mut self, setup: AwaitedAdjustment, ) -> OutcomingPaymentsInstructions { let now = SystemTime::now(); - self.payment_adjuster - .borrow_mut() - .adjust_payments(setup, now) + self.payment_adjuster.adjust_payments(setup, now) } } @@ -298,7 +295,7 @@ impl PayableScanner { payable_dao, pending_payable_dao, payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), - payment_adjuster: RefCell::new(payment_adjuster), + payment_adjuster, } } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index c1e36403f..e374a2707 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -23,7 +23,7 @@ pub trait PayableScannerMiddleProcedures { intentionally_blank!() } fn exacting_payments_instructions( - &self, + &mut self, _setup: AwaitedAdjustment, ) -> OutcomingPaymentsInstructions { intentionally_blank!() From a7dbd203d00c3977b8363fca2b263e91f06dd574 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 14:12:20 +0200 Subject: [PATCH 062/250] GH-711: commented out tests have been taken care of --- .../payment_adjuster/diagnostics.rs | 74 ++++++ .../miscellaneous/helper_functions.rs | 37 ++- node/src/accountant/payment_adjuster/mod.rs | 223 +++++++----------- 3 files changed, 171 insertions(+), 163 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index d5799b700..49ccd4233 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -149,6 +149,80 @@ pub mod formulas_progressive_characteristics { } } +pub mod separately_defined_diagnostic_functions { + use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::diagnostics; + use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; + use crate::sub_lib::wallet::Wallet; + use thousands::Separable; + + pub fn possibly_outweighed_accounts_diagnostics( + account_info: &AdjustedAccountBeforeFinalization, + ) { + diagnostics!( + &account_info.original_account.wallet, + "OUTWEIGHED ACCOUNT FOUND", + "Original balance: {}, proposed balance: {}", + account_info + .original_account + .balance_wei + .separate_with_commas(), + account_info + .proposed_adjusted_balance + .separate_with_commas() + ); + } + + pub fn exhausting_cw_balance_diagnostics( + non_finalized_account_info: &AdjustedAccountBeforeFinalization, + possible_extra_addition: u128, + ) { + diagnostics!( + "EXHAUSTING CW ON PAYMENT", + "For account {} from proposed {} to the possible maximum of {}", + non_finalized_account_info.original_account.wallet, + non_finalized_account_info.proposed_adjusted_balance, + non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition + ); + } + + pub fn not_exhausting_cw_balance_diagnostics( + non_finalized_account_info: &AdjustedAccountBeforeFinalization, + ) { + diagnostics!( + "FULLY EXHAUSTED CW, PASSING ACCOUNT OVER", + "Account {} with original balance {} must be finalized with proposed {}", + non_finalized_account_info.original_account.wallet, + non_finalized_account_info.original_account.balance_wei, + non_finalized_account_info.proposed_adjusted_balance + ); + } + + pub fn non_finalized_adjusted_accounts_diagnostics( + account: &PayableAccount, + proposed_adjusted_balance: u128, + ) { + diagnostics!( + &account.wallet, + "PROPOSED ADJUSTED BALANCE", + "{}", + proposed_adjusted_balance.separate_with_commas() + ); + } + + pub fn maybe_find_account_to_disqualify_diagnostics( + disqualification_suspected_accounts: &[&AdjustedAccountBeforeFinalization], + wallet: &Wallet, + ) { + diagnostics!( + "PICKED DISQUALIFIED ACCOUNT", + "From {:?} picked {}", + disqualification_suspected_accounts, + wallet + ); + } +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index c7cac67a5..7904a2567 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -1,6 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ + exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, + possibly_outweighed_accounts_diagnostics, +}; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ AdjustedAccountBeforeFinalization, ResolutionAfterFullyDetermined, }; @@ -31,6 +35,12 @@ pub fn cut_back_by_gas_count_limit( weights_and_accounts: Vec<(u128, PayableAccount)>, limit: u16, ) -> Vec<(u128, PayableAccount)> { + diagnostics!( + "ACCOUNTS CUTBACK FOR TRANSACTION FEE", + "keeping {} out of {} accounts", + limit, + weights_and_accounts.len() + ); weights_and_accounts .into_iter() .take(limit as usize) @@ -60,18 +70,7 @@ pub fn possibly_outweighed_accounts_fold_guts( Vec, ) { if account_info.proposed_adjusted_balance > account_info.original_account.balance_wei { - diagnostics!( - &account_info.original_account.wallet, - "OUTWEIGHED ACCOUNT FOUND", - "Original balance: {}, proposed balance: {}", - account_info - .original_account - .balance_wei - .separate_with_commas(), - account_info - .proposed_adjusted_balance - .separate_with_commas() - ); + possibly_outweighed_accounts_diagnostics(&account_info); let new_account_info = AdjustedAccountBeforeFinalization { proposed_adjusted_balance: account_info.original_account.balance_wei, @@ -129,7 +128,7 @@ impl ExhaustionStatus { self.remainder = self .remainder .checked_sub(possible_extra_addition) - .unwrap_or(0); //TODO wait for overflow + .unwrap_or(0); //TODO wait for overflow!!!!!!!!!!!!!!!! self.add(corrected_adjusted_account_before_finalization) } @@ -185,21 +184,16 @@ pub fn exhaust_cw_balance_totally( status.remainder }; - diagnostics!( - "EXHAUSTING CW ON PAYMENT", - "For account {} from proposed {} to the possible maximum of {}", - non_finalized_account_info.original_account.wallet, - non_finalized_account_info.proposed_adjusted_balance, - non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition - ); + exhausting_cw_balance_diagnostics(&non_finalized_account_info, possible_extra_addition); status.update_and_add(non_finalized_account_info, possible_extra_addition) } else { + not_exhausting_cw_balance_diagnostics(&non_finalized_account_info); + status.add(non_finalized_account_info) } } - eprintln!("accounts before exhaustion {:?}", verified_accounts); let adjusted_balances_total: u128 = sum_as(&verified_accounts, |account_info| { account_info.proposed_adjusted_balance }); @@ -222,7 +216,6 @@ pub fn exhaust_cw_balance_totally( &info_b.proposed_adjusted_balance, ) }) - .inspect(|account_info| eprintln!("\n\n{:?}", account_info)) //TODO delete me .fold(init, fold_guts) .already_finalized_accounts .into_iter() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e4670159e..81895247c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -13,6 +13,9 @@ use crate::accountant::payment_adjuster::criteria_calculators::{ AgeCriterionCalculator, BalanceCriterionCalculator, CriteriaIteratorAdaptor, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ + maybe_find_account_to_disqualify_diagnostics, non_finalized_adjusted_accounts_diagnostics, +}; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_for_collections}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -52,7 +55,6 @@ use std::collections::HashMap; use std::iter::once; use std::ops::Not; use std::time::SystemTime; -use thousands::Separable; use web3::types::U256; pub trait PaymentAdjuster { @@ -498,12 +500,7 @@ impl PaymentAdjusterReal { / multiplication_coeff_u256) .as_u128(); - diagnostics!( - &account.wallet, - "PROPOSED ADJUSTED BALANCE", - "{}", - proposed_adjusted_balance.separate_with_commas() - ); + non_finalized_adjusted_accounts_diagnostics(&account, proposed_adjusted_balance); AdjustedAccountBeforeFinalization::new( account, @@ -590,12 +587,12 @@ impl PaymentAdjusterReal { let wallet = find_disqualified_account_with_smallest_proposed_balance( &disqualification_suspected_accounts, ); - diagnostics!( - "PICKED DISQUALIFIED ACCOUNT", - "From {:?} picked {}", - disqualification_suspected_accounts, - wallet + + maybe_find_account_to_disqualify_diagnostics( + &disqualification_suspected_accounts, + &wallet, ); + trace!( logger, "Found accounts {:?} whose proposed new, adjusted balances laid under \ @@ -1387,6 +1384,79 @@ mod tests { TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } + #[test] + fn both_balances_are_insufficient_but_adjustment_by_masq_cuts_down_no_accounts_it_just_adjusts_their_balances( + ) { + init_test_logging(); + let test_name = "both_balances_are_insufficient_but_adjustment_by_masq_cuts_down_no_accounts_it_just_adjusts_their_balances"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; + let response_skeleton_opt = Some(ResponseSkeleton { + client_id: 123, + context_id: 321, + }); //just hardening, not so important + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + }, + estimated_gas_limit_per_transaction: 77_000, + desired_gas_price_gwei: 24, + }, + )), + response_skeleton_opt, + }; + let adjustment_setup = AwaitedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::PriorityTransactionFee { + limited_count_from_gas: 2, + }, + }; + + let result = subject.adjust_payments(adjustment_setup, now); + + // account_1, being the least important one, was eliminated as there wouldn't be enough + // the transaction fee balance for all of them + let expected_accounts = { + let account_2_adjusted = PayableAccount { + balance_wei: 222_000_000_000_000, + ..account_2 + }; + vec![account_2_adjusted, account_3] + }; + assert_eq!( + result, + OutcomingPaymentsInstructions { + accounts: expected_accounts, + response_skeleton_opt + } + ); + } + #[test] fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts( ) { @@ -1619,135 +1689,6 @@ mod tests { ) } - //TODO do I really want to delete this test? Why? I probably don't - // #[test] - // fn adjust_payments_when_both_parameters_must_be_treated_but_masq_doesnt_cut_down_any_account_it_just_adjusts_the_balances( - // ) { - // init_test_logging(); - // let test_name = "adjust_payments_when_gas_limits_the_final_transaction_count"; - // let now = SystemTime::now(); - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 111_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghk"), - // balance_wei: 222_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - // let subject = PaymentAdjusterReal::new(); - // let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; - // let setup_msg = PayablePaymentSetup { - // qualified_payables, - // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), - // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // masq_tokens_wei: U256::from(consuming_wallet_masq_balance), - // }, - // estimated_gas_limit_per_transaction: 77_000, - // desired_gas_price_gwei: 24, - // }, - // )), - // response_skeleton_opt: None, - // }; - // let adjustment_setup = AwaitedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::Both { - // limited_count_from_gas: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - // - // let expected_accounts = emulation_of_the_actual_adjustment_algorithm( - // account_2, - // account_3, - // None, - // consuming_wallet_masq_balance, - // now, - // ); - // assert_eq!( - // result, - // OutcomingPaymentsInstructions { - // accounts: expected_accounts, - // response_skeleton_opt: None - // } - // ); - // } - - //TODO do I really want to delete this test? Why? - // #[test] - // fn adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut( - // ) { - // init_test_logging(); - // let test_name = "adjust_payments_when_both_parameters_are_supposed_to_be_treated_but_masq_will_do_after_the_gas_cut"; - // let now = SystemTime::now(); - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 111_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghk"), - // balance_wei: 222_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - // let subject = PaymentAdjusterReal::new(); - // let consuming_wallet_masq_balance = 333_000_000_000_000_u128 + 222_000_000_000_000 + 1; - // let setup_msg = PayablePaymentSetup { - // qualified_payables, - // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), - // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // masq_tokens_wei: U256::from(consuming_wallet_masq_balance), - // }, - // estimated_gas_limit_per_transaction: 77_000, - // desired_gas_price_gwei: 24, - // }, - // )), - // response_skeleton_opt: None, - // }; - // let adjustment_setup = AwaitedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::Both { - // limited_count_from_gas: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now, &Logger::new(test_name)); - // - // assert_eq!( - // result, - // OutcomingPaymentsInstructions { - // accounts: vec![account_2, account_3], - // response_skeleton_opt: None - // } - // ); - // } - #[test] fn adjust_payments_when_masq_as_well_as_gas_will_limit_the_count() { init_test_logging(); From 117480464771d225e163bf287d39e101e4417f4d Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 14:39:09 +0200 Subject: [PATCH 063/250] GH-711: not so well going attempt to get away with 'gas' for 'transaction fee' --- node/src/accountant/payment_adjuster/inner.rs | 34 ++-- .../miscellaneous/helper_functions.rs | 7 +- node/src/accountant/payment_adjuster/mod.rs | 155 +++++++++--------- .../accountant/payment_adjuster/test_utils.rs | 17 +- 4 files changed, 114 insertions(+), 99 deletions(-) diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 3c8e7c573..1c9727867 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -6,8 +6,8 @@ pub trait PaymentAdjusterInner { fn now(&self) -> SystemTime { PaymentAdjusterInnerNull::panicking_operation("now()") } - fn gas_limitation_opt(&self) -> Option { - PaymentAdjusterInnerNull::panicking_operation("gas_limitation_opt()") + fn transaction_fee_count_limit_opt(&self) -> Option { + PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") } fn original_cw_masq_balance(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance()") @@ -23,16 +23,20 @@ pub trait PaymentAdjusterInner { pub struct PaymentAdjusterInnerReal { now: SystemTime, - gas_limitation_opt: Option, + transaction_fee_count_limit_opt: Option, original_cw_masq_balance: u128, unallocated_cw_masq_balance: u128, } impl PaymentAdjusterInnerReal { - pub fn new(now: SystemTime, gas_limitation_opt: Option, cw_masq_balance: u128) -> Self { + pub fn new( + now: SystemTime, + transaction_fee_count_limit_opt: Option, + cw_masq_balance: u128, + ) -> Self { Self { now, - gas_limitation_opt, + transaction_fee_count_limit_opt, original_cw_masq_balance: cw_masq_balance, unallocated_cw_masq_balance: cw_masq_balance, } @@ -43,8 +47,8 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn now(&self) -> SystemTime { self.now } - fn gas_limitation_opt(&self) -> Option { - self.gas_limitation_opt + fn transaction_fee_count_limit_opt(&self) -> Option { + self.transaction_fee_count_limit_opt } fn original_cw_masq_balance(&self) -> u128 { self.original_cw_masq_balance @@ -84,12 +88,16 @@ mod tests { #[test] fn inner_real_is_constructed_correctly() { let now = SystemTime::now(); - let gas_limitation_opt = Some(3); + let transaction_fee_count_limit_opt = Some(3); let cw_masq_balance = 123_456_789; - let result = PaymentAdjusterInnerReal::new(now, gas_limitation_opt, cw_masq_balance); + let result = + PaymentAdjusterInnerReal::new(now, transaction_fee_count_limit_opt, cw_masq_balance); assert_eq!(result.now, now); - assert_eq!(result.gas_limitation_opt, gas_limitation_opt); + assert_eq!( + result.transaction_fee_count_limit_opt, + transaction_fee_count_limit_opt + ); assert_eq!(result.original_cw_masq_balance, cw_masq_balance); assert_eq!(result.unallocated_cw_masq_balance, cw_masq_balance) } @@ -106,12 +114,12 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the gas_limitation_opt() method in PaymentAdjusterInner" + expected = "Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" )] - fn inner_null_calling_gas_limitation_opt() { + fn inner_null_calling_transaction_fee_count_limit_opt() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.gas_limitation_opt(); + let _ = subject.transaction_fee_count_limit_opt(); } #[test] diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 7904a2567..55a76b6be 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -31,7 +31,7 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn cut_back_by_gas_count_limit( +pub fn cut_back_by_excessive_transaction_fee( weights_and_accounts: Vec<(u128, PayableAccount)>, limit: u16, ) -> Vec<(u128, PayableAccount)> { @@ -310,7 +310,7 @@ mod tests { MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ - get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; use crate::accountant::test_utils::make_payable_account; @@ -761,7 +761,8 @@ mod tests { months_of_debt_and_balances_matrix: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { let now = SystemTime::now(); - let accounts = get_extreme_accounts(Either::Right(months_of_debt_and_balances_matrix), now); + let accounts = + make_extreme_accounts(Either::Right(months_of_debt_and_balances_matrix), now); let wallets_in_order = accounts .iter() .map(|account| account.wallet.clone()) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 81895247c..cdb341aec 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -32,7 +32,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_gas_count_limit, + compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, @@ -91,15 +91,15 @@ impl PaymentAdjuster for PaymentAdjusterReal { StageData::FinancialAndTechDetails(details) => details, }; - match Self::determine_transactions_count_limit_by_gas( + match Self::determine_transactions_count_limit_by_transaction_fee( &this_stage_data, qualified_payables.len(), &self.logger, ) { Ok(None) => (), - Ok(Some(limited_count_from_gas)) => { + Ok(Some(affordable_transaction_count)) => { return Ok(Some(Adjustment::PriorityTransactionFee { - limited_count_from_gas, + affordable_transaction_count, })) } Err(e) => return Err(e), @@ -194,27 +194,28 @@ impl PaymentAdjusterReal { required_adjustment: Adjustment, now: SystemTime, ) { - let gas_limitation_opt = match required_adjustment { + let transaction_fee_limitation_opt = match required_adjustment { Adjustment::PriorityTransactionFee { - limited_count_from_gas, - } => Some(limited_count_from_gas), + affordable_transaction_count, + } => Some(affordable_transaction_count), Adjustment::MasqToken => None, }; let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_wei.as_u128(); - let inner = PaymentAdjusterInnerReal::new(now, gas_limitation_opt, cw_masq_balance); + let inner = + PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); self.inner = Box::new(inner); } - fn determine_transactions_count_limit_by_gas( + fn determine_transactions_count_limit_by_transaction_fee( tech_info: &FinancialAndTechDetails, required_transactions_count: usize, logger: &Logger, ) -> Result, AnalysisError> { let transaction_fee_required_per_transaction_in_major = u128::try_from(tech_info.estimated_gas_limit_per_transaction) - .expectv("small number for gas limit") + .expectv("small number for transaction fee limit") * u128::try_from(tech_info.desired_gas_price_gwei) - .expectv("small number for gas price"); + .expectv("small number for transaction fee price"); let tfrpt_in_minor: U256 = gwei_to_wei(transaction_fee_required_per_transaction_in_major); let available_balance_in_minor = tech_info.consuming_wallet_balances.gas_currency_wei; let limiting_max_possible_count = (available_balance_in_minor / tfrpt_in_minor).as_u128(); @@ -300,25 +301,28 @@ impl PaymentAdjusterReal { purpose_specific_adjuster.adjust(self, accounts_with_individual_criteria_sorted) } - fn begin_with_adjustment_by_gas( + fn begin_with_adjustment_by_transaction_fees( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - count_limit_by_gas: u16, + already_known_count_limit: u16, ) -> Either, Vec> { - let weighted_accounts_cut_by_gas = - cut_back_by_gas_count_limit(accounts_with_individual_criteria, count_limit_by_gas); + let transaction_fee_affordable_weighted_accounts = cut_back_by_excessive_transaction_fee( + accounts_with_individual_criteria, + already_known_count_limit, + ); match Self::check_need_of_masq_balances_adjustment( &self.logger, - Either::Right(&weighted_accounts_cut_by_gas), + Either::Right(&transaction_fee_affordable_weighted_accounts), self.inner.unallocated_cw_masq_balance(), ) { true => { - let result_awaiting_verification = - self.propose_adjustment_recursively(weighted_accounts_cut_by_gas); + let result_awaiting_verification = self + .propose_adjustment_recursively(transaction_fee_affordable_weighted_accounts); Either::Left(result_awaiting_verification) } false => { - let finalized_accounts = rebuild_accounts(weighted_accounts_cut_by_gas); + let finalized_accounts = + rebuild_accounts(transaction_fee_affordable_weighted_accounts); Either::Right(finalized_accounts) } } @@ -648,7 +652,7 @@ impl PaymentAdjusterReal { #[derive(Debug, PartialEq, Eq)] pub enum Adjustment { MasqToken, - PriorityTransactionFee { limited_count_from_gas: u16 }, + PriorityTransactionFee { affordable_transaction_count: u16 }, } #[derive(Debug, PartialEq, Eq)] @@ -679,10 +683,12 @@ impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { - match payment_adjuster.inner.gas_limitation_opt() { + match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { - return payment_adjuster - .begin_with_adjustment_by_gas(accounts_with_individual_criteria_sorted, limit) + return payment_adjuster.begin_with_adjustment_by_transaction_fees( + accounts_with_individual_criteria_sorted, + limit, + ) } None => (), }; @@ -717,7 +723,7 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; use crate::accountant::payment_adjuster::test_utils::{ - get_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ Adjustment, AnalysisError, MasqAndTransactionFeeAdjuster, MasqOnlyAdjuster, @@ -777,24 +783,24 @@ mod tests { //masq balance = payments let msg_2 = make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 15], 100)), None); - //gas balance > payments + //transaction_fee balance > payments let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(GasTestConditions { - desired_gas_price_gwei: 111, + Some(TransactionFeeTestConditions { + desired_transaction_fee_price_per_major: 111, number_of_payments: 5, - estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_gas_gwei: (111 * 5 * 53_000) + 1, + estimated_fee_limit_per_transaction: 53_000, + consuming_wallet_transaction_fee_major: (111 * 5 * 53_000) + 1, }), ); - //gas balance = payments + //transaction_fee balance = payments let msg_4 = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(GasTestConditions { - desired_gas_price_gwei: 100, + Some(TransactionFeeTestConditions { + desired_transaction_fee_price_per_major: 100, number_of_payments: 6, - estimated_gas_limit_per_transaction: 53_000, - consuming_wallet_gas_gwei: 100 * 6 * 53_000, + estimated_fee_limit_per_transaction: 53_000, + consuming_wallet_transaction_fee_major: 100 * 6 * 53_000, }), ); @@ -832,20 +838,20 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_positive_for_gas() { + fn search_for_indispensable_adjustment_positive_for_transaction_fee() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_gas"; + let test_name = "search_for_indispensable_adjustment_positive_for_transaction_fee"; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let number_of_payments = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(GasTestConditions { - desired_gas_price_gwei: 100, + Some(TransactionFeeTestConditions { + desired_transaction_fee_price_per_major: 100, number_of_payments, - estimated_gas_limit_per_transaction: 55_000, - consuming_wallet_gas_gwei: 100 * 3 * 55_000 - 1, + estimated_fee_limit_per_transaction: 55_000, + consuming_wallet_transaction_fee_major: 100 * 3 * 55_000 - 1, }), ); @@ -855,7 +861,7 @@ mod tests { assert_eq!( result, Ok(Some(Adjustment::PriorityTransactionFee { - limited_count_from_gas: expected_limiting_count + affordable_transaction_count: expected_limiting_count })) ); let log_handler = TestLogHandler::new(); @@ -869,17 +875,17 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_gas( + fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_transaction_fee( ) { let subject = PaymentAdjusterReal::new(); let number_of_payments = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(GasTestConditions { - desired_gas_price_gwei: 100, + Some(TransactionFeeTestConditions { + desired_transaction_fee_price_per_major: 100, number_of_payments, - estimated_gas_limit_per_transaction: 55_000, - consuming_wallet_gas_gwei: 54_000 * 100, + estimated_fee_limit_per_transaction: 55_000, + consuming_wallet_transaction_fee_major: 54_000 * 100, }), ); @@ -979,7 +985,7 @@ mod tests { } #[test] - fn masq_only_adjuster_is_not_meant_to_adjust_by_gas() { + fn masq_only_adjuster_is_not_meant_to_adjust_also_by_transaction_fee() { let now = SystemTime::now(); let cw_balance = 9_000_000; let details = FinancialAndTechDetails { @@ -1002,7 +1008,7 @@ mod tests { account_2.wallet = wallet_2.clone(); let accounts = vec![account_1, account_2]; let adjustment = Adjustment::PriorityTransactionFee { - limited_count_from_gas: 1, + affordable_transaction_count: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); payment_adjuster.initialize_inner(details, adjustment, now); @@ -1016,7 +1022,7 @@ mod tests { .map(|account| account.original_account.wallet) .collect::>(); assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) - //if the gas adjustment had been available, only one account would've been returned, the test passes + //if the transaction_fee adjustment had been available, only one account would've been returned, the test passes } #[test] @@ -1163,7 +1169,7 @@ mod tests { //each of 3 accounts contains the full token supply and a 10-years-old debt which generates extremely big numbers in the criteria let qualified_payables = { let debt_age_in_months = vec![120, 120, 120]; - get_extreme_accounts( + make_extreme_accounts( Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), now, ) @@ -1263,7 +1269,7 @@ mod tests { }; let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual gas balance limitations + adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations }; let result = subject.adjust_payments(adjustment_setup, now); @@ -1308,10 +1314,10 @@ mod tests { } #[test] - fn adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut( + fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_masq_will_do_after_the_transaction_fee_cut( ) { init_test_logging(); - let test_name = "adjust_payments_when_only_gas_limits_the_final_transaction_count_and_masq_will_do_after_the_gas_cut"; + let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_masq_will_do_after_the_transaction_fee_cut"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1352,7 +1358,7 @@ mod tests { let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { - limited_count_from_gas: 2, + affordable_transaction_count: 2, }, }; @@ -1388,7 +1394,6 @@ mod tests { fn both_balances_are_insufficient_but_adjustment_by_masq_cuts_down_no_accounts_it_just_adjusts_their_balances( ) { init_test_logging(); - let test_name = "both_balances_are_insufficient_but_adjustment_by_masq_cuts_down_no_accounts_it_just_adjusts_their_balances"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1433,7 +1438,7 @@ mod tests { let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { - limited_count_from_gas: 2, + affordable_transaction_count: 2, }, }; @@ -1690,9 +1695,9 @@ mod tests { } #[test] - fn adjust_payments_when_masq_as_well_as_gas_will_limit_the_count() { + fn adjust_payments_when_masq_as_well_as_transaction_fee_will_limit_the_count() { init_test_logging(); - let test_name = "adjust_payments_when_masq_as_well_as_gas_will_limit_the_count"; + let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_will_limit_the_count"; let now = SystemTime::now(); //thrown away as the second one because of its insignificance (proposed adjusted balance is smaller than half the original) let account_1 = PayableAccount { @@ -1701,7 +1706,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - //thrown away as the first one because of gas + //thrown away as the first one for insufficient transaction_fee let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 55_000_000_000, @@ -1738,7 +1743,7 @@ mod tests { let adjustment_setup = AwaitedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { - limited_count_from_gas: 2, + affordable_transaction_count: 2, }, }; @@ -1779,31 +1784,31 @@ mod tests { todo!("write this occasional test") } - struct GasTestConditions { - desired_gas_price_gwei: u64, + struct TransactionFeeTestConditions { + desired_transaction_fee_price_per_major: u64, number_of_payments: usize, - estimated_gas_limit_per_transaction: u64, - consuming_wallet_gas_gwei: u64, + estimated_fee_limit_per_transaction: u64, + consuming_wallet_transaction_fee_major: u64, } fn make_payable_setup_msg_coming_from_blockchain_bridge( q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, - gas_price_opt: Option, + transaction_fee_price_opt: Option, ) -> PayablePaymentSetup { let (qualified_payables_gwei, consuming_wallet_masq_gwei) = q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); let ( - desired_gas_price, + desired_transaction_fee_price, number_of_payments, - estimated_gas_limit_per_tx, - cw_balance_gas_gwei, - ) = match gas_price_opt { + estimated_transaction_fee_limit_per_tx, + cw_balance_transaction_fee_major, + ) = match transaction_fee_price_opt { Some(conditions) => ( - conditions.desired_gas_price_gwei, + conditions.desired_transaction_fee_price_per_major, conditions.number_of_payments, - conditions.estimated_gas_limit_per_transaction, - conditions.consuming_wallet_gas_gwei, + conditions.estimated_fee_limit_per_transaction, + conditions.consuming_wallet_transaction_fee_major, ), None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), }; @@ -1823,11 +1828,11 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: gwei_to_wei(cw_balance_gas_gwei), + gas_currency_wei: gwei_to_wei(cw_balance_transaction_fee_major), masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), }, - estimated_gas_limit_per_transaction: estimated_gas_limit_per_tx, - desired_gas_price_gwei: desired_gas_price, + estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, + desired_gas_price_gwei: desired_transaction_fee_price, }, )), response_skeleton_opt: None, diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 50cd04bc7..666179c3e 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -33,25 +33,26 @@ pub fn make_initialized_subject( } } -pub fn get_extreme_accounts( - months_of_debt_matrix_and_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, +pub fn make_extreme_accounts( + months_of_debt_vs_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, now: SystemTime, ) -> Vec { - let seed: Vec<(usize, u128)> = match months_of_debt_matrix_and_balance_setup { - Either::Left((vec, const_balance)) => vec + let accounts_seed: Vec<(usize, u128)> = match months_of_debt_vs_balance_setup { + Either::Left((vec, constant_balance)) => vec .into_iter() - .map(|months| (months, const_balance)) + .map(|months| (months, constant_balance)) .collect(), Either::Right(vec_of_pairs) => vec_of_pairs, }; - seed.into_iter() + accounts_seed + .into_iter() .enumerate() - .map(|(idx, (number_of_months, balance_wei))| PayableAccount { + .map(|(idx, (months_count, balance_wei))| PayableAccount { wallet: make_wallet(&format!("blah{}", idx)), balance_wei, last_paid_timestamp: now .checked_sub(Duration::from_secs( - number_of_months as u64 * (*ONE_MONTH_LONG_DEBT_SEC), + months_count as u64 * (*ONE_MONTH_LONG_DEBT_SEC), )) .unwrap(), pending_payable_opt: None, From 9206241c3cbf57bea3775d5a60bd75f67439cf54 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 16:04:45 +0200 Subject: [PATCH 064/250] GH-711: calculators decently refactored --- .../payment_adjuster/criteria_calculators.rs | 329 ------------------ .../age_criterion_calculator.rs | 172 +++++++++ .../balance_criterion_calculator.rs | 115 ++++++ .../criteria_calculators/mod.rs | 94 +++++ .../payment_adjuster/diagnostics.rs | 16 +- .../miscellaneous/helper_functions.rs | 110 +++--- node/src/accountant/payment_adjuster/mod.rs | 7 +- 7 files changed, 451 insertions(+), 392 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/criteria_calculators.rs create mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs create mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs create mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/mod.rs diff --git a/node/src/accountant/payment_adjuster/criteria_calculators.rs b/node/src/accountant/payment_adjuster/criteria_calculators.rs deleted file mode 100644 index 049f4d626..000000000 --- a/node/src/accountant/payment_adjuster/criteria_calculators.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - compute_progressive_characteristics, DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, - BALANCE_DIAGNOSTICS_CONFIG_OPT, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2, x_or_1}; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use std::fmt::Debug; -use std::sync::Mutex; -use std::time::SystemTime; - -//caution: always remember to use checked math operations in the formula! -pub trait CriterionCalculator { - type Input; - fn formula(&self) -> &dyn Fn(Self::Input) -> u128; - fn input_from_account(&self, account: &PayableAccount) -> Self::Input; - - fn add_calculated_criterion( - &self, - (criteria_sum, account): (u128, PayableAccount), - ) -> (u128, PayableAccount) - where - ::Input: Debug, - { - #[cfg(test)] - self.diagnostics(); - - let updated_criteria_sum = criteria_sum + self.formula()(self.input_from_account(&account)); - (updated_criteria_sum, account) - } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>>; - #[cfg(test)] - fn diagnostics_config_opt(&self) -> Option> { - self.diagnostics_config_location() - .lock() - .expect("diagnostics poisoned") - .take() - } - #[cfg(test)] - fn diagnostics(&self) - where - ::Input: Debug, - { - if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { - compute_progressive_characteristics(self.diagnostics_config_opt(), self.formula()) - } - } -} - -const AGE_MAIN_EXPONENT: u32 = 3; -// divisor^(numerator/denominator) -const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; -const AGE_MULTIPLIER: u128 = 150; -const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; -const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; -const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; - -pub struct AgeCriterionCalculator { - formula: Box u128>, -} - -impl AgeCriterionCalculator { - pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { - let now = payment_adjuster.inner.now(); - let formula = Box::new(move |last_paid_timestamp: SystemTime| { - let elapsed_secs: u64 = now - .duration_since(last_paid_timestamp) - .expect("time traveller") - .as_secs(); - let divisor = Self::compute_divisor(elapsed_secs); - let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - (elapsed_secs as u128) - .checked_pow(AGE_MAIN_EXPONENT) - .unwrap_or(u128::MAX) //TODO sensible and tested ???? - .checked_div(divisor) - .expect("div overflow") - .checked_mul(log_multiplier) - .expect("mul overflow") - }); - Self { formula } - } - - fn compute_divisor(elapsed_sec: u64) -> u128 { - (elapsed_sec as f64).sqrt().ceil() as u128 - } - - fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { - let fast_growing_argument = (elapsed_secs as u128) - .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) - .expect("pow blew up") as f64; - let log = fast_growing_argument.ln(); - let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) - * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; - let final_log_multiplier = (log_stressed - / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) - .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); - x_or_1(final_log_multiplier) - } -} - -impl CriterionCalculator for AgeCriterionCalculator { - type Input = SystemTime; - - fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { - self.formula.as_ref() - } - - fn input_from_account(&self, account: &PayableAccount) -> Self::Input { - account.last_paid_timestamp - } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> { - &AGE_DIAGNOSTICS_CONFIG_OPT - } -} - -// this parameter affects the steepness (sensitivity on increase in balance) -const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; - -pub struct BalanceCriterionCalculator { - formula: Box u128>, -} - -impl BalanceCriterionCalculator { - pub fn new() -> Self { - let formula = Box::new(|balance_wei: u128| { - let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); - balance_wei - .checked_mul(binary_weight as u128) - .expect("mul overflow") - }); - Self { formula } - } - - fn compute_binary_argument(balance_wei: u128) -> u128 { - x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) - } -} - -impl CriterionCalculator for BalanceCriterionCalculator { - type Input = u128; - - fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { - self.formula.as_ref() - } - - fn input_from_account(&self, account: &PayableAccount) -> Self::Input { - account.balance_wei - } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> { - &BALANCE_DIAGNOSTICS_CONFIG_OPT - } -} - -pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { - iter: I, - calculator: C, -} - -impl CriteriaIterator { - fn new(iter: I, calculator: C) -> Self { - Self { iter, calculator } - } -} - -impl Iterator for CriteriaIterator -where - I: Iterator, - C: CriterionCalculator, - ::Input: Debug, -{ - type Item = (u128, PayableAccount); - - fn next(&mut self) -> Option { - self.iter - .next() - .map(|item| self.calculator.add_calculated_criterion(item)) - } -} - -pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { - fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator - where - Self: Sized; -} - -impl CriteriaIteratorAdaptor for I { - fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator { - CriteriaIterator::new(self, calculator) - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::{ - AgeCriterionCalculator, BalanceCriterionCalculator, CriterionCalculator, - AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, - AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, - AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, - AGE_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, - }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; - use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; - use std::time::{Duration, SystemTime}; - - #[test] - fn constants_are_correct() { - assert_eq!(AGE_MAIN_EXPONENT, 3); - assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); - assert_eq!(AGE_MULTIPLIER, 10); - assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); - } - - #[test] - fn compute_divisor_works() { - let result: Vec<_> = [100, 81, 82, 80] - .into_iter() - .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) - .collect(); - - assert_eq!(result, vec![10, 9, 10, 9]) - } - - #[test] - fn compute_descending_multiplier_works() { - let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18] - .into_iter() - .take(12) - .map(|exp| 10_u64.pow(exp)) - .map(|seconds_elapsed| { - let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); - AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) - }) - .collect(); - - assert_eq!( - result, - vec![ - 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, - 1 - ] - ) - } - - #[test] - fn age_criteria_calculation_works() { - let now = SystemTime::now(); - let payment_adjuster = make_initialized_subject(now, None, None); - let subject = AgeCriterionCalculator::new(&payment_adjuster); - let last_paid_timestamp = SystemTime::now() - .checked_sub(Duration::from_secs(1500)) - .unwrap(); - - let result = subject.formula()(last_paid_timestamp); - - let expected_criterion = { - let elapsed_secs: u64 = now.duration_since(last_paid_timestamp).unwrap().as_secs(); - let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); - let log_multiplier = - AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); - (elapsed_secs as u128) - .checked_pow(AGE_MAIN_EXPONENT) - .unwrap() - .checked_div(divisor) - .unwrap() - .checked_mul(log_multiplier) - .unwrap() - }; - assert_eq!(result, expected_criterion) - } - - #[test] - fn compute_binary_argument_works() { - let inputs = [ - 1, - BALANCE_LOG_2_ARG_DIVISOR - 1, - BALANCE_LOG_2_ARG_DIVISOR, - BALANCE_LOG_2_ARG_DIVISOR + 1, - BALANCE_LOG_2_ARG_DIVISOR + 1000, - ]; - - let result: Vec<_> = inputs - .into_iter() - .map(|arg| BalanceCriterionCalculator::compute_binary_argument(arg)) - .collect(); - - assert_eq!( - result, - vec![ - 1, - 1, - 1, - 1, - (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR - ] - ) - } - - #[test] - fn balance_criteria_calculation_works() { - let subject = BalanceCriterionCalculator::new(); - let balance_wei = 111_333_555_777; - - let result = subject.formula()(balance_wei); - - let expected_result = { - let binary_weight = log_2(BalanceCriterionCalculator::compute_binary_argument( - balance_wei, - )); - balance_wei - .checked_mul(binary_weight as u128) - .expect("mul overflow") - }; - assert_eq!(result, expected_result) - } -} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs new file mode 100644 index 000000000..2a33fd765 --- /dev/null +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -0,0 +1,172 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use std::sync::Mutex; +use std::time::SystemTime; + +const AGE_MAIN_EXPONENT: u32 = 3; +// divisor^(numerator/denominator) +const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; +const AGE_MULTIPLIER: u128 = 150; +const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; +const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; +const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; +const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; + +pub struct AgeCriterionCalculator { + formula: Box u128>, +} + +impl AgeCriterionCalculator { + pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { + let now = payment_adjuster.inner.now(); + let formula = Box::new(move |wrapped_last_paid_timestamp: AgeInput| { + let last_paid_timestamp = wrapped_last_paid_timestamp.0; + let elapsed_secs: u64 = now + .duration_since(last_paid_timestamp) + .expect("time traveller") + .as_secs(); + let divisor = Self::compute_divisor(elapsed_secs); + let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) + .expect("pow overflow") + .checked_div(divisor) + .expect("div overflow") + .checked_mul(log_multiplier) + .expect("mul overflow") + }); + Self { formula } + } + + fn compute_divisor(elapsed_sec: u64) -> u128 { + (elapsed_sec as f64).sqrt().ceil() as u128 + } + + fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { + let fast_growing_argument = (elapsed_secs as u128) + .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) + .expect("pow blew up") as f64; + let log = fast_growing_argument.ln(); + let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) + * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; + let final_log_multiplier = (log_stressed + / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) + .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); + x_or_1(final_log_multiplier) + } +} + +impl CriterionCalculator for AgeCriterionCalculator { + type Input = AgeInput; + + fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { + self.formula.as_ref() + } + + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>> { + &AGE_DIAGNOSTICS_CONFIG_OPT + } +} + +#[derive(Debug, Clone, Copy)] +pub struct AgeInput(pub SystemTime); + +impl From<&PayableAccount> for AgeInput { + fn from(account: &PayableAccount) -> Self { + AgeInput(account.last_paid_timestamp) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ + AgeCriterionCalculator, AgeInput, AGE_DESC_MULTIPLIER_ARG_EXP, + AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, + AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, + AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, + }; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use std::time::{Duration, SystemTime}; + + #[test] + fn constants_are_correct() { + assert_eq!(AGE_MAIN_EXPONENT, 3); + assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); + assert_eq!(AGE_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); + assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); + assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); + } + + #[test] + fn compute_divisor_works() { + let result: Vec<_> = [100, 81, 82, 80] + .into_iter() + .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) + .collect(); + + assert_eq!(result, vec![10, 9, 10, 9]) + } + + #[test] + fn compute_descending_multiplier_works() { + let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18] + .into_iter() + .take(12) + .map(|exp| 10_u64.pow(exp)) + .map(|seconds_elapsed| { + let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); + AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) + }) + .collect(); + + assert_eq!( + result, + vec![ + 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, + 1 + ] + ) + } + + #[test] + fn age_criteria_calculation_works() { + let now = SystemTime::now(); + let payment_adjuster = make_initialized_subject(now, None, None); + let subject = AgeCriterionCalculator::new(&payment_adjuster); + let last_paid_timestamp = AgeInput( + SystemTime::now() + .checked_sub(Duration::from_secs(1500)) + .unwrap(), + ); + + let result = subject.formula()(last_paid_timestamp); + + let expected_criterion = { + let elapsed_secs: u64 = now.duration_since(last_paid_timestamp.0).unwrap().as_secs(); + let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); + let log_multiplier = + AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) + .unwrap() + .checked_div(divisor) + .unwrap() + .checked_mul(log_multiplier) + .unwrap() + }; + assert_eq!(result, expected_criterion) + } +} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs new file mode 100644 index 000000000..61fe5e0a8 --- /dev/null +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + DiagnosticsConfig, BALANCE_DIAGNOSTICS_CONFIG_OPT, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2, x_or_1}; +use std::sync::Mutex; + +// this parameter affects the steepness (sensitivity on balance increase) +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; + +pub struct BalanceCriterionCalculator { + formula: Box u128>, +} + +impl BalanceCriterionCalculator { + pub fn new() -> Self { + let formula = Box::new(|wrapped_balance_wei: BalanceInput| { + let balance_wei = wrapped_balance_wei.0; + let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); + balance_wei + .checked_mul(binary_weight as u128) + .expect("mul overflow") + }); + Self { formula } + } + + fn compute_binary_argument(balance_wei: u128) -> u128 { + x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) + } +} + +impl CriterionCalculator for BalanceCriterionCalculator { + type Input = BalanceInput; + + fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { + self.formula.as_ref() + } + + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>> { + &BALANCE_DIAGNOSTICS_CONFIG_OPT + } +} + +#[derive(Debug, Clone, Copy)] +pub struct BalanceInput(pub u128); + +impl From<&PayableAccount> for BalanceInput { + fn from(account: &PayableAccount) -> Self { + BalanceInput(account.balance_wei) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ + BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, + }; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; + + #[test] + fn constants_are_correct() { + assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 33) + } + + #[test] + fn compute_binary_argument_works() { + let inputs = [ + 1, + BALANCE_LOG_2_ARG_DIVISOR - 1, + BALANCE_LOG_2_ARG_DIVISOR, + BALANCE_LOG_2_ARG_DIVISOR + 1, + BALANCE_LOG_2_ARG_DIVISOR + 1000, + ]; + + let result: Vec<_> = inputs + .into_iter() + .map(|arg| BalanceCriterionCalculator::compute_binary_argument(arg)) + .collect(); + + assert_eq!( + result, + vec![ + 1, + 1, + 1, + 1, + (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR + ] + ) + } + + #[test] + fn balance_criteria_calculation_works() { + let subject = BalanceCriterionCalculator::new(); + let balance_wei = BalanceInput(111_333_555_777); + + let result = subject.formula()(balance_wei); + + let expected_result = { + let binary_weight = log_2(BalanceCriterionCalculator::compute_binary_argument( + balance_wei.0, + )); + balance_wei + .0 + .checked_mul(binary_weight as u128) + .expect("mul overflow") + }; + assert_eq!(result, expected_result) + } +} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs new file mode 100644 index 000000000..0976e8d90 --- /dev/null +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod age_criterion_calculator; +pub mod balance_criterion_calculator; + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + compute_progressive_characteristics, DiagnosticsConfig, + COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, +}; +use std::fmt::Debug; +use std::sync::Mutex; + +// Caution: always remember to use checked math operations in the formula! +pub trait CriterionCalculator { + // The additional trait constrain comes from efforts convert write the API more Rust-like. + // This implementation has its own pros and cons; the little cons for you are that whenever + // you must see the pattern of defining a wrapper for the input of your calculator. Refrain + // from writing a From implementation for third part types to satisfy the requirement. + type Input: for<'a> From<&'a PayableAccount>; + + fn formula(&self) -> &dyn Fn(Self::Input) -> u128; + + fn add_calculated_criterion( + &self, + (criteria_sum, account): (u128, PayableAccount), + ) -> (u128, PayableAccount) + where + ::Input: Debug, + { + #[cfg(test)] + self.diagnostics(); + + let updated_criteria_sum = criteria_sum + self.formula()((&account).into()); + (updated_criteria_sum, account) + } + + #[cfg(test)] + fn diagnostics_config_location(&self) -> &Mutex>>; + #[cfg(test)] + fn diagnostics_config_opt(&self) -> Option> { + self.diagnostics_config_location() + .lock() + .expect("diagnostics poisoned") + .take() + } + #[cfg(test)] + fn diagnostics(&self) + where + ::Input: Debug, + { + if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { + compute_progressive_characteristics(self.diagnostics_config_opt(), self.formula()) + } + } +} + +pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { + iter: I, + calculator: C, +} + +impl CriteriaIterator { + fn new(iter: I, calculator: C) -> Self { + Self { iter, calculator } + } +} + +impl Iterator for CriteriaIterator +where + I: Iterator, + C: CriterionCalculator, + ::Input: Debug, +{ + type Item = (u128, PayableAccount); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|item| self.calculator.add_calculated_criterion(item)) + } +} + +pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { + fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator + where + Self: Sized; +} + +impl CriteriaIteratorAdaptor for I { + fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator { + CriteriaIterator::new(self, calculator) + } +} diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 49ccd4233..3023e4c06 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,5 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; use std::fmt::Debug; const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; @@ -53,6 +54,8 @@ pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { #[cfg(test)] pub mod formulas_progressive_characteristics { + use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; + use crate::accountant::payment_adjuster::diagnostics::AgeInput; use itertools::Itertools; use lazy_static::lazy_static; use std::fmt::Debug; @@ -61,6 +64,7 @@ pub mod formulas_progressive_characteristics { use std::time::Duration; use std::time::SystemTime; use thousands::Separable; + pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; //mutex should be fine for debugging, no need for mut static static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); @@ -73,7 +77,7 @@ pub mod formulas_progressive_characteristics { } lazy_static! { - pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let now = SystemTime::now(); let x_axis_supply = { [1, 2, 3, 4, 5, 6, 7, 8, 9, 12] @@ -85,12 +89,14 @@ pub mod formulas_progressive_characteristics { label: "AGE", progressive_x_axis_supply_non_native: x_axis_supply, x_axis_native_type_formatter: Box::new(move |secs_since_last_paid_payable| { - now.checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling") + let native_time = now + .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling"); + AgeInput(native_time) }), })) }; - pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let x_axis_supply = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] .into_iter() .map(|exp| 10_u128.pow(exp)) @@ -98,7 +104,7 @@ pub mod formulas_progressive_characteristics { Mutex::new(Some(DiagnosticsConfig { label: "BALANCE", progressive_x_axis_supply_non_native: x_axis_supply, - x_axis_native_type_formatter: Box::new(|balance_wei| balance_wei), + x_axis_native_type_formatter: Box::new(|balance_wei| BalanceInput(balance_wei)), })) }; } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 55a76b6be..f3aaa0433 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -102,61 +102,6 @@ pub fn find_disqualified_account_with_smallest_proposed_balance( .clone() } -struct ExhaustionStatus { - remainder: u128, - already_finalized_accounts: Vec, -} - -impl ExhaustionStatus { - fn new(remainder: u128) -> Self { - Self { - remainder, - already_finalized_accounts: vec![], - } - } - - fn update_and_add( - mut self, - mut non_finalized_account_info: AdjustedAccountBeforeFinalization, - possible_extra_addition: u128, - ) -> Self { - let corrected_adjusted_account_before_finalization = { - non_finalized_account_info.proposed_adjusted_balance = - non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition; - non_finalized_account_info - }; - self.remainder = self - .remainder - .checked_sub(possible_extra_addition) - .unwrap_or(0); //TODO wait for overflow!!!!!!!!!!!!!!!! - self.add(corrected_adjusted_account_before_finalization) - } - - fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { - let finalized_account = PayableAccount::from(( - non_finalized_account_info, - ResolutionAfterFullyDetermined::Finalize, - )); - self.already_finalized_accounts.push(finalized_account); - self - } -} - -pub fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, -) -> Vec<(u128, PayableAccount)> { - unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) - .collect() -} - -pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { - criteria_and_accounts - .into_iter() - .map(|(_, account)| account) - .collect() -} - pub fn exhaust_cw_balance_totally( verified_accounts: Vec, original_cw_masq_balance: u128, @@ -223,6 +168,61 @@ pub fn exhaust_cw_balance_totally( .collect() } +struct ExhaustionStatus { + remainder: u128, + already_finalized_accounts: Vec, +} + +impl ExhaustionStatus { + fn new(remainder: u128) -> Self { + Self { + remainder, + already_finalized_accounts: vec![], + } + } + + fn update_and_add( + mut self, + mut non_finalized_account_info: AdjustedAccountBeforeFinalization, + possible_extra_addition: u128, + ) -> Self { + let corrected_adjusted_account_before_finalization = { + non_finalized_account_info.proposed_adjusted_balance = + non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition; + non_finalized_account_info + }; + self.remainder = self + .remainder + .checked_sub(possible_extra_addition) + .expect("we hit zero"); + self.add(corrected_adjusted_account_before_finalization) + } + + fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { + let finalized_account = PayableAccount::from(( + non_finalized_account_info, + ResolutionAfterFullyDetermined::Finalize, + )); + self.already_finalized_accounts.push(finalized_account); + self + } +} + +pub fn sort_in_descendant_order_by_weights( + unsorted: impl Iterator, +) -> Vec<(u128, PayableAccount)> { + unsorted + .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .collect() +} + +pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { + criteria_and_accounts + .into_iter() + .map(|(_, account)| account) + .collect() +} + pub fn list_accounts_under_the_disqualification_limit( non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], ) -> Vec<&AdjustedAccountBeforeFinalization> { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index cdb341aec..8dd5a6094 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -9,9 +9,9 @@ mod miscellaneous; mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::{ - AgeCriterionCalculator, BalanceCriterionCalculator, CriteriaIteratorAdaptor, -}; +use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ maybe_find_account_to_disqualify_diagnostics, non_finalized_adjusted_accounts_diagnostics, @@ -426,6 +426,7 @@ impl PaymentAdjusterReal { let weights_and_accounts = accounts_with_zero_criteria .iterate_for_criteria(AgeCriterionCalculator::new(self)) .iterate_for_criteria(BalanceCriterionCalculator::new()); + let collected_accounts_with_criteria = sort_in_descendant_order_by_weights(weights_and_accounts); From d5c5333819df337b12c0b6aec79b8c315970d99c Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Aug 2023 16:16:16 +0200 Subject: [PATCH 065/250] GH-711: again, better arrangement...this time adjustment runners --- .../payment_adjuster/adjustment_runners.rs | 113 ++++++++++++++ node/src/accountant/payment_adjuster/mod.rs | 140 ++++-------------- 2 files changed, 139 insertions(+), 114 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/adjustment_runners.rs diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs new file mode 100644 index 000000000..42f216648 --- /dev/null +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use itertools::Either; + +pub trait AdjustmentRunner { + type ReturnType; + + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + ) -> Self::ReturnType; +} + +pub struct MasqAndTransactionFeeRunner {} + +impl AdjustmentRunner for MasqAndTransactionFeeRunner { + type ReturnType = Either, Vec>; + + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + ) -> Self::ReturnType { + match payment_adjuster.inner.transaction_fee_count_limit_opt() { + Some(limit) => { + return payment_adjuster.begin_with_adjustment_by_transaction_fees( + accounts_with_individual_criteria_sorted, + limit, + ) + } + None => (), + }; + + Either::Left( + payment_adjuster + .propose_adjustment_recursively(accounts_with_individual_criteria_sorted), + ) + } +} + +pub struct MasqOnlyRunner {} + +impl AdjustmentRunner for MasqOnlyRunner { + type ReturnType = Vec; + + fn adjust( + &self, + payment_adjuster: &mut PaymentAdjusterReal, + accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + ) -> Self::ReturnType { + payment_adjuster.propose_adjustment_recursively(accounts_with_individual_criteria_sorted) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::adjustment_runners::{ + AdjustmentRunner, MasqOnlyRunner, + }; + use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; + use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + use crate::sub_lib::wallet::Wallet; + use crate::test_utils::make_wallet; + use std::time::{Duration, SystemTime}; + use web3::types::U256; + + #[test] + fn masq_only_adjuster_is_not_meant_to_adjust_also_by_transaction_fee() { + let now = SystemTime::now(); + let cw_balance = 9_000_000; + let details = FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + gas_currency_wei: U256::from(0), + masq_tokens_wei: U256::from(cw_balance), + }, + desired_gas_price_gwei: 30, + estimated_gas_limit_per_transaction: 100, + }; + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 5_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let mut account_2 = account_1.clone(); + account_2.wallet = wallet_2.clone(); + let accounts = vec![account_1, account_2]; + let adjustment = Adjustment::PriorityTransactionFee { + affordable_transaction_count: 1, + }; + let mut payment_adjuster = PaymentAdjusterReal::new(); + payment_adjuster.initialize_inner(details, adjustment, now); + let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); + let adjustment_runner = MasqOnlyRunner {}; + + let result = adjustment_runner.adjust(&mut payment_adjuster, seeds); + + let returned_accounts_accounts = result + .into_iter() + .map(|account| account.original_account.wallet) + .collect::>(); + assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) + //if the transaction_fee adjustment had been available, only one account would've been returned, the test passes + } +} diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8dd5a6094..07949e75e 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. //keep these modules private +mod adjustment_runners; mod criteria_calculators; mod diagnostics; mod inner; @@ -9,6 +10,9 @@ mod miscellaneous; mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::adjustment_runners::{ + AdjustmentRunner, MasqAndTransactionFeeRunner, MasqOnlyRunner, +}; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; @@ -272,7 +276,7 @@ impl PaymentAdjusterReal { fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, - MasqAndTransactionFeeAdjuster {}, + MasqAndTransactionFeeRunner {}, ) { Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( non_exhausted_accounts, @@ -288,7 +292,7 @@ impl PaymentAdjusterReal { purpose_specific_adjuster: A, ) -> R where - A: PurposeSpecificAdjuster, + A: AdjustmentRunner, { diagnostics_for_collections( "\nUNRESOLVED QUALIFIED ACCOUNTS:", @@ -397,7 +401,7 @@ impl PaymentAdjusterReal { let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustment_recursively( remaining, - MasqOnlyAdjuster {}, + MasqOnlyRunner {}, ); (here_decided_accounts, down_stream_decided_accounts) @@ -452,7 +456,7 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self.compute_non_finalized_adjusted_accounts( + let non_finalized_adjusted_accounts = self.compute_non_finalized_already_adjusted_accounts( accounts_with_individual_criteria, criteria_total, ); @@ -463,19 +467,18 @@ impl PaymentAdjusterReal { Right(with_some_outweighed) => return with_some_outweighed, }; - let verified_accounts = - match Self::consider_account_disqualification_from_percentage_insignificance( - unchecked_for_disqualified, - &self.logger, - ) { - Left(verified_accounts) => verified_accounts, - Right(with_some_disqualified) => return with_some_disqualified, - }; + let verified_accounts = match Self::consider_accounts_disqualification( + unchecked_for_disqualified, + &self.logger, + ) { + Left(verified_accounts) => verified_accounts, + Right(with_some_disqualified) => return with_some_disqualified, + }; AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) } - fn compute_non_finalized_adjusted_accounts( + fn compute_non_finalized_already_adjusted_accounts( &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, @@ -520,7 +523,7 @@ impl PaymentAdjusterReal { .collect() } - fn consider_account_disqualification_from_percentage_insignificance( + fn consider_accounts_disqualification( non_finalized_adjusted_accounts: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { @@ -664,60 +667,10 @@ pub enum AnalysisError { }, } -trait PurposeSpecificAdjuster { - type ReturnType; - - fn adjust( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - ) -> Self::ReturnType; -} - -struct MasqAndTransactionFeeAdjuster {} - -impl PurposeSpecificAdjuster for MasqAndTransactionFeeAdjuster { - type ReturnType = Either, Vec>; - - fn adjust( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - ) -> Self::ReturnType { - match payment_adjuster.inner.transaction_fee_count_limit_opt() { - Some(limit) => { - return payment_adjuster.begin_with_adjustment_by_transaction_fees( - accounts_with_individual_criteria_sorted, - limit, - ) - } - None => (), - }; - - Either::Left( - payment_adjuster - .propose_adjustment_recursively(accounts_with_individual_criteria_sorted), - ) - } -} - -struct MasqOnlyAdjuster {} - -impl PurposeSpecificAdjuster for MasqOnlyAdjuster { - type ReturnType = Vec; - - fn adjust( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, - ) -> Self::ReturnType { - payment_adjuster.propose_adjustment_recursively(accounts_with_individual_criteria_sorted) - } -} - #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ AdjustmentIterationResult, DisqualifiedPayableAccount, @@ -727,9 +680,8 @@ mod tests { make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - Adjustment, AnalysisError, MasqAndTransactionFeeAdjuster, MasqOnlyAdjuster, - PaymentAdjuster, PaymentAdjusterReal, PercentageAccountInsignificance, - PurposeSpecificAdjuster, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterReal, + PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -972,10 +924,11 @@ mod tests { let accounts_with_individual_criteria = subject .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); let criteria_total = criteria_total(&accounts_with_individual_criteria); - let non_finalized_adjusted_accounts = subject.compute_non_finalized_adjusted_accounts( - accounts_with_individual_criteria, - criteria_total, - ); + let non_finalized_adjusted_accounts = subject + .compute_non_finalized_already_adjusted_accounts( + accounts_with_individual_criteria, + criteria_total, + ); let result = PaymentAdjusterReal::maybe_find_an_account_to_disqualify_in_this_iteration( &non_finalized_adjusted_accounts, @@ -985,47 +938,6 @@ mod tests { assert_eq!(result, Some(wallet_3)); } - #[test] - fn masq_only_adjuster_is_not_meant_to_adjust_also_by_transaction_fee() { - let now = SystemTime::now(); - let cw_balance = 9_000_000; - let details = FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(0), - masq_tokens_wei: U256::from(cw_balance), - }, - desired_gas_price_gwei: 30, - estimated_gas_limit_per_transaction: 100, - }; - let wallet_1 = make_wallet("abc"); - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 5_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let mut account_2 = account_1.clone(); - account_2.wallet = wallet_2.clone(); - let accounts = vec![account_1, account_2]; - let adjustment = Adjustment::PriorityTransactionFee { - affordable_transaction_count: 1, - }; - let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.initialize_inner(details, adjustment, now); - let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); - let purpose_specific_adjuster = MasqOnlyAdjuster {}; - - let result = purpose_specific_adjuster.adjust(&mut payment_adjuster, seeds); - - let returned_accounts_accounts = result - .into_iter() - .map(|account| account.original_account.wallet) - .collect::>(); - assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) - //if the transaction_fee adjustment had been available, only one account would've been returned, the test passes - } - #[test] fn smaller_debt_with_extreme_age_is_picked_prioritized_as_outweighed_but_not_with_more_money_than_required( ) { @@ -1054,7 +966,7 @@ mod tests { let mut result = subject .calculate_criteria_and_propose_adjustment_recursively( qualified_payables.clone(), - MasqAndTransactionFeeAdjuster {}, + MasqAndTransactionFeeRunner {}, ) .left() .unwrap(); From 485e15cd4f6765f0f598f3415ef8c066a804e4bb Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 7 Aug 2023 20:05:13 +0200 Subject: [PATCH 066/250] GH-711: progress on so many fronts...eh --- .../age_criterion_calculator.rs | 166 +++++++- .../balance_criterion_calculator.rs | 87 +++- .../criteria_calculators/mod.rs | 26 +- .../payment_adjuster/diagnostics.rs | 188 ++++----- .../accountant/payment_adjuster/log_fns.rs | 2 +- .../miscellaneous/data_sructures.rs | 12 +- .../miscellaneous/helper_functions.rs | 200 +++++++-- node/src/accountant/payment_adjuster/mod.rs | 393 +++++++++++------- 8 files changed, 725 insertions(+), 349 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 2a33fd765..9cdf73f1c 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -1,18 +1,19 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::{ + CriterionCalculator, NamedCalculator, +}; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - DiagnosticsConfig, AGE_DIAGNOSTICS_CONFIG_OPT, + DiagnosticsConfig, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::sync::Mutex; use std::time::SystemTime; +use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; const AGE_MAIN_EXPONENT: u32 = 3; -// divisor^(numerator/denominator) -const AGE_DIVISOR_EXP_IN_NUMERATOR: u32 = 3; const AGE_MULTIPLIER: u128 = 150; const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; @@ -27,14 +28,15 @@ pub struct AgeCriterionCalculator { impl AgeCriterionCalculator { pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { let now = payment_adjuster.inner.now(); + let formula = Box::new(move |wrapped_last_paid_timestamp: AgeInput| { let last_paid_timestamp = wrapped_last_paid_timestamp.0; - let elapsed_secs: u64 = now - .duration_since(last_paid_timestamp) - .expect("time traveller") - .as_secs(); - let divisor = Self::compute_divisor(elapsed_secs); + let elapsed_secs: u64 = Self::nonzero_elapsed(now, last_paid_timestamp); + + let divisor = Self::nonzero_compute_divisor(elapsed_secs); + let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + (elapsed_secs as u128) .checked_pow(AGE_MAIN_EXPONENT) .expect("pow overflow") @@ -46,20 +48,44 @@ impl AgeCriterionCalculator { Self { formula } } - fn compute_divisor(elapsed_sec: u64) -> u128 { + fn nonzero_elapsed(now: SystemTime, previous_timestamp: SystemTime) -> u64 { + let elapsed = now + .duration_since(previous_timestamp) + .expect("time traveller") + .as_secs(); + if elapsed > 0 { + elapsed + } else { + 1 + } + } + + fn nonzero_compute_divisor(elapsed_sec: u64) -> u128 { (elapsed_sec as f64).sqrt().ceil() as u128 } + fn nonzero_log_value(num: f64) -> u128 { + if num < 2.0 { + 1 + } else { + num.log2() as u128 + } + } + fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { let fast_growing_argument = (elapsed_secs as u128) .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) .expect("pow blew up") as f64; - let log = fast_growing_argument.ln(); - let log_stressed = (log as u128).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) + + let log_value = Self::nonzero_log_value(fast_growing_argument); + + let log_stressed = (log_value).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; + let final_log_multiplier = (log_stressed / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); + x_or_1(final_log_multiplier) } } @@ -77,6 +103,12 @@ impl CriterionCalculator for AgeCriterionCalculator { } } +impl NamedCalculator for AgeCriterionCalculator { + fn parameter_name(&self) -> &'static str { + "AGE" + } +} + #[derive(Debug, Clone, Copy)] pub struct AgeInput(pub SystemTime); @@ -86,22 +118,79 @@ impl From<&PayableAccount> for AgeInput { } } +#[cfg(test)] +pub mod characteristics_config { + use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use itertools::Either::{Left, Right}; + use lazy_static::lazy_static; + use std::sync::Mutex; + use std::time::Duration; + use std::time::SystemTime; + + lazy_static! { + pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + let now = SystemTime::now(); + let x_axis_supply = { + [ + Left(1), + Left(5), + Left(9), + Left(25), + Left(44), + Left(50), + Left(75), + Right(2), + Right(3), + Right(4), + Left(33_333), + Right(5), + Right(6), + Right(7), + Right(8), + Left(200_300_400), + Right(9), + Right(10), + Right(12), + ] + .into_iter() + .map(|exp| match exp { + Left(precise_secs) => precise_secs, + Right(decimal_exp) => 10_u128.pow(decimal_exp), + }) + .collect() + }; + Mutex::new(Some(DiagnosticsConfig { + label: "AGE", + x_axis_progressive_supply: x_axis_supply, + x_axis_native_type_formatter: Box::new(move |secs_since_last_paid_payable| { + let native_time = now + .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling"); + AgeInput(native_time) + }), + })) + }; + } +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ AgeCriterionCalculator, AgeInput, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, - AGE_DIVISOR_EXP_IN_NUMERATOR, AGE_MAIN_EXPONENT, AGE_MULTIPLIER, + AGE_MAIN_EXPONENT, AGE_MULTIPLIER, + }; + use crate::accountant::payment_adjuster::criteria_calculators::{ + CriterionCalculator, NamedCalculator, }; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; #[test] fn constants_are_correct() { assert_eq!(AGE_MAIN_EXPONENT, 3); - assert_eq!(AGE_DIVISOR_EXP_IN_NUMERATOR, 3); assert_eq!(AGE_MULTIPLIER, 10); assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); @@ -111,13 +200,28 @@ mod tests { } #[test] - fn compute_divisor_works() { - let result: Vec<_> = [100, 81, 82, 80] + fn nonzero_compute_divisor_works() { + let result: Vec<_> = [1, 100, 81, 82, 80] .into_iter() - .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) + .map(|secs| AgeCriterionCalculator::nonzero_compute_divisor(secs)) .collect(); - assert_eq!(result, vec![10, 9, 10, 9]) + assert_eq!(result, vec![1, 10, 9, 10, 9]) + } + + #[test] + fn nonzero_elapsed_works() { + let now = SystemTime::now(); + let result: Vec<_> = [ + now.checked_sub(Duration::from_nanos(55)).unwrap(), + now.checked_sub(Duration::from_secs(1)).unwrap(), + now.checked_sub(Duration::from_secs(2)).unwrap(), + ] + .into_iter() + .map(|timestamp| AgeCriterionCalculator::nonzero_elapsed(now, timestamp)) + .collect(); + + assert_eq!(result, vec![1, 1, 2]) } #[test] @@ -127,7 +231,7 @@ mod tests { .take(12) .map(|exp| 10_u64.pow(exp)) .map(|seconds_elapsed| { - let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); + let divisor = AgeCriterionCalculator::nonzero_compute_divisor(seconds_elapsed); AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) }) .collect(); @@ -141,6 +245,26 @@ mod tests { ) } + #[test] + fn nonzero_log_works() { + let result = vec![0.0, 0.6, 1.3, 1.99999, 2.0, 2.1, 5.0, 9.0] + .into_iter() + .map(|num| AgeCriterionCalculator::nonzero_log_value(num)) + .collect::>(); + + assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 2, 3]) + } + + #[test] + fn calculator_has_the_right_name() { + let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); + let subject = AgeCriterionCalculator::new(&payment_adjuster); + + let result = subject.parameter_name(); + + assert_eq!(result, "AGE") + } + #[test] fn age_criteria_calculation_works() { let now = SystemTime::now(); @@ -156,7 +280,7 @@ mod tests { let expected_criterion = { let elapsed_secs: u64 = now.duration_since(last_paid_timestamp.0).unwrap().as_secs(); - let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); + let divisor = AgeCriterionCalculator::nonzero_compute_divisor(elapsed_secs); let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 61fe5e0a8..174389aad 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -1,12 +1,15 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::{ + CriterionCalculator, NamedCalculator, +}; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - DiagnosticsConfig, BALANCE_DIAGNOSTICS_CONFIG_OPT, + DiagnosticsConfig, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2, x_or_1}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2}; use std::sync::Mutex; +use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; // this parameter affects the steepness (sensitivity on balance increase) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; @@ -18,17 +21,26 @@ pub struct BalanceCriterionCalculator { impl BalanceCriterionCalculator { pub fn new() -> Self { let formula = Box::new(|wrapped_balance_wei: BalanceInput| { - let balance_wei = wrapped_balance_wei.0; - let binary_weight = log_2(Self::compute_binary_argument(balance_wei)); - balance_wei + let balance_minor = wrapped_balance_wei.0; + let binary_weight = log_2(Self::calculate_binary_argument(balance_minor)); + balance_minor .checked_mul(binary_weight as u128) .expect("mul overflow") }); Self { formula } } - fn compute_binary_argument(balance_wei: u128) -> u128 { - x_or_1(balance_wei / BALANCE_LOG_2_ARG_DIVISOR) + fn nonzero_log2(balance_minor: u128) -> u32 { + let log = log_2(Self::calculate_binary_argument(balance_minor)); + if log > 0 { + log + } else { + 1 + } + } + + fn calculate_binary_argument(balance_minor: u128) -> u128 { + balance_minor / BALANCE_LOG_2_ARG_DIVISOR } } @@ -45,6 +57,12 @@ impl CriterionCalculator for BalanceCriterionCalculator { } } +impl NamedCalculator for BalanceCriterionCalculator { + fn parameter_name(&self) -> &'static str { + "BALANCE" + } +} + #[derive(Debug, Clone, Copy)] pub struct BalanceInput(pub u128); @@ -54,12 +72,36 @@ impl From<&PayableAccount> for BalanceInput { } } +#[cfg(test)] +pub mod characteristics_config { + use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use lazy_static::lazy_static; + use std::sync::Mutex; + + lazy_static! { + pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + let x_axis_decimal_exponens = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] + .into_iter() + .map(|exp| 10_u128.pow(exp)) + .collect(); + Mutex::new(Some(DiagnosticsConfig { + label: "BALANCE", + x_axis_progressive_supply: x_axis_decimal_exponens, + x_axis_native_type_formatter: Box::new(|balance_wei| BalanceInput(balance_wei)), + })) + }; + } +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, }; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::criteria_calculators::{ + CriterionCalculator, NamedCalculator, + }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; #[test] @@ -79,14 +121,14 @@ mod tests { let result: Vec<_> = inputs .into_iter() - .map(|arg| BalanceCriterionCalculator::compute_binary_argument(arg)) + .map(|arg| BalanceCriterionCalculator::calculate_binary_argument(arg)) .collect(); assert_eq!( result, vec![ - 1, - 1, + 0, + 0, 1, 1, (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR @@ -94,6 +136,25 @@ mod tests { ) } + #[test] + fn nonzero_log2_works() { + let result: Vec<_> = [0, 5, 66, 100, 131, 132] + .into_iter() + .map(|balance| BalanceCriterionCalculator::nonzero_log2(balance)) + .collect(); + + assert_eq!(result, vec![1, 1, 1, 1, 1, 2]) + } + + #[test] + fn calculator_has_the_right_name() { + let subject = BalanceCriterionCalculator::new(); + + let result = subject.parameter_name(); + + assert_eq!(result, "BALANCE") + } + #[test] fn balance_criteria_calculation_works() { let subject = BalanceCriterionCalculator::new(); @@ -102,7 +163,7 @@ mod tests { let result = subject.formula()(balance_wei); let expected_result = { - let binary_weight = log_2(BalanceCriterionCalculator::compute_binary_argument( + let binary_weight = log_2(BalanceCriterionCalculator::calculate_binary_argument( balance_wei.0, )); balance_wei diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 0976e8d90..7b25e7d63 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -8,20 +8,24 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara compute_progressive_characteristics, DiagnosticsConfig, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::calculator_local_diagnostics; use std::fmt::Debug; use std::sync::Mutex; // Caution: always remember to use checked math operations in the formula! -pub trait CriterionCalculator { +pub trait CriterionCalculator: NamedCalculator { // The additional trait constrain comes from efforts convert write the API more Rust-like. // This implementation has its own pros and cons; the little cons for you are that whenever // you must see the pattern of defining a wrapper for the input of your calculator. Refrain // from writing a From implementation for third part types to satisfy the requirement. type Input: for<'a> From<&'a PayableAccount>; + // This is the only function you are supposed to implement for your calculator. + // All it does is linking the formula from inside of your calculator (see implementations), + // and exposes it to outside fn formula(&self) -> &dyn Fn(Self::Input) -> u128; - fn add_calculated_criterion( + fn calculate_and_add_to_criteria_sum( &self, (criteria_sum, account): (u128, PayableAccount), ) -> (u128, PayableAccount) @@ -29,10 +33,14 @@ pub trait CriterionCalculator { ::Input: Debug, { #[cfg(test)] - self.diagnostics(); + self.formula_characteristics_diagnostics(); - let updated_criteria_sum = criteria_sum + self.formula()((&account).into()); - (updated_criteria_sum, account) + let criterion: u128 = self.formula()((&account).into()); + let new_sum = criteria_sum + criterion; + + calculator_local_diagnostics(&account.wallet, self, criterion, new_sum); + + (criteria_sum + criterion, account) } #[cfg(test)] @@ -45,7 +53,7 @@ pub trait CriterionCalculator { .take() } #[cfg(test)] - fn diagnostics(&self) + fn formula_characteristics_diagnostics(&self) where ::Input: Debug, { @@ -77,7 +85,7 @@ where fn next(&mut self) -> Option { self.iter .next() - .map(|item| self.calculator.add_calculated_criterion(item)) + .map(|item| self.calculator.calculate_and_add_to_criteria_sum(item)) } } @@ -92,3 +100,7 @@ impl CriteriaIteratorAdaptor for I { CriteriaIterator::new(self, calculator) } } + +pub trait NamedCalculator { + fn parameter_name(&self) -> &'static str; +} diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 3023e4c06..e53d1f18e 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,9 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -52,111 +51,9 @@ pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { } } -#[cfg(test)] -pub mod formulas_progressive_characteristics { - use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; - use crate::accountant::payment_adjuster::diagnostics::AgeInput; - use itertools::Itertools; - use lazy_static::lazy_static; - use std::fmt::Debug; - use std::iter::once; - use std::sync::{Mutex, Once}; - use std::time::Duration; - use std::time::SystemTime; - use thousands::Separable; - - pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; - //mutex should be fine for debugging, no need for mut static - static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); - static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); - - pub struct DiagnosticsConfig { - label: &'static str, - progressive_x_axis_supply_non_native: Vec, - x_axis_native_type_formatter: Box A + Send>, - } - - lazy_static! { - pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let now = SystemTime::now(); - let x_axis_supply = { - [1, 2, 3, 4, 5, 6, 7, 8, 9, 12] - .into_iter() - .map(|exp| 10_u128.pow(exp)) - .collect() - }; - Mutex::new(Some(DiagnosticsConfig { - label: "AGE", - progressive_x_axis_supply_non_native: x_axis_supply, - x_axis_native_type_formatter: Box::new(move |secs_since_last_paid_payable| { - let native_time = now - .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling"); - AgeInput(native_time) - }), - })) - }; - pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let x_axis_supply = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] - .into_iter() - .map(|exp| 10_u128.pow(exp)) - .collect(); - Mutex::new(Some(DiagnosticsConfig { - label: "BALANCE", - progressive_x_axis_supply_non_native: x_axis_supply, - x_axis_native_type_formatter: Box::new(|balance_wei| BalanceInput(balance_wei)), - })) - }; - } - - pub fn print_formulas_characteristics_for_diagnostics() { - if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { - FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { - let report = STRINGS_WITH_FORMULAS_CHARACTERISTICS - .lock() - .expect("diagnostics poisoned") - .join("\n\n"); - eprintln!("{}", report) - }) - } - } - - pub fn compute_progressive_characteristics( - config_opt: Option>, - formula: &dyn Fn(A) -> u128, - ) where - A: Debug, - { - config_opt.map(|config| { - let config_x_axis_type_formatter = config.x_axis_native_type_formatter; - let characteristics = - config - .progressive_x_axis_supply_non_native - .into_iter() - .map(|input| { - let correctly_formatted_input = config_x_axis_type_formatter(input); - format!( - "x: {:( + wallet_ref: &Wallet, + calculator: &N, + criterion: u128, + added_in_the_sum: u128, + ) { + const FIRST_COLUMN_WIDTH: usize = 30; + diagnostics!( + wallet_ref, + "PARTIAL CRITERION CALCULATED", + "{:> = Mutex::new(vec![]); + static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); + + pub struct DiagnosticsConfig { + pub label: &'static str, + pub x_axis_progressive_supply: Vec, + pub x_axis_native_type_formatter: Box A + Send>, + } + + pub fn print_formulas_characteristics_for_diagnostics() { + if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { + FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { + let report = STRINGS_WITH_FORMULAS_CHARACTERISTICS + .lock() + .expect("diagnostics poisoned") + .join("\n\n"); + eprintln!("{}", report) + }) + } + } + + pub fn compute_progressive_characteristics( + config_opt: Option>, + formula: &dyn Fn(A) -> u128, + ) where + A: Debug, + { + config_opt.map(|config| { + let config_x_axis_type_formatter = config.x_axis_native_type_formatter; + let characteristics = config.x_axis_progressive_supply.into_iter().map(|input| { + let correctly_formatted_input = config_x_axis_type_formatter(input); + format!( + "x: {:, resolution: ResolutionAfterFullyDetermined, ) -> Vec { @@ -71,3 +71,13 @@ impl DisqualifiedPayableAccount { } } } + +// sets the minimal percentage of the original balance that must be +// proposed after the adjustment or the account will be eliminated for insignificance +#[derive(Debug, PartialEq, Eq)] +pub struct PercentageAccountInsignificance { + // using integers means we have to represent accurate percentage + // as set of two constants + pub multiplier: u128, + pub divisor: u128, +} diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index f3aaa0433..b122fbea3 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -6,16 +6,22 @@ use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnos possibly_outweighed_accounts_diagnostics, }; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ - AdjustedAccountBeforeFinalization, ResolutionAfterFullyDetermined, + AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, + ResolutionAfterFullyDetermined, }; -use crate::accountant::payment_adjuster::{diagnostics, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE}; -use crate::sub_lib::wallet::Wallet; +use crate::accountant::payment_adjuster::{diagnostics, AnalysisError}; use itertools::Itertools; use std::iter::successors; use thousands::Separable; const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; +// represents 50% +pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = + PercentageAccountInsignificance { + multiplier: 1, + divisor: 2, + }; pub fn sum_as(collection: &[T], arranger: F) -> N where @@ -31,6 +37,29 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } +pub fn analyze_potential_adjustment_feasibility( + accounts: &[&PayableAccount], + cw_masq_balance_minor: u128, +) -> Result<(), AnalysisError> { + let largest_account = + find_largest_debt_account_generic(accounts, |account| account.balance_wei); + eprintln!( + "largest: {:?}, cw balance {}", + largest_account, cw_masq_balance_minor + ); + if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + <= cw_masq_balance_minor + { + Ok(()) + } else { + Err(AnalysisError::RiskOfAdjustmentWithTooLowBalances { + cw_masq_balance_minor, + number_of_accounts: accounts.len(), + }) //TODO think later if you wanna carry the info about count, we could fetch it at a different place too + } +} + pub fn cut_back_by_excessive_transaction_fee( weights_and_accounts: Vec<(u128, PayableAccount)>, limit: u16, @@ -85,21 +114,31 @@ pub fn possibly_outweighed_accounts_fold_guts( } } -pub fn find_disqualified_account_with_smallest_proposed_balance( - accounts: &[&AdjustedAccountBeforeFinalization], -) -> Wallet { - let account_ref = accounts.iter().reduce(|smallest_so_far, current| { - if current.proposed_adjusted_balance > smallest_so_far.proposed_adjusted_balance { - smallest_so_far - } else { - current - } - }); - account_ref - .expect("the iterator was empty but we had checked it") - .original_account - .wallet - .clone() +pub fn find_largest_debt_account_generic(accounts: &[A], balance_fetcher: fn(&A) -> u128) -> &A { + struct Largest<'a, A> { + account: &'a A, + balance: u128, + } + + let first_account = accounts.first().expect("collection was empty"); + let init = Largest { + account: first_account, + balance: balance_fetcher(first_account), + }; + accounts + .iter() + .fold(init, |largest_so_far, current| { + let balance = balance_fetcher(current); + if balance <= largest_so_far.balance { + largest_so_far + } else { + Largest { + account: current, + balance, + } + } + }) + .account } pub fn exhaust_cw_balance_totally( @@ -263,7 +302,7 @@ const fn num_bits() -> usize { pub fn log_2(x: u128) -> u32 { if x < 1 { - panic!("log2 of 0 not supported") + return 0; } num_bits::() as u32 - x.leading_zeros() - 1 } @@ -303,16 +342,17 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, - find_disqualified_account_with_smallest_proposed_balance, + analyze_potential_adjustment_feasibility, compute_fraction_preventing_mul_coeff, + exhaust_cw_balance_totally, find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, log_10, log_2, - possibly_outweighed_accounts_fold_guts, ExhaustionStatus, EMPIRIC_PRECISION_COEFFICIENT, + possibly_outweighed_accounts_fold_guts, ExhaustionStatus, + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; + use crate::accountant::payment_adjuster::AnalysisError; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -322,7 +362,76 @@ mod tests { #[test] fn constants_are_correct() { assert_eq!(MAX_EXPONENT_FOR_10_IN_U128, 38); - assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8) + assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); + assert_eq!(ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier, 1); + assert_eq!(ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor, 2) + } + + fn test_body_for_adjustment_feasibility_nearly_insufficient( + original_accounts: Vec, + cw_masq_balance: u128, + ) { + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + + let result = + analyze_potential_adjustment_feasibility(&accounts_in_expected_format, cw_masq_balance); + + assert_eq!(result, Ok(())) + } + + fn calculate_border_line(account_balance: u128) -> u128 { + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + } + + #[test] + fn adjustment_feasibility_nearly_insufficient_when_1_less() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_border_line(account_1.balance_wei) + 1; + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + } + + #[test] + fn adjustment_feasibility_nearly_insufficient_when_equal() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_border_line(account_1.balance_wei); + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + } + + #[test] + fn adjustment_feasibility_err_from_insufficient_balance() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(222); + account_2.balance_wei = 2_000_000_002; + let mut account_3 = make_payable_account(333); + account_3.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_border_line(account_2.balance_wei) - 1; + let original_accounts = vec![account_1, account_2, account_3]; + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + + let result = + analyze_potential_adjustment_feasibility(&accounts_in_expected_format, cw_masq_balance); + + assert_eq!( + result, + Err(AnalysisError::RiskOfAdjustmentWithTooLowBalances { + cw_masq_balance_minor: cw_masq_balance, + number_of_accounts: 3 + }) + ) } #[test] @@ -354,9 +463,10 @@ mod tests { } #[test] - #[should_panic(expected = "log2 of 0 not supported")] - fn log_2_dislikes_0() { - let _ = log_2(0); + fn log_2_for_0() { + let result = log_2(0); + + assert_eq!(result, 0) } #[test] @@ -480,24 +590,32 @@ mod tests { } #[test] - fn find_disqualified_account_with_smallest_proposed_balance_when_accounts_with_equal_balances() - { - let account_info = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(111), - proposed_adjusted_balance: 1_234_567_890, - criteria_sum: 400_000_000, - }; + fn find_largest_debt_account_generic_works() { + let account_1 = make_payable_account(111); + let account_2 = make_payable_account(333); + let account_3 = make_payable_account(222); + let account_4 = make_payable_account(332); + let accounts = vec![account_1, account_2.clone(), account_3, account_4]; + + let result = find_largest_debt_account_generic(&accounts, |account| account.balance_wei); + + assert_eq!(result, &account_2) + } + + #[test] + fn find_largest_debt_account_generic_when_accounts_with_equal_balances() { + let account = make_payable_account(111); let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); - let mut account_info_1 = account_info.clone(); - account_info_1.original_account.wallet = wallet_1; - let mut account_info_2 = account_info; - account_info_2.original_account.wallet = wallet_2.clone(); - let accounts = vec![&account_info_1, &account_info_2]; + let mut account_1 = account.clone(); + account_1.wallet = wallet_1.clone(); + let mut account_2 = account; + account_2.wallet = wallet_2; + let accounts = vec![account_1.clone(), account_2]; - let result = find_disqualified_account_with_smallest_proposed_balance(&accounts); + let result = find_largest_debt_account_generic(&accounts, |account| account.balance_wei); - assert_eq!(result, wallet_2) + assert_eq!(result, &account_1) } #[test] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 07949e75e..7e479be39 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -18,7 +18,7 @@ use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ - maybe_find_account_to_disqualify_diagnostics, non_finalized_adjusted_accounts_diagnostics, + maybe_find_an_account_to_disqualify_diagnostics, non_finalized_adjusted_accounts_diagnostics, }; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_for_collections}; use crate::accountant::payment_adjuster::inner::{ @@ -37,7 +37,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, - exhaust_cw_balance_totally, find_disqualified_account_with_smallest_proposed_balance, + exhaust_cw_balance_totally, find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, }; @@ -161,23 +161,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { implement_as_any!(); } -// represents 50% -const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = - PercentageAccountInsignificance { - multiplier: 1, - divisor: 2, - }; - -// sets the minimal percentage of the original balance that must be -// proposed after the adjustment or the account will be eliminated for insignificance -#[derive(Debug, PartialEq, Eq)] -struct PercentageAccountInsignificance { - // using integers means we have to represent accurate percentage - // as set of two constants - multiplier: u128, - divisor: u128, -} - impl Default for PaymentAdjusterReal { fn default() -> Self { Self::new() @@ -192,87 +175,103 @@ impl PaymentAdjusterReal { } } - fn initialize_inner( - &mut self, - setup: FinancialAndTechDetails, - required_adjustment: Adjustment, - now: SystemTime, - ) { - let transaction_fee_limitation_opt = match required_adjustment { - Adjustment::PriorityTransactionFee { - affordable_transaction_count, - } => Some(affordable_transaction_count), - Adjustment::MasqToken => None, - }; - let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_wei.as_u128(); - let inner = - PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); - self.inner = Box::new(inner); - } - fn determine_transactions_count_limit_by_transaction_fee( tech_info: &FinancialAndTechDetails, - required_transactions_count: usize, + required_tx_count: usize, logger: &Logger, ) -> Result, AnalysisError> { - let transaction_fee_required_per_transaction_in_major = - u128::try_from(tech_info.estimated_gas_limit_per_transaction) - .expectv("small number for transaction fee limit") - * u128::try_from(tech_info.desired_gas_price_gwei) - .expectv("small number for transaction fee price"); - let tfrpt_in_minor: U256 = gwei_to_wei(transaction_fee_required_per_transaction_in_major); - let available_balance_in_minor = tech_info.consuming_wallet_balances.gas_currency_wei; - let limiting_max_possible_count = (available_balance_in_minor / tfrpt_in_minor).as_u128(); - if limiting_max_possible_count == 0 { + let transaction_fee_required_per_transaction_major = + tech_info.estimated_gas_limit_per_transaction as u128 + * tech_info.desired_gas_price_gwei as u128; + + let transaction_fee_required_minor: U256 = + gwei_to_wei(transaction_fee_required_per_transaction_major); + let available_balance_minor = tech_info.consuming_wallet_balances.gas_currency_wei; + let max_doable_tx_count_loose_boundaries = + available_balance_minor / transaction_fee_required_minor; + let (max_doable_tx_count_u16, required_tx_count_u16) = + Self::process_big_nums_and_look_out_for_ceiling( + max_doable_tx_count_loose_boundaries, + required_tx_count, + ); + if max_doable_tx_count_u16 == 0 { + let cw_balance = wei_to_gwei(available_balance_minor); + let per_transaction_requirement = transaction_fee_required_per_transaction_major as u64; Err(AnalysisError::BalanceBelowSingleTxFee { - one_transaction_requirement: transaction_fee_required_per_transaction_in_major - as u64, - cw_balance: wei_to_gwei(available_balance_in_minor), + per_transaction_requirement, + cw_balance_major: cw_balance, }) - } else if limiting_max_possible_count >= required_transactions_count as u128 { + } else if max_doable_tx_count_u16 >= required_tx_count_u16 { Ok(None) } else { - let limiting_count = u16::try_from(limiting_max_possible_count) - .expectv("small number for possible tx count"); log_insufficient_transaction_fee_balance( logger, - required_transactions_count, + required_tx_count_u16, tech_info, - limiting_count, + max_doable_tx_count_u16, ); - Ok(Some(limiting_count)) + Ok(Some(max_doable_tx_count_u16)) } } + fn process_big_nums_and_look_out_for_ceiling( + max_doable_tx_count: U256, + required_tx_count: usize, + ) -> (u16, u16) { + ( + u16::try_from(max_doable_tx_count).unwrap_or(u16::MAX), + u16::try_from(required_tx_count).unwrap_or(u16::MAX), + ) + } + //TODO we should check there is at least one half of the smallest payment fn check_need_of_masq_balances_adjustment( logger: &Logger, - qualified_payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, - consuming_wallet_balance_wei: u128, + payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, + consuming_wallet_balance_minor: u128, ) -> bool { - let qualified_payables: Vec<&PayableAccount> = match qualified_payables { + let qualified_payables: Vec<&PayableAccount> = match payables { Either::Left(accounts) => accounts.iter().collect(), Either::Right(criteria_and_accounts) => criteria_and_accounts .iter() .map(|(_, account)| account) .collect(), }; + let required_masq_sum: u128 = sum_as(&qualified_payables, |account: &&PayableAccount| { account.balance_wei }); - if required_masq_sum <= consuming_wallet_balance_wei { + if consuming_wallet_balance_minor >= required_masq_sum { false } else { log_adjustment_by_masq_required( logger, required_masq_sum, - consuming_wallet_balance_wei, + consuming_wallet_balance_minor, ); true } } + fn initialize_inner( + &mut self, + setup: FinancialAndTechDetails, + required_adjustment: Adjustment, + now: SystemTime, + ) { + let transaction_fee_limitation_opt = match required_adjustment { + Adjustment::PriorityTransactionFee { + affordable_transaction_count, + } => Some(affordable_transaction_count), + Adjustment::MasqToken => None, + }; + let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_wei.as_u128(); + let inner = + PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); + self.inner = Box::new(inner); + } + fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, @@ -592,11 +591,16 @@ impl PaymentAdjusterReal { .is_empty() .not() .then(|| { - let wallet = find_disqualified_account_with_smallest_proposed_balance( + // comparison based on the criteria sum is more accurate than on the proposed adjusted balance + // which comes rounded up to here + let account_to_disqualify = find_largest_debt_account_generic( &disqualification_suspected_accounts, + |account_info| account_info.criteria_sum, ); - maybe_find_account_to_disqualify_diagnostics( + let wallet = account_to_disqualify.original_account.wallet.clone(); + + maybe_find_an_account_to_disqualify_diagnostics( &disqualification_suspected_accounts, &wallet, ); @@ -624,7 +628,7 @@ impl PaymentAdjusterReal { if outweighed.is_empty() { Left(passing_through) } else { - let remaining = AdjustedAccountBeforeFinalization::finalize_collection_of_self( + let remaining = AdjustedAccountBeforeFinalization::finalize_collection( passing_through, ResolutionAfterFullyDetermined::Revert, ); @@ -662,8 +666,12 @@ pub enum Adjustment { #[derive(Debug, PartialEq, Eq)] pub enum AnalysisError { BalanceBelowSingleTxFee { - one_transaction_requirement: u64, - cw_balance: u64, + per_transaction_requirement: u64, + cw_balance_major: u64, + }, + RiskOfAdjustmentWithTooLowBalances { + cw_masq_balance_minor: u128, + number_of_accounts: usize, }, } @@ -681,7 +689,6 @@ mod tests { }; use crate::accountant::payment_adjuster::{ Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterReal, - PercentageAccountInsignificance, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -698,21 +705,10 @@ mod tests { use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; - use std::vec; + use std::{usize, vec}; use thousands::Separable; use web3::types::U256; - #[test] - fn constants_are_correct() { - assert_eq!( - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - PercentageAccountInsignificance { - multiplier: 1, - divisor: 2, - } - ); - } - #[test] #[should_panic( expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" @@ -723,6 +719,11 @@ mod tests { let _ = result.inner.unallocated_cw_masq_balance(); } + struct MasqBalancesTestSetup { + balances_of_accounts_major: Vec, + cw_balance_major: u64, + } + #[test] fn search_for_indispensable_adjustment_negative_answer() { init_test_logging(); @@ -731,11 +732,21 @@ mod tests { let logger = Logger::new(test_name); subject.logger = logger; //masq balance > payments - let msg_1 = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 14], 100)), None); + let msg_1 = make_payable_setup_msg_coming_from_blockchain_bridge( + Some(MasqBalancesTestSetup { + balances_of_accounts_major: vec![85, 14], + cw_balance_major: 100, + }), + None, + ); //masq balance = payments - let msg_2 = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 15], 100)), None); + let msg_2 = make_payable_setup_msg_coming_from_blockchain_bridge( + Some(MasqBalancesTestSetup { + balances_of_accounts_major: vec![85, 15], + cw_balance_major: 100, + }), + None, + ); //transaction_fee balance > payments let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( None, @@ -776,8 +787,13 @@ mod tests { let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; - let msg = - make_payable_setup_msg_coming_from_blockchain_bridge(Some((vec![85, 16], 100)), None); + let msg = make_payable_setup_msg_coming_from_blockchain_bridge( + Some(MasqBalancesTestSetup { + balances_of_accounts_major: vec![85, 16], + cw_balance_major: 100, + }), + None, + ); let result = subject.search_for_indispensable_adjustment(&msg); @@ -847,12 +863,66 @@ mod tests { assert_eq!( result, Err(AnalysisError::BalanceBelowSingleTxFee { - one_transaction_requirement: 55_000 * 100, - cw_balance: 54_000 * 100 + per_transaction_requirement: 55_000 * 100, + cw_balance_major: 54_000 * 100 }) ); } + #[test] + fn there_is_u16_max_ceiling_for_doable_txs_count() { + let result = [-3_i8, -1, 0, 1, 10] + .into_iter() + .map(|correction| { + if correction < 0 { + U256::from(u16::MAX - (correction.abs() as u16)) + } else { + U256::from(u16::MAX as u32 + correction as u32) + } + }) + .map(|max_doable_txs_count_u256| { + let (doable_txs_count, _) = + PaymentAdjusterReal::process_big_nums_and_look_out_for_ceiling( + max_doable_txs_count_u256, + 123, + ); + doable_txs_count + }) + .collect::>(); + + assert_eq!( + result, + vec![u16::MAX - 3, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] + ) + } + + #[test] + fn there_is_u16_max_ceiling_for_required_txs_count() { + let result = [-9_i8, -1, 0, 1, 5] + .into_iter() + .map(|correction| { + if correction < 0 { + (u16::MAX - (correction.abs() as u16)) as usize + } else { + u16::MAX as usize + correction as usize + } + }) + .map(|required_tx_count_usize| { + let (_, required_tx_count) = + PaymentAdjusterReal::process_big_nums_and_look_out_for_ceiling( + U256::from(123), + required_tx_count_usize, + ); + required_tx_count + }) + .collect::>(); + + assert_eq!( + result, + vec![u16::MAX - 9, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] + ) + } + #[test] fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { let now = SystemTime::now(); @@ -886,39 +956,40 @@ mod tests { } #[test] - fn only_the_least_demanding_disqualified_account_is_picked_at_a_time_even_though_more_of_them_can_be_found( - ) { - let test_name = "only_the_least_demanding_disqualified_account_is_picked_at_a_time_even_though_more_of_them_can_be_found"; + fn only_the_most_demanding_one_is_picked_from_disqualified_accounts() { + let test_name = "only_the_most_demanding_one_is_picked_from_disqualified_accounts"; let now = SystemTime::now(); - let cw_masq_balance = 1_000_000_000_000_000_000; + let cw_masq_balance = 2_000_000_000_000; let logger = Logger::new(test_name); let subject = make_initialized_subject(now, Some(cw_masq_balance), None); let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { wallet: wallet_1.clone(), - balance_wei: 600_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + balance_wei: 700_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }; let wallet_2 = make_wallet("def"); let account_2 = PayableAccount { wallet: wallet_2.clone(), - balance_wei: 8_000_000_000_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(300_000)).unwrap(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghi"); + // balance big enough to be hard to match it up to the disqualification limit and 1 s more + // than the first account let account_3 = PayableAccount { wallet: wallet_3.clone(), - balance_wei: 333_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(800)).unwrap(), + balance_wei: 700_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_001)).unwrap(), pending_payable_opt: None, }; let wallet_4 = make_wallet("jkl"); let account_4 = PayableAccount { wallet: wallet_4.clone(), - balance_wei: 700_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + balance_wei: 120_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(7_000)).unwrap(), pending_payable_opt: None, }; let accounts_with_individual_criteria = subject @@ -1456,46 +1527,52 @@ mod tests { wei which is not at least more than a half of the original debt 600,000,000")); } - struct CompetitiveAccountsTestInputFeeder<'a> { - common: CommonInput<'a>, - balance_account_1: u128, - balance_account_2: u128, - age_secs_account_1: u64, - age_secs_account_2: u64, + struct CompetitiveAccountsTestInput<'a> { + common: WalletsSetup<'a>, + balance_correction_account_1: u128, + balance_correction_account_2: u128, + age_correction_secs_account_1: u64, + age_correction_secs_account_2: u64, } #[derive(Clone, Copy)] - struct CommonInput<'a> { - cw_wallet_balance: u128, + struct WalletsSetup<'a> { wallet_1: &'a Wallet, wallet_2: &'a Wallet, } fn test_competitive_accounts<'a>( test_scenario_name: &str, - inputs: CompetitiveAccountsTestInputFeeder, + inputs: CompetitiveAccountsTestInput, expected_winning_account: &'a Wallet, ) { + let consuming_wallet_balance_minor = 100_000_000_000_000_u128 - 1; + let common_balance_account = 100_000_000_000_000; + let common_age_secs_account = 12000; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: inputs.common.wallet_1.clone(), - balance_wei: inputs.balance_account_1, + balance_wei: inputs.balance_correction_account_1 + common_balance_account, last_paid_timestamp: now - .checked_sub(Duration::from_secs(inputs.age_secs_account_1)) + .checked_sub(Duration::from_secs( + inputs.age_correction_secs_account_1 + common_age_secs_account, + )) .unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: inputs.common.wallet_2.clone(), - balance_wei: inputs.balance_account_2, + balance_wei: inputs.balance_correction_account_2 + common_balance_account, last_paid_timestamp: now - .checked_sub(Duration::from_secs(inputs.age_secs_account_2)) + .checked_sub(Duration::from_secs( + inputs.age_correction_secs_account_2 + common_age_secs_account, + )) .unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = U256::from(inputs.common.cw_wallet_balance); + let consuming_wallet_masq_balance_wei = U256::from(consuming_wallet_balance_minor); let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( @@ -1524,9 +1601,9 @@ mod tests { test_scenario_name, winning_account.wallet, expected_winning_account ); assert_eq!( - winning_account.balance_wei, inputs.common.cw_wallet_balance, + winning_account.balance_wei, consuming_wallet_balance_minor, "{}: expected full cw balance {}, but the account had {}", - test_scenario_name, winning_account.balance_wei, inputs.common.cw_wallet_balance + test_scenario_name, winning_account.balance_wei, consuming_wallet_balance_minor ); assert!( result.is_empty(), @@ -1538,70 +1615,63 @@ mod tests { #[test] fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { - fn merge_test_name_and_study_description(test_name: &str, description: &str) -> String { - format!("{}/{}", test_name, description) + fn merge_main_test_name_with_test_case(description: &str) -> String { + format!( + "adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account_{}", + description + ) } - let test_name = "adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account"; - let consuming_wallet_balance = 100_000_000_000_000_u128 - 1; + let wallet_1 = make_wallet("abcd"); let wallet_2 = make_wallet("cdef"); - let balance_account_1 = 100_000_000_000_000; - let balance_account_2 = 100_000_000_000_000; - let age_secs_account_1 = 12000; - let age_secs_account_2 = 12000; - let common_input = CommonInput { - cw_wallet_balance: consuming_wallet_balance, + let common_input = WalletsSetup { wallet_1: &wallet_1, wallet_2: &wallet_2, }; // scenario A - let first_scenario_name = merge_test_name_and_study_description(test_name, "when equal"); - // first we disqualify the smallest, but also last of that balance, account which is account 2 here, - // therefore only account 1 remains and wins - let expected_winning_account = &wallet_1; + let first_scenario_name = merge_main_test_name_with_test_case("when equal"); + let expected_winning_account = &wallet_2; test_competitive_accounts( &first_scenario_name, - CompetitiveAccountsTestInputFeeder { + CompetitiveAccountsTestInput { common: common_input, - balance_account_1, - balance_account_2, - age_secs_account_1, - age_secs_account_2, + balance_correction_account_1: 0, + balance_correction_account_2: 0, + age_correction_secs_account_1: 0, + age_correction_secs_account_2: 0, }, expected_winning_account, ); // scenario B - let second_scenario_name = - merge_test_name_and_study_description(test_name, "first heavier by balance"); - let expected_winning_account = &wallet_1; + let second_scenario_name = merge_main_test_name_with_test_case("first heavier by balance"); + let expected_winning_account = &wallet_2; test_competitive_accounts( &second_scenario_name, - CompetitiveAccountsTestInputFeeder { + CompetitiveAccountsTestInput { common: common_input, - balance_account_1: balance_account_1 + 1, - balance_account_2, - age_secs_account_1, - age_secs_account_2, + balance_correction_account_1: 1, + balance_correction_account_2: 0, + age_correction_secs_account_1: 0, + age_correction_secs_account_2: 0, }, expected_winning_account, ); // scenario C - let third_scenario_name = - merge_test_name_and_study_description(test_name, "second heavier by age"); - let expected_winning_account = &wallet_2; + let third_scenario_name = merge_main_test_name_with_test_case("second heavier by age"); + let expected_winning_account = &wallet_1; test_competitive_accounts( &third_scenario_name, - CompetitiveAccountsTestInputFeeder { + CompetitiveAccountsTestInput { common: common_input, - balance_account_1, - balance_account_2, - age_secs_account_1, - age_secs_account_2: age_secs_account_2 + 1, + balance_correction_account_1: 0, + balance_correction_account_2: 0, + age_correction_secs_account_1: 1, + age_correction_secs_account_2: 0, }, expected_winning_account, ) @@ -1705,32 +1775,37 @@ mod tests { } fn make_payable_setup_msg_coming_from_blockchain_bridge( - q_payables_gwei_and_cw_balance_gwei_opt: Option<(Vec, u64)>, - transaction_fee_price_opt: Option, + masq_balances_setup_opt: Option, + transaction_fee_per_unit_opt: Option, ) -> PayablePaymentSetup { - let (qualified_payables_gwei, consuming_wallet_masq_gwei) = - q_payables_gwei_and_cw_balance_gwei_opt.unwrap_or((vec![1, 1], u64::MAX)); + let masq_balances_setup = masq_balances_setup_opt.unwrap_or(MasqBalancesTestSetup { + balances_of_accounts_major: vec![1, 1], + cw_balance_major: u64::MAX, + }); + + let accounts_count = masq_balances_setup.balances_of_accounts_major.len(); let ( desired_transaction_fee_price, number_of_payments, estimated_transaction_fee_limit_per_tx, cw_balance_transaction_fee_major, - ) = match transaction_fee_price_opt { + ) = match transaction_fee_per_unit_opt { Some(conditions) => ( conditions.desired_transaction_fee_price_per_major, conditions.number_of_payments, conditions.estimated_fee_limit_per_transaction, conditions.consuming_wallet_transaction_fee_major, ), - None => (120, qualified_payables_gwei.len(), 55_000, u64::MAX), + None => (120, accounts_count, 55_000, u64::MAX), }; - let qualified_payables: Vec<_> = match number_of_payments != qualified_payables_gwei.len() { + let qualified_payables: Vec<_> = match number_of_payments != accounts_count { true => (0..number_of_payments) .map(|idx| make_payable_account(idx as u64)) .collect(), - false => qualified_payables_gwei + false => masq_balances_setup + .balances_of_accounts_major .into_iter() .map(|balance| make_payable_account(balance)) .collect(), @@ -1742,7 +1817,7 @@ mod tests { FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { gas_currency_wei: gwei_to_wei(cw_balance_transaction_fee_major), - masq_tokens_wei: gwei_to_wei(consuming_wallet_masq_gwei), + masq_tokens_wei: gwei_to_wei(masq_balances_setup.cw_balance_major), }, estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, desired_gas_price_gwei: desired_transaction_fee_price, From ca12a89787f7e282affec0fdfc5d0943e351bcd7 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 8 Aug 2023 14:49:04 +0200 Subject: [PATCH 067/250] GH-711: all tests except the ocassional one (not even a piece written) are left secure --- .../age_criterion_calculator.rs | 22 +++- .../payment_adjuster/diagnostics.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 8 +- .../miscellaneous/helper_functions.rs | 25 ++-- node/src/accountant/payment_adjuster/mod.rs | 124 +++++++++++------- .../accountant/payment_adjuster/test_utils.rs | 4 +- 6 files changed, 109 insertions(+), 76 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 9cdf73f1c..e7b6f1a1e 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -72,6 +72,13 @@ impl AgeCriterionCalculator { } } + // This multiplier is meant to push against the growth of the age criterion, + // slowing it down more and more as the time parameter increases. + // The reason is that balance numbers soon get huge but yet are not so unrealistic. + // For a balanced solution, the age criterion formula is designed to progress + // more steeply in the area of rather smaller amounts of seconds, while if + // we move on towards a couple of days, weeks, months and so on, the impact of the parameter + // diminishes fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { let fast_growing_argument = (elapsed_secs as u128) .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) @@ -79,12 +86,13 @@ impl AgeCriterionCalculator { let log_value = Self::nonzero_log_value(fast_growing_argument); - let log_stressed = (log_value).pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) + let log_stressed = log_value.pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; - let final_log_multiplier = (log_stressed - / (divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER)) - .pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); + let divisor_stressed = divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER; + + let final_log_multiplier = + (log_stressed / divisor_stressed).pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); x_or_1(final_log_multiplier) } @@ -191,7 +199,7 @@ mod tests { #[test] fn constants_are_correct() { assert_eq!(AGE_MAIN_EXPONENT, 3); - assert_eq!(AGE_MULTIPLIER, 10); + assert_eq!(AGE_MULTIPLIER, 150); assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); @@ -239,8 +247,8 @@ mod tests { assert_eq!( result, vec![ - 64000000, 531441000, 147197952, 34012224, 4574296, 373248, 32768, 1728, 125, 1, 1, - 1 + 729000000, 4826809000, 1435249152, 308915776, 40353607, 3511808, 287496, 21952, + 1331, 1, 1, 1 ] ) } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index e53d1f18e..6d820f0fb 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -152,7 +152,7 @@ pub mod formulas_progressive_characteristics { use std::sync::{Mutex, Once}; use thousands::Separable; - pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; + pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; //mutex should be fine for debugging, no need for mut static static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index ebb6af7d8..0a91b25ac 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -117,10 +117,10 @@ pub fn before_and_after_debug_msg( pub fn log_info_for_disqualified_account(logger: &Logger, account: &DisqualifiedPayableAccount) { info!( - logger, - "Consuming wallet low in MASQ balance. Recently qualified \ - payable for wallet {} will not be paid as the consuming wallet handles to provide only {} wei \ - which is not at least more than a half of the original debt {}", + logger, + "Consuming wallet is short of MASQ. Seems unavoidable to disregard payable {} \ + at the moment. Reason is the computed possible payment of {} wei would not be at least half of \ + the original debt {}.", account.wallet, account.proposed_adjusted_balance.separate_with_commas(), account.original_balance.separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index b122fbea3..e953a9ab1 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -534,7 +534,6 @@ mod tests { }) .collect::>(); - eprintln!("results {:?}", results); let initial_accounts_order_from_the_seeds_iter = initial_accounts_order_from_the_seeds.iter().enumerate(); let coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs = results @@ -549,37 +548,37 @@ mod tests { .sorted_by(|(a_idx, _, _), (b_idx, _, _)| Ord::cmp(&b_idx, &a_idx)) .map(|(_, coeff, criteria_sum)| (coeff, criteria_sum)) .collect::>(); - //to preserve easy visual checks + //to enable an easy visual check #[rustfmt::skip] fn expected_result() -> Vec<(u128, u128)> { vec![ ( 100000000000000000000000000000000000000, - 3337514568138519074931415968855 + 3337685069315260840795395456195 ), ( 100000000000000000000000000000000000, - 2977068138519074931415968855 + 3147569315260840795395456195 ), ( 100000000000000000000000000000000000, - 2968604285622712478129675136 + 3032031628421209321195830846 ), ( - 10000000000000000000000000000000, - 879662486510538526960128 + 100000000000000000000000000000000, + 8388546281691944888584197 ), ( - 1000000000000000000000000000000, - 43211890301705270704000 + 10000000000000000000000000000000, + 428973298027210119732866 ), ( - 1000000000000000000000000000000, - 13327534955520000000000 + 10000000000000000000000000000000, + 137300730946560000000000 ), ( 100000000000000000000000000000000000, - 2962501520859680498325341824 + 2962514007434791591273391962 ) ] } @@ -888,7 +887,7 @@ mod tests { let subject = make_initialized_subject(now, None, None); // when criteria are applied the collection will get sorted and will not necessarily have to match the initial order let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(accounts); - eprintln!("wallets in order {:?}", wallets_in_order); + (criteria_and_accounts, wallets_in_order) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 7e479be39..9e35fa1d7 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -377,16 +377,18 @@ impl PaymentAdjusterReal { log_info_for_disqualified_account(&self.logger, &disqualified); if remaining.is_empty() { - // Meaning that the processing reached a niche and the performed combination - // eliminated all accounts even though there was at least one debt at - // the beginning that we could have paid out. - // The preceding check for the need of an adjustment is supposed to prevent vast - // majory of such situations. We wouldn't have proceeded to that adjustment - // at all if we'd had a catch. + // We've reached a niche. The performed combination has eliminated all + // accounts in spite of the initial mechanism, that tried to weight + // whether it was possible to return minimally one adjusted account for + // the payments, was pushing hard to the edge, it actually did not manage + // to get it right. Being here means the prevention of possibly unnecessary + // computation has failed. // - // we want to avoid another iteration; probably wouldn't be fatal, but it's better - // to be certain about the behavior than letting it go on - todo!() + // To get out of here we want to be gentle and avoid another iteration + // (probably wouldn't be fatal though). Anyway, it's a win pf the choice for + // well determined behavior instead of letting the situation unwind down to + // waters we don't exactly know. + return (vec![], vec![]); } vec![] @@ -681,7 +683,7 @@ mod tests { use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ - AdjustmentIterationResult, DisqualifiedPayableAccount, + AdjustmentIterationResult, DisqualifiedPayableAccount, SpecialTreatment, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; use crate::accountant::payment_adjuster::test_utils::{ @@ -1084,14 +1086,14 @@ mod tests { } #[test] - fn an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first( + fn an_account_never_becomes_outweighed_and_needing_more_than_the_cw_balance_has_because_disqualified_accounts_need_to_be_eliminated_first( ) { - // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test either. - // This test answers what is happening when the cw MASQ balance cannot cover the outweighed accounts at the first try but if this condition holds it also means - // that there will be another account in the set that will meet the requirements for the disqualified one. - // With enough money, the other attached account doesn't need to be picked for the disqualification which means the concern about outweighed account that - // couldn't have been paid in its full volume turns out groundless. Meaning also the algorithm that knocks the balance in full size down to these accounts - // is not going to lead to just differently formed balance insufficient, found at the time of executing the transactions + // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test at all. + // This test answers what is happening when the cw MASQ balance cannot cover the outweighed accounts at the first try but if there are outweighed accounts + // some other accounts must be also around of which one would definitely be heading to disqualification. + // With enough money, the other account might not need to meet disqualification which means the concern about an outweighed account that + // could not be paid by its full volume turns out to be groundless. Also meaning, that the algorithm strives not to cause another form of + // insufficient, different from that one we had at the beginning const SECONDS_IN_3_DAYS: u64 = 259_200; let test_name = "an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first"; @@ -1137,13 +1139,38 @@ mod tests { }; let expected_disqualified_account = DisqualifiedPayableAccount { wallet: wallet_2, - proposed_adjusted_balance: 7_871_319_192, + proposed_adjusted_balance: 890_785_846, original_balance: balance_2, }; assert_eq!(disqualified, expected_disqualified_account); assert_eq!(remaining, vec![account_1]) } + #[test] + fn there_are_safe_doors_out_if_we_have_a_disqualified_account_while_no_the_remaining_accounts_are_empty( + ) { + init_test_logging(); + let test_name = "there_are_safe_doors_out_if_we_have_a_disqualified_account_while_no_the_remaining_accounts_are_empty"; + let mut subject = + make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); + let insignificant_account = DisqualifiedPayableAccount { + wallet: make_wallet("abc"), + proposed_adjusted_balance: 123, + original_balance: 600, + }; + let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { + special_case: SpecialTreatment::TreatInsignificantAccount(insignificant_account), + remaining: vec![], + }; + + let (here_decided_accounts, downstream_decided_accounts) = + subject.resolve_iteration_result(adjustment_iteration_result); + + assert!(here_decided_accounts.is_empty()); + assert!(downstream_decided_accounts.is_empty()); + TestLogHandler::new().exists_log_matching(&format!("INFO: {test_name}: .*unavoidable to disregard payable 0x0000000000000000000000000000000000616263")); + } + #[test] fn loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations( ) { @@ -1186,15 +1213,15 @@ mod tests { //because the proposed final balances all all way lower than (at least) the half of the original balances assert_eq!(result.accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { - format!("INFO: {test_name}: Consuming wallet low in MASQ balance. Recently qualified \ - payable for wallet {wallet} will not be paid as the consuming wallet handles to provide only {\ - proposed_adjusted_balance_in_this_iteration} wei which is not at least more than a half of \ - the original debt {}", (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR / 2).separate_with_commas()) + format!("INFO: {test_name}: Consuming wallet is short of MASQ. Seems unavoidable to \ + disregard payable {wallet} at the moment. Reason is the computed possible payment of {\ + proposed_adjusted_balance_in_this_iteration} wei would not be at least half of the original \ + debt {}", (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas()) }; let log_handler = TestLogHandler::new(); // notice that the proposals grow by dropping one disqualified account in each iteration log_handler.exists_log_containing(&expected_log( - "0x000000000000000000000000000000626c616832", + "0x000000000000000000000000000000626c616830", 333, )); log_handler.exists_log_containing(&expected_log( @@ -1202,7 +1229,7 @@ mod tests { 499, )); log_handler.exists_log_containing(&expected_log( - "0x000000000000000000000000000000626c616830", + "0x000000000000000000000000000000626c616832", 999, )); } @@ -1220,21 +1247,21 @@ mod tests { }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 6_666_666_666_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + balance_wei: 6_000_000_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 6_000_000_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + wallet: make_wallet("ghi"), + balance_wei: 6_666_666_666_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let accounts_sum: u128 = - 4_444_444_444_444_444_444 + 6_666_666_666_000_000_000 + 6_000_000_000_000_000_000; + 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; let consuming_wallet_masq_balance_wei = U256::from(accounts_sum - 2_000_000_000_000_000_000); let setup_msg = PayablePaymentSetup { @@ -1260,15 +1287,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { - balance_wei: 3_895_912_927_516_778_963, + balance_wei: 3_918_231_688_187_775_576, ..account_1 }; let account_2_adjusted = PayableAccount { - balance_wei: 5_833_507_422_574_361_619, + balance_wei: 5_921_593_128_688_275_336, ..account_2 }; let account_3_adjusted = PayableAccount { - balance_wei: 5_381_690_760_353_303_862, + balance_wei: 5_271_286_293_568_393_532, ..account_3 }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] @@ -1287,12 +1314,12 @@ mod tests { |Successfully Adjusted Original | Adjusted | -|0x0000000000000000000000000000000000646566 6666666666000000000 -| 5833507422574361619 -|0x000000000000000000000000000000000067686b 6000000000000000000 -| 5381690760353303862 +|0x0000000000000000000000000000000000646566 6000000000000000000 +| 5921593128688275336 +|0x0000000000000000000000000000000000676869 6666666666000000000 +| 5271286293568393532 |0x0000000000000000000000000000000000616263 4444444444444444444 -| 3895912927516778963" +| 3918231688187775576" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1316,7 +1343,7 @@ mod tests { pending_payable_opt: None, }; let account_3 = PayableAccount { - wallet: make_wallet("ghk"), + wallet: make_wallet("ghi"), balance_wei: 222_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, @@ -1364,7 +1391,7 @@ mod tests { | |0x0000000000000000000000000000000000646566 333000000000000 | 333000000000000 -|0x000000000000000000000000000000000067686b 222000000000000 +|0x0000000000000000000000000000000000676869 222000000000000 | 222000000000000 | |Ruled Out in Favor of the Others Original @@ -1447,10 +1474,9 @@ mod tests { } #[test] - fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts( - ) { + fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count() { init_test_logging(); - let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count_through_outweighed_accounts"; + let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count"; let now = SystemTime::now(); let wallet_1 = make_wallet("def"); // account to be adjusted up to maximum @@ -1521,10 +1547,10 @@ mod tests { context_id: 234 }) ); - TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Consuming wallet \ - low in MASQ balance. Recently qualified payable for wallet 0x00000000000000000000000000000\ - 0000067686b will not be paid as the consuming wallet handles to provide only 73,839,651,271 \ - wei which is not at least more than a half of the original debt 600,000,000")); + TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Consuming wallet is \ + short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ + at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ + would not be at least half of the original debt 600,000,000,000")); } struct CompetitiveAccountsTestInput<'a> { @@ -1696,7 +1722,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - let wallet_3 = make_wallet("ghk"); + let wallet_3 = make_wallet("ghi"); let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); let account_3 = PayableAccount { wallet: wallet_3.clone(), @@ -1751,7 +1777,7 @@ mod tests { |Successfully Adjusted Original | Adjusted | -|0x000000000000000000000000000000000067686b 333000000000000 +|0x0000000000000000000000000000000000676869 333000000000000 | 300000000000000 | |Ruled Out in Favor of the Others Original diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 666179c3e..b8d66b9ad 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -20,14 +20,14 @@ lazy_static! { pub fn make_initialized_subject( now: SystemTime, - cw_masq_balance_opt: Option, + cw_masq_balance_minor_opt: Option, logger_opt: Option, ) -> PaymentAdjusterReal { PaymentAdjusterReal { inner: Box::new(PaymentAdjusterInnerReal::new( now, None, - cw_masq_balance_opt.unwrap_or(0), + cw_masq_balance_minor_opt.unwrap_or(0), )), logger: logger_opt.unwrap_or(Logger::new("test")), } From 6640e05c7d6fd160a5774e900c74e80d67e5de4e Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 9 Aug 2023 12:36:18 +0200 Subject: [PATCH 068/250] GH-711: todos have been planted for error cases --- node/src/accountant/mod.rs | 9 +- .../payment_adjuster/adjustment_runners.rs | 11 +- .../miscellaneous/helper_functions.rs | 42 +++-- node/src/accountant/payment_adjuster/mod.rs | 148 +++++++++++------- node/src/accountant/scanners/mod.rs | 6 +- .../scanners/scan_mid_procedures.rs | 2 +- node/src/accountant/test_utils.rs | 18 ++- 7 files changed, 147 insertions(+), 89 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 0fd9b358f..9fdcf290c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -650,9 +650,14 @@ impl Accountant { Ok(Either::Left(finalized_msg)) => finalized_msg, Ok(Either::Right(unaccepted_msg)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 - self.scanners + match self + .scanners .payable .exacting_payments_instructions(unaccepted_msg) + { + Ok(instructions) => todo!(), + Err(e) => todo!(), + } } Err(_e) => todo!("be completed by GH-711"), }; @@ -1482,7 +1487,7 @@ mod tests { let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_params(&adjust_payments_params_arc) - .adjust_payments_result(adjusted_payments_instructions); + .adjust_payments_result(Ok(adjusted_payments_instructions)); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 42f216648..9edaa49e1 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -2,7 +2,7 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; pub trait AdjustmentRunner { @@ -18,7 +18,10 @@ pub trait AdjustmentRunner { pub struct MasqAndTransactionFeeRunner {} impl AdjustmentRunner for MasqAndTransactionFeeRunner { - type ReturnType = Either, Vec>; + type ReturnType = Result< + Either, Vec>, + PaymentAdjusterError, + >; fn adjust( &self, @@ -35,10 +38,10 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { None => (), }; - Either::Left( + Ok(Either::Left( payment_adjuster .propose_adjustment_recursively(accounts_with_individual_criteria_sorted), - ) + )) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e953a9ab1..49ae6581f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, ResolutionAfterFullyDetermined, }; -use crate::accountant::payment_adjuster::{diagnostics, AnalysisError}; +use crate::accountant::payment_adjuster::{diagnostics, AnalysisError, PaymentAdjusterError}; use itertools::Itertools; use std::iter::successors; use thousands::Separable; @@ -37,10 +37,10 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn analyze_potential_adjustment_feasibility( +pub fn assess_potential_masq_adjustment_feasibility( accounts: &[&PayableAccount], cw_masq_balance_minor: u128, -) -> Result<(), AnalysisError> { +) -> Result<(), PaymentAdjusterError> { let largest_account = find_largest_debt_account_generic(accounts, |account| account.balance_wei); eprintln!( @@ -53,10 +53,12 @@ pub fn analyze_potential_adjustment_feasibility( { Ok(()) } else { - Err(AnalysisError::RiskOfAdjustmentWithTooLowBalances { - cw_masq_balance_minor, - number_of_accounts: accounts.len(), - }) //TODO think later if you wanna carry the info about count, we could fetch it at a different place too + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowBalances { + cw_masq_balance_minor, + number_of_accounts: accounts.len(), + }, + )) //TODO think later if you wanna carry the info about count, we could fetch it at a different place too } } @@ -342,7 +344,7 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - analyze_potential_adjustment_feasibility, compute_fraction_preventing_mul_coeff, + assess_potential_masq_adjustment_feasibility, compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, log_10, log_2, possibly_outweighed_accounts_fold_guts, ExhaustionStatus, @@ -352,7 +354,7 @@ mod tests { use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::AnalysisError; + use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -374,8 +376,10 @@ mod tests { let accounts_in_expected_format = original_accounts.iter().collect::>(); - let result = - analyze_potential_adjustment_feasibility(&accounts_in_expected_format, cw_masq_balance); + let result = assess_potential_masq_adjustment_feasibility( + &accounts_in_expected_format, + cw_masq_balance, + ); assert_eq!(result, Ok(())) } @@ -422,15 +426,19 @@ mod tests { let accounts_in_expected_format = original_accounts.iter().collect::>(); - let result = - analyze_potential_adjustment_feasibility(&accounts_in_expected_format, cw_masq_balance); + let result = assess_potential_masq_adjustment_feasibility( + &accounts_in_expected_format, + cw_masq_balance, + ); assert_eq!( result, - Err(AnalysisError::RiskOfAdjustmentWithTooLowBalances { - cw_masq_balance_minor: cw_masq_balance, - number_of_accounts: 3 - }) + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowBalances { + cw_masq_balance_minor: cw_masq_balance, + number_of_accounts: 3 + } + )) ) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 9e35fa1d7..a7439a029 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -36,10 +36,11 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, - exhaust_cw_balance_totally, find_largest_debt_account_generic, - list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, - rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, + assess_potential_masq_adjustment_feasibility, compute_fraction_preventing_mul_coeff, + criteria_total, cut_back_by_excessive_transaction_fee, exhaust_cw_balance_totally, + find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, + possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, + sum_as, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -65,13 +66,13 @@ pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, - ) -> Result, AnalysisError>; + ) -> Result, PaymentAdjusterError>; fn adjust_payments( &mut self, setup: AwaitedAdjustment, now: SystemTime, - ) -> OutcomingPaymentsInstructions; + ) -> Result; declare_as_any!(); } @@ -85,7 +86,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, - ) -> Result, AnalysisError> { + ) -> Result, PaymentAdjusterError> { let qualified_payables = msg.qualified_payables.as_slice(); let this_stage_data = match msg .this_stage_data_opt @@ -117,8 +118,9 @@ impl PaymentAdjuster for PaymentAdjusterReal { .masq_tokens_wei .as_u128(), ) { - true => Ok(Some(Adjustment::MasqToken)), - false => Ok(None), + Ok(true) => Ok(Some(Adjustment::MasqToken)), + Ok(false) => Ok(None), + Err(e) => todo!(), } } @@ -126,7 +128,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { &mut self, setup: AwaitedAdjustment, now: SystemTime, - ) -> OutcomingPaymentsInstructions { + ) -> Result { let msg = setup.original_setup_msg; let qualified_payables: Vec = msg.qualified_payables; let response_skeleton_opt = msg.response_skeleton_opt; @@ -144,7 +146,10 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = self.run_adjustment(qualified_payables); + let adjusted_accounts = match self.run_adjustment(qualified_payables) { + Ok(adjusted_accounts) => adjusted_accounts, + Err(e) => todo!(), + }; debug!( self.logger, @@ -152,10 +157,10 @@ impl PaymentAdjuster for PaymentAdjusterReal { before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); - OutcomingPaymentsInstructions { + Ok(OutcomingPaymentsInstructions { accounts: adjusted_accounts, response_skeleton_opt, - } + }) } implement_as_any!(); @@ -179,7 +184,7 @@ impl PaymentAdjusterReal { tech_info: &FinancialAndTechDetails, required_tx_count: usize, logger: &Logger, - ) -> Result, AnalysisError> { + ) -> Result, PaymentAdjusterError> { let transaction_fee_required_per_transaction_major = tech_info.estimated_gas_limit_per_transaction as u128 * tech_info.desired_gas_price_gwei as u128; @@ -197,10 +202,12 @@ impl PaymentAdjusterReal { if max_doable_tx_count_u16 == 0 { let cw_balance = wei_to_gwei(available_balance_minor); let per_transaction_requirement = transaction_fee_required_per_transaction_major as u64; - Err(AnalysisError::BalanceBelowSingleTxFee { - per_transaction_requirement, - cw_balance_major: cw_balance, - }) + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::BalanceBelowSingleTxFee { + per_transaction_requirement, + cw_balance_major: cw_balance, + }, + )) } else if max_doable_tx_count_u16 >= required_tx_count_u16 { Ok(None) } else { @@ -224,12 +231,11 @@ impl PaymentAdjusterReal { ) } - //TODO we should check there is at least one half of the smallest payment fn check_need_of_masq_balances_adjustment( logger: &Logger, payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, - consuming_wallet_balance_minor: u128, - ) -> bool { + cw_masq_balance_minor: u128, + ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { Either::Left(accounts) => accounts.iter().collect(), Either::Right(criteria_and_accounts) => criteria_and_accounts @@ -242,15 +248,23 @@ impl PaymentAdjusterReal { account.balance_wei }); - if consuming_wallet_balance_minor >= required_masq_sum { - false + if cw_masq_balance_minor >= required_masq_sum { + Ok(false) } else { - log_adjustment_by_masq_required( - logger, - required_masq_sum, - consuming_wallet_balance_minor, - ); - true + match assess_potential_masq_adjustment_feasibility( + &qualified_payables, + cw_masq_balance_minor, + ) { + Ok(()) => { + log_adjustment_by_masq_required( + logger, + required_masq_sum, + cw_masq_balance_minor, + ); + Ok(true) + } + Err(e) => todo!(), + } } } @@ -272,16 +286,20 @@ impl PaymentAdjusterReal { self.inner = Box::new(inner); } - fn run_adjustment(&mut self, qualified_accounts: Vec) -> Vec { + fn run_adjustment( + &mut self, + qualified_accounts: Vec, + ) -> Result, PaymentAdjusterError> { match self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, MasqAndTransactionFeeRunner {}, ) { - Either::Left(non_exhausted_accounts) => exhaust_cw_balance_totally( + Ok(Either::Left(non_exhausted_accounts)) => Ok(exhaust_cw_balance_totally( non_exhausted_accounts, self.inner.original_cw_masq_balance(), - ), - Either::Right(finalized_accounts) => finalized_accounts, + )), + Ok(Either::Right(finalized_accounts)) => Ok(finalized_accounts), + Err(e) => todo!(), //TODO think about a question mark } } @@ -308,7 +326,10 @@ impl PaymentAdjusterReal { &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, already_known_count_limit: u16, - ) -> Either, Vec> { + ) -> Result< + Either, Vec>, + PaymentAdjusterError, + > { let transaction_fee_affordable_weighted_accounts = cut_back_by_excessive_transaction_fee( accounts_with_individual_criteria, already_known_count_limit, @@ -318,16 +339,20 @@ impl PaymentAdjusterReal { Either::Right(&transaction_fee_affordable_weighted_accounts), self.inner.unallocated_cw_masq_balance(), ) { - true => { - let result_awaiting_verification = self - .propose_adjustment_recursively(transaction_fee_affordable_weighted_accounts); - Either::Left(result_awaiting_verification) - } - false => { - let finalized_accounts = - rebuild_accounts(transaction_fee_affordable_weighted_accounts); - Either::Right(finalized_accounts) - } + Ok(definite_answer) => match definite_answer { + true => { + let result_awaiting_verification = self.propose_adjustment_recursively( + transaction_fee_affordable_weighted_accounts, + ); + Ok(Either::Left(result_awaiting_verification)) + } + false => { + let finalized_accounts = + rebuild_accounts(transaction_fee_affordable_weighted_accounts); + Ok(Either::Right(finalized_accounts)) + } + }, + Err(e) => todo!(), } } @@ -665,6 +690,11 @@ pub enum Adjustment { PriorityTransactionFee { affordable_transaction_count: u16 }, } +#[derive(Debug, PartialEq, Eq)] +pub enum PaymentAdjusterError { + AnalysisError(AnalysisError), +} + #[derive(Debug, PartialEq, Eq)] pub enum AnalysisError { BalanceBelowSingleTxFee { @@ -690,7 +720,7 @@ mod tests { make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterReal, + Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -864,10 +894,12 @@ mod tests { assert_eq!( result, - Err(AnalysisError::BalanceBelowSingleTxFee { - per_transaction_requirement: 55_000 * 100, - cw_balance_major: 54_000 * 100 - }) + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::BalanceBelowSingleTxFee { + per_transaction_requirement: 55_000 * 100, + cw_balance_major: 54_000 * 100 + } + )) ); } @@ -1041,6 +1073,7 @@ mod tests { qualified_payables.clone(), MasqAndTransactionFeeRunner {}, ) + .unwrap() .left() .unwrap(); @@ -1208,7 +1241,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); //because the proposed final balances all all way lower than (at least) the half of the original balances assert_eq!(result.accounts, vec![]); @@ -1283,7 +1316,7 @@ mod tests { adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { @@ -1373,7 +1406,7 @@ mod tests { }, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); assert_eq!( result, @@ -1453,7 +1486,7 @@ mod tests { }, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); // account_1, being the least important one, was eliminated as there wouldn't be enough // the transaction fee balance for all of them @@ -1530,7 +1563,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); let expected_accounts = { let account_1_adjusted = PayableAccount { @@ -1618,7 +1651,10 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let mut result = subject.adjust_payments(adjustment_setup, now).accounts; + let mut result = subject + .adjust_payments(adjustment_setup, now) + .unwrap() + .accounts; let winning_account = result.remove(0); assert_eq!( @@ -1756,7 +1792,7 @@ mod tests { }, }; - let mut result = subject.adjust_payments(adjustment_setup, now); + let mut result = subject.adjust_payments(adjustment_setup, now).unwrap(); let only_account = result.accounts.remove(0); assert_eq!( diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index b5fbd2033..79914a695 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -275,9 +275,11 @@ impl PayableScannerMiddleProcedures for PayableScanner { fn exacting_payments_instructions( &mut self, setup: AwaitedAdjustment, - ) -> OutcomingPaymentsInstructions { + ) -> Result { let now = SystemTime::now(); - self.payment_adjuster.adjust_payments(setup, now) + self.payment_adjuster + .adjust_payments(setup, now) + .map_err(|e| todo!()) } } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index e374a2707..7740ec6d4 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -25,7 +25,7 @@ pub trait PayableScannerMiddleProcedures { fn exacting_payments_instructions( &mut self, _setup: AwaitedAdjustment, - ) -> OutcomingPaymentsInstructions { + ) -> Result { intentionally_blank!() } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index a3225cef5..bfcb0fda9 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -13,7 +13,7 @@ use crate::accountant::database_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; -use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; +use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError}; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::{ AwaitedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, @@ -1393,16 +1393,17 @@ impl PayableThresholdsGaugeMock { pub struct PaymentAdjusterMock { search_for_indispensable_adjustment_params: Arc>>, search_for_indispensable_adjustment_results: - RefCell, AnalysisError>>>, + RefCell, PaymentAdjusterError>>>, adjust_payments_params: Arc>>, - adjust_payments_results: RefCell>, + adjust_payments_results: + RefCell>>, } impl PaymentAdjuster for PaymentAdjusterMock { fn search_for_indispensable_adjustment( &self, msg: &PayablePaymentSetup, - ) -> Result, AnalysisError> { + ) -> Result, PaymentAdjusterError> { self.search_for_indispensable_adjustment_params .lock() .unwrap() @@ -1416,7 +1417,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { &mut self, setup: AwaitedAdjustment, now: SystemTime, - ) -> OutcomingPaymentsInstructions { + ) -> Result { self.adjust_payments_params .lock() .unwrap() @@ -1436,7 +1437,7 @@ impl PaymentAdjusterMock { pub fn search_for_indispensable_adjustment_result( self, - result: Result, AnalysisError>, + result: Result, PaymentAdjusterError>, ) -> Self { self.search_for_indispensable_adjustment_results .borrow_mut() @@ -1452,7 +1453,10 @@ impl PaymentAdjusterMock { self } - pub fn adjust_payments_result(self, result: OutcomingPaymentsInstructions) -> Self { + pub fn adjust_payments_result( + self, + result: Result, + ) -> Self { self.adjust_payments_results.borrow_mut().push(result); self } From ec65537daf177839bf96d7f0b9bcc21eeb51912c Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 10 Aug 2023 13:39:04 +0200 Subject: [PATCH 069/250] GH-711: interim commit --- node/src/accountant/mod.rs | 259 ++++++++++++++++-- .../payment_adjuster/adjustment_runners.rs | 6 +- .../accountant/payment_adjuster/log_fns.rs | 2 +- .../miscellaneous/helper_functions.rs | 20 +- node/src/accountant/payment_adjuster/mod.rs | 209 +++++++++----- node/src/accountant/scanners/mod.rs | 28 +- .../scanners/payable_scan_setup_msgs.rs | 2 +- .../scanners/scan_mid_procedures.rs | 8 +- node/src/blockchain/blockchain_bridge.rs | 24 +- node/src/sub_lib/blockchain_bridge.rs | 6 +- 10 files changed, 426 insertions(+), 138 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 9fdcf290c..070596312 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -15,9 +15,9 @@ use std::cell::{Ref, RefCell}; use masq_lib::messages::{ QueryResults, ScanType, UiFinancialStatistics, UiPayableAccount, UiReceivableAccount, - UiScanRequest, + UiScanRequest, UiScanResponse, }; -use masq_lib::ui_gateway::{MessageBody, MessagePath}; +use masq_lib::ui_gateway::{MessageBody, MessagePath, MessageTarget}; use crate::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoError}; use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; @@ -646,27 +646,55 @@ impl Accountant { } fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) { - let bb_instructions = match self.scanners.payable.try_softly(msg) { - Ok(Either::Left(finalized_msg)) => finalized_msg, - Ok(Either::Right(unaccepted_msg)) => { + let response_skeleton_opt = msg.response_skeleton_opt; + let payment_adjustment_result = match self + .scanners + .payable + .try_skipping_payment_adjustment(msg, &self.logger) + { + Ok(Either::Left(transformed_message)) => Ok(transformed_message), + Ok(Either::Right(adjustment_info)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 match self .scanners .payable - .exacting_payments_instructions(unaccepted_msg) + .perform_payment_adjustment(adjustment_info) { - Ok(instructions) => todo!(), + Ok(instructions) => Ok(instructions), Err(e) => todo!(), } } - Err(_e) => todo!("be completed by GH-711"), + Err(e) => Err(()), }; - self.outcoming_payments_instructions_sub_opt - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(bb_instructions) - .expect("BlockchainBridge is dead") - //TODO implement send point for ScanError; be completed by GH-711 + + match payment_adjustment_result { + Ok(blockchain_bridge_instructions) => self + .outcoming_payments_instructions_sub_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(blockchain_bridge_instructions) + .expect("BlockchainBridge is dead"), + Err(e) => { + error!( + self.logger, + "Payable scanner could not finish. Preventing to settle already mature \ + payables could have serious consequences" + ); + self.scanners.payable.mark_as_ended(&self.logger); + if let Some(response_skeleton) = response_skeleton_opt { + self.ui_message_sub_opt + .as_ref() + .expect("UI gateway unbound") + .try_send(NodeToUiMessage { + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), + }) + .expect("UI gateway is dead") + } else { + todo!("eliminate me") + } + } + } } fn handle_financials(&self, msg: &UiFinancialsRequest, client_id: u64, context_id: u64) { @@ -1012,7 +1040,7 @@ mod tests { use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::database_access_objects::utils::from_time_t; use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; - use crate::accountant::payment_adjuster::Adjustment; + use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjusterError}; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, StageData, }; @@ -1072,7 +1100,9 @@ mod tests { use masq_lib::test_utils::logging::TestLogHandler; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::ui_gateway::MessagePath::Conversation; - use masq_lib::ui_gateway::{MessageBody, MessagePath, NodeFromUiMessage, NodeToUiMessage}; + use masq_lib::ui_gateway::{ + MessageBody, MessagePath, MessageTarget, NodeFromUiMessage, NodeToUiMessage, + }; use std::any::TypeId; use std::ops::{Add, Sub}; use std::sync::Arc; @@ -1404,17 +1434,17 @@ mod tests { let subject_addr = subject.start(); let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); - let system = System::new("test"); + let system = System::new(test_name); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u32::MAX), - masq_tokens_wei: U256::from(u32::MAX), + transaction_fee_minor: u32::MAX as u128, + masq_tokens_minor: u32::MAX as u128, }, estimated_gas_limit_per_transaction: 112_000, - desired_gas_price_gwei: 123, + desired_transaction_fee_price_major: 123, }, )), response_skeleton_opt: Some(ResponseSkeleton { @@ -1497,17 +1527,17 @@ mod tests { let subject_addr = subject.start(); let account_1 = make_payable_account(111_111); let account_2 = make_payable_account(222_222); - let system = System::new("test"); + let system = System::new(test_name); let consuming_balances_and_qualified_payments = PayablePaymentSetup { qualified_payables: vec![account_1.clone(), account_2.clone()], this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u32::MAX), - masq_tokens_wei: U256::from(150_000_000_000_u64), + transaction_fee_minor: u32::MAX as u128, + masq_tokens_minor: 150_000_000_000, }, estimated_gas_limit_per_transaction: 110_000, - desired_gas_price_gwei: 0, + desired_transaction_fee_price_major: 0, }, )), response_skeleton_opt: Some(response_skeleton), @@ -1541,6 +1571,187 @@ mod tests { ); } + #[test] + fn payment_adjuster_spit_out_an_error_from_the_insolvency_check() { + init_test_logging(); + let test_name = "payment_adjuster_spit_out_an_error_from_the_insolvency_check"; + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let blockchain_bridge_recipient = blockchain_bridge.start().recipient(); + let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); + let ui_gateway_recipient = ui_gateway + .system_stop_conditions(match_every_type_id!(NodeToUiMessage)) + .start() + .recipient(); + let mut subject = AccountantBuilder::default().build(); + let response_skeleton = ResponseSkeleton { + client_id: 12, + context_id: 55, + }; + let cw_transaction_fee_balance_major = 123_u64; + let one_transaction_transaction_fee_requirement = 60 * 55_000; + let payment_adjuster = PaymentAdjusterMock::default() + .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 1, + per_transaction_requirement_minor: one_transaction_transaction_fee_requirement, + cw_transaction_fee_balance_minor: gwei_to_wei(cw_transaction_fee_balance_major), + }, + ))); + let payable_scanner = PayableScannerBuilder::new() + .payment_adjuster(payment_adjuster) + .build(); + subject.outcoming_payments_instructions_sub_opt = Some(blockchain_bridge_recipient); + subject.ui_message_sub_opt = Some(ui_gateway_recipient); + subject.logger = Logger::new(test_name); + subject.scanners.payable = Box::new(payable_scanner); + let scan_started_at = SystemTime::now(); + subject.scanners.payable.mark_as_started(scan_started_at); + let subject_addr = subject.start(); + let account_1 = make_payable_account(111_111); + let system = System::new(test_name); + let consuming_balances_and_qualified_payments = PayablePaymentSetup { + qualified_payables: vec![account_1], + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: wei_to_gwei::( + cw_transaction_fee_balance_major, + ), + masq_tokens_minor: 150_000_000_000, + }, + estimated_gas_limit_per_transaction: 55_000, + desired_transaction_fee_price_major: 60, + }, + )), + response_skeleton_opt: Some(response_skeleton), + }; + let assertion_message = AssertionsMessage { + assertions: Box::new(|accountant: &mut Accountant| { + assert_eq!(accountant.scanners.payable.scan_started_at(), None) // meaning the scan wa called off + }), + }; + + subject_addr + .try_send(consuming_balances_and_qualified_payments) + .unwrap(); + + subject_addr.try_send(assertion_message).unwrap(); + assert_eq!(system.run(), 0); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + assert_eq!(blockchain_bridge_recording.len(), 0); + let ui_gateway_recording = ui_gateway_recording_arc.lock().unwrap(); + let response_to_user: &NodeToUiMessage = ui_gateway_recording.get_record(0); + assert_eq!( + response_to_user, + &NodeToUiMessage { + target: MessageTarget::ClientId(12), + body: UiScanResponse {}.tmb(55) + } + ); + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ + suffice for a payment for any of the recently qualified payables by the larger part of each. \ + Please fund your consuming wallet in order to avoid being banned from your creditors. Failure \ + reason: Found less transaction fee balance than required by one payment. Number of canceled \ + payments: 1. Transaction fee for a single account: 3,300,000 wei. Current consuming wallet \ + balance: 123,000,000,000 wei.")); + log_handler + .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); + log_handler.exists_log_containing(&format!( + "ERROR: {test_name}: Payable scanner could not finish. \ + Preventing to settle already mature payables could have serious consequences" + )); + } + + #[test] + fn payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment() { + todo!("write a test body for these two tests, only payment adjuster should go in, and the logs should stay outside"); + init_test_logging(); + let test_name = + "payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let blockchain_bridge_recipient = blockchain_bridge.start().recipient(); + let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); + let ui_gateway_recipient = ui_gateway + .system_stop_conditions(match_every_type_id!(NodeToUiMessage)) + .start() + .recipient(); + let mut subject = AccountantBuilder::default().build(); + let response_skeleton = ResponseSkeleton { + client_id: 12, + context_id: 55, + }; + let cw_transaction_fee_balance_major = 123_u64; + let one_transaction_transaction_fee_requirement = 60 * 55_000; + let payment_adjuster = PaymentAdjusterMock::default() + .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) + .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsUnexpectedlyEliminated)); + let payable_scanner = PayableScannerBuilder::new() + .payment_adjuster(payment_adjuster) + .build(); + subject.outcoming_payments_instructions_sub_opt = Some(blockchain_bridge_recipient); + subject.ui_message_sub_opt = Some(ui_gateway_recipient); + subject.logger = Logger::new(test_name); + subject.scanners.payable = Box::new(payable_scanner); + let scan_started_at = SystemTime::now(); + subject.scanners.payable.mark_as_started(scan_started_at); + let subject_addr = subject.start(); + let account_1 = make_payable_account(111_111); + let system = System::new(test_name); + let consuming_balances_and_qualified_payments = PayablePaymentSetup { + qualified_payables: vec![account_1], + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: wei_to_gwei::( + cw_transaction_fee_balance_major, + ), + masq_tokens_minor: 150_000_000_000, + }, + estimated_gas_limit_per_transaction: 55_000, + desired_transaction_fee_price_major: 60, + }, + )), + response_skeleton_opt: Some(response_skeleton), + }; + let assertion_message = AssertionsMessage { + assertions: Box::new(|accountant: &mut Accountant| { + assert_eq!(accountant.scanners.payable.scan_started_at(), None) // meaning the scan wa called off + }), + }; + + subject_addr + .try_send(consuming_balances_and_qualified_payments) + .unwrap(); + + subject_addr.try_send(assertion_message).unwrap(); + assert_eq!(system.run(), 0); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + assert_eq!(blockchain_bridge_recording.len(), 0); + let ui_gateway_recording = ui_gateway_recording_arc.lock().unwrap(); + let response_to_user: &NodeToUiMessage = ui_gateway_recording.get_record(0); + assert_eq!( + response_to_user, + &NodeToUiMessage { + target: MessageTarget::ClientId(12), + body: UiScanResponse {}.tmb(55) + } + ); + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ + suffice for a payment for any of the recently qualified payables by the larger part of each. \ + Please fund your consuming wallet in order to avoid being banned from your creditors. Failure \ + reason: Found less transaction fee balance than required by one payment. Number of canceled \ + payments: 1. Transaction fee for a single account: 3,300,000 wei. Current consuming wallet \ + balance: 123,000,000,000 wei.")); + log_handler + .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); + log_handler.exists_log_containing(&format!( + "ERROR: {test_name}: Payable scanner could not finish. \ + Preventing to settle already mature payables could have serious consequences" + )); + } + #[test] fn scan_pending_payables_request() { let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 9edaa49e1..24659a6fd 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -79,10 +79,10 @@ mod tests { let cw_balance = 9_000_000; let details = FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(0), - masq_tokens_wei: U256::from(cw_balance), + transaction_fee_minor: 0, + masq_tokens_minor: cw_balance, }, - desired_gas_price_gwei: 30, + desired_transaction_fee_price_major: 30, estimated_gas_limit_per_transaction: 100, }; let wallet_1 = make_wallet("abc"); diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 0a91b25ac..b36e85eaf 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -151,7 +151,7 @@ pub fn log_insufficient_transaction_fee_balance( their count.", this_stage_data .consuming_wallet_balances - .masq_tokens_wei + .masq_tokens_minor .separate_with_commas(), required_transactions_count, limiting_count diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 49ae6581f..8002ddbf2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -10,6 +10,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::{diagnostics, AnalysisError, PaymentAdjusterError}; +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use itertools::Itertools; use std::iter::successors; use thousands::Separable; @@ -43,10 +44,7 @@ pub fn assess_potential_masq_adjustment_feasibility( ) -> Result<(), PaymentAdjusterError> { let largest_account = find_largest_debt_account_generic(accounts, |account| account.balance_wei); - eprintln!( - "largest: {:?}, cw balance {}", - largest_account, cw_masq_balance_minor - ); + if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor <= cw_masq_balance_minor @@ -54,11 +52,12 @@ pub fn assess_potential_masq_adjustment_feasibility( Ok(()) } else { Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowBalances { - cw_masq_balance_minor, + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { number_of_accounts: accounts.len(), + cw_masq_balance_minor: todo!(), + //TODO think later if you wanna carry the info about count, we could fetch it at a different place too }, - )) //TODO think later if you wanna carry the info about count, we could fetch it at a different place too + )) } } @@ -356,6 +355,7 @@ mod tests { }; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; @@ -434,9 +434,9 @@ mod tests { assert_eq!( result, Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowBalances { - cw_masq_balance_minor: cw_masq_balance, - number_of_accounts: 3 + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: 3, + cw_masq_balance_minor: cw_masq_balance } )) ) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index a7439a029..8f9c8e100 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -49,7 +49,7 @@ use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; +use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; use itertools::Either; use itertools::Either::{Left, Right}; @@ -57,9 +57,11 @@ use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; use std::collections::HashMap; +use std::fmt::{Display, Formatter}; use std::iter::once; use std::ops::Not; use std::time::SystemTime; +use thousands::Separable; use web3::types::U256; pub trait PaymentAdjuster { @@ -113,10 +115,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { match Self::check_need_of_masq_balances_adjustment( &self.logger, Either::Left(qualified_payables), - this_stage_data - .consuming_wallet_balances - .masq_tokens_wei - .as_u128(), + this_stage_data.consuming_wallet_balances.masq_tokens_minor, ) { Ok(true) => Ok(Some(Adjustment::MasqToken)), Ok(false) => Ok(None), @@ -187,11 +186,11 @@ impl PaymentAdjusterReal { ) -> Result, PaymentAdjusterError> { let transaction_fee_required_per_transaction_major = tech_info.estimated_gas_limit_per_transaction as u128 - * tech_info.desired_gas_price_gwei as u128; + * tech_info.desired_transaction_fee_price_major as u128; - let transaction_fee_required_minor: U256 = + let transaction_fee_required_minor: u128 = gwei_to_wei(transaction_fee_required_per_transaction_major); - let available_balance_minor = tech_info.consuming_wallet_balances.gas_currency_wei; + let available_balance_minor = tech_info.consuming_wallet_balances.transaction_fee_minor; let max_doable_tx_count_loose_boundaries = available_balance_minor / transaction_fee_required_minor; let (max_doable_tx_count_u16, required_tx_count_u16) = @@ -200,12 +199,12 @@ impl PaymentAdjusterReal { required_tx_count, ); if max_doable_tx_count_u16 == 0 { - let cw_balance = wei_to_gwei(available_balance_minor); let per_transaction_requirement = transaction_fee_required_per_transaction_major as u64; Err(PaymentAdjusterError::AnalysisError( - AnalysisError::BalanceBelowSingleTxFee { - per_transaction_requirement, - cw_balance_major: cw_balance, + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: todo!(), + per_transaction_requirement_minor: per_transaction_requirement, + cw_transaction_fee_balance_minor: available_balance_minor, }, )) } else if max_doable_tx_count_u16 >= required_tx_count_u16 { @@ -222,7 +221,7 @@ impl PaymentAdjusterReal { } fn process_big_nums_and_look_out_for_ceiling( - max_doable_tx_count: U256, + max_doable_tx_count: u128, required_tx_count: usize, ) -> (u16, u16) { ( @@ -280,7 +279,7 @@ impl PaymentAdjusterReal { } => Some(affordable_transaction_count), Adjustment::MasqToken => None, }; - let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_wei.as_u128(); + let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_minor; let inner = PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); self.inner = Box::new(inner); @@ -693,20 +692,44 @@ pub enum Adjustment { #[derive(Debug, PartialEq, Eq)] pub enum PaymentAdjusterError { AnalysisError(AnalysisError), + AllAccountsUnexpectedlyEliminated, } #[derive(Debug, PartialEq, Eq)] pub enum AnalysisError { - BalanceBelowSingleTxFee { - per_transaction_requirement: u64, - cw_balance_major: u64, + NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: usize, + per_transaction_requirement_minor: u64, + cw_transaction_fee_balance_minor: u128, }, - RiskOfAdjustmentWithTooLowBalances { - cw_masq_balance_minor: u128, + RiskOfAdjustmentWithTooLowMASQBalances { number_of_accounts: usize, + cw_masq_balance_minor: u128, }, } +impl Display for PaymentAdjusterError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + PaymentAdjusterError::AnalysisError(analysis_error) => match analysis_error{ + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx {number_of_accounts,per_transaction_requirement_minor, cw_transaction_fee_balance_minor } => + write!(f, "Found less transaction fee balance than required by one payment. \ + Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ + Current consuming wallet balance: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), + cw_transaction_fee_balance_minor.separate_with_commas()), + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances {number_of_accounts, cw_masq_balance_minor } => + write!(f,"Analysis has revealed possibly unfavourable payment adjustments given \ + the anticipated resulting payments from the limited resources. Please, provide more \ + funds instead. Number of canceled payments: {}. Current consuming wallet balance: \ + {} wei of MASQ", number_of_accounts.separate_with_commas(),cw_masq_balance_minor.separate_with_commas()) + }, + PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!(f, "Despite \ + the preliminary analysis had expected a possibility to compute some executable \ + adjusted payments, the algorithm eventually rejected them all") + } + } +} + #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; @@ -751,7 +774,7 @@ mod tests { let _ = result.inner.unallocated_cw_masq_balance(); } - struct MasqBalancesTestSetup { + struct PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: Vec, cw_balance_major: u64, } @@ -765,7 +788,7 @@ mod tests { subject.logger = logger; //masq balance > payments let msg_1 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(MasqBalancesTestSetup { + Some(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![85, 14], cw_balance_major: 100, }), @@ -773,7 +796,7 @@ mod tests { ); //masq balance = payments let msg_2 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(MasqBalancesTestSetup { + Some(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![85, 15], cw_balance_major: 100, }), @@ -782,21 +805,21 @@ mod tests { //transaction_fee balance > payments let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(TransactionFeeTestConditions { + Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 111, number_of_payments: 5, estimated_fee_limit_per_transaction: 53_000, - consuming_wallet_transaction_fee_major: (111 * 5 * 53_000) + 1, + cw_transaction_fee_balance_major: (111 * 5 * 53_000) + 1, }), ); //transaction_fee balance = payments let msg_4 = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(TransactionFeeTestConditions { + Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, number_of_payments: 6, estimated_fee_limit_per_transaction: 53_000, - consuming_wallet_transaction_fee_major: 100 * 6 * 53_000, + cw_transaction_fee_balance_major: 100 * 6 * 53_000, }), ); @@ -820,7 +843,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(MasqBalancesTestSetup { + Some(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![85, 16], cw_balance_major: 100, }), @@ -848,11 +871,11 @@ mod tests { let number_of_payments = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( None, - Some(TransactionFeeTestConditions { + Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, number_of_payments, estimated_fee_limit_per_transaction: 55_000, - consuming_wallet_transaction_fee_major: 100 * 3 * 55_000 - 1, + cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, }), ); @@ -881,12 +904,15 @@ mod tests { let subject = PaymentAdjusterReal::new(); let number_of_payments = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - None, - Some(TransactionFeeTestConditions { + Some(PayableBalancesAndCWBAlanceInTest { + balances_of_accounts_major: vec![123], + cw_balance_major: 444, + }), + Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, number_of_payments, estimated_fee_limit_per_transaction: 55_000, - consuming_wallet_transaction_fee_major: 54_000 * 100, + cw_transaction_fee_balance_major: 54_000 * 100, }), ); @@ -895,23 +921,61 @@ mod tests { assert_eq!( result, Err(PaymentAdjusterError::AnalysisError( - AnalysisError::BalanceBelowSingleTxFee { - per_transaction_requirement: 55_000 * 100, - cw_balance_major: 54_000 * 100 + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 1, + per_transaction_requirement_minor: 55_000 * 100 * 1_000_000_000, + cw_transaction_fee_balance_minor: 54_000 * 100 * 1_000_000_000 } )) ); } + #[test] + fn payment_adjuster_error_implements_display() { + vec![ + ( + PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: 5, + cw_masq_balance_minor: 333_000_000, + }, + ), + "Analysis has revealed possibly unfavourable payment adjustments given \ + the anticipated resulting payments from the limited resources. Please, \ + provide more funds instead. Number of canceled payments: 5. Current \ + consuming wallet balance: 333,000,000 wei of MASQ", + ), + ( + PaymentAdjusterError::AnalysisError( + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 4, + per_transaction_requirement_minor: 70_000_000_000_000, + cw_transaction_fee_balance_minor: 90_000, + }, + ), + "Found less transaction fee balance than required by one payment. \ + Number of canceled payments: 4. Transaction fee for a single account: \ + 70,000,000,000,000 wei. Current consuming wallet balance: 90,000 wei", + ), + ( + PaymentAdjusterError::AllAccountsUnexpectedlyEliminated, + "Despite the preliminary analysis had expected a possibility to compute some \ + executable adjusted payments, the algorithm eventually rejected them all", + ), + ] + .into_iter() + .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) + } + #[test] fn there_is_u16_max_ceiling_for_doable_txs_count() { let result = [-3_i8, -1, 0, 1, 10] .into_iter() .map(|correction| { if correction < 0 { - U256::from(u16::MAX - (correction.abs() as u16)) + (u16::MAX - correction.abs() as u16) as u128 } else { - U256::from(u16::MAX as u32 + correction as u32) + u16::MAX as u128 + correction as u128 } }) .map(|max_doable_txs_count_u256| { @@ -944,7 +1008,7 @@ mod tests { .map(|required_tx_count_usize| { let (_, required_tx_count) = PaymentAdjusterReal::process_big_nums_and_look_out_for_ceiling( - U256::from(123), + 123, required_tx_count_usize, ); required_tx_count @@ -1227,11 +1291,11 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u32::MAX), - masq_tokens_wei: U256::from(cw_masq_balance), + transaction_fee_minor: u32::MAX as u128, + masq_tokens_minor: cw_masq_balance, }, estimated_gas_limit_per_transaction: 70_000, - desired_gas_price_gwei: 120, + desired_transaction_fee_price_major: 120, }, )), response_skeleton_opt: None, @@ -1295,18 +1359,17 @@ mod tests { subject.logger = Logger::new(test_name); let accounts_sum: u128 = 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; - let consuming_wallet_masq_balance_wei = - U256::from(accounts_sum - 2_000_000_000_000_000_000); + let consuming_wallet_masq_balance_minor = accounts_sum - 2_000_000_000_000_000_000; let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u32::MAX), - masq_tokens_wei: consuming_wallet_masq_balance_wei, + transaction_fee_minor: u32::MAX as u128, + masq_tokens_minor: consuming_wallet_masq_balance_minor, }, estimated_gas_limit_per_transaction: 70_000, - desired_gas_price_gwei: 120, + desired_transaction_fee_price_major: 120, }, )), response_skeleton_opt: None, @@ -1389,12 +1452,12 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: U256::from(10_u128.pow(22)), + masq_tokens_minor: 10_u128.pow(22), }, estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, + desired_transaction_fee_price_major: 24, }, )), response_skeleton_opt: None, @@ -1469,12 +1532,12 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + masq_tokens_minor: consuming_wallet_masq_balance, }, estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, + desired_transaction_fee_price_major: 24, }, )), response_skeleton_opt, @@ -1539,18 +1602,18 @@ mod tests { let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let consuming_wallet_masq_balance_wei = U256::from(333_000_000_000_u64 + 50_000_000_000); + let consuming_wallet_masq_balance_minor = 333_000_000_000 + 50_000_000_000; let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_000_000_000_000_000_000_000_000_u128), + transaction_fee_minor: 5_000_000_000_000_000_000_000_000_u128, //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: consuming_wallet_masq_balance_wei, + masq_tokens_minor: consuming_wallet_masq_balance_minor, }, estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, + desired_transaction_fee_price_major: 24, }, )), response_skeleton_opt: Some(ResponseSkeleton { @@ -1631,17 +1694,17 @@ mod tests { }; let qualified_payables = vec![account_1.clone(), account_2.clone()]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = U256::from(consuming_wallet_balance_minor); + let consuming_wallet_masq_balance_wei = consuming_wallet_balance_minor; let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(u128::MAX), - masq_tokens_wei: consuming_wallet_masq_balance_wei, + transaction_fee_minor: u128::MAX, + masq_tokens_minor: consuming_wallet_masq_balance_wei, }, estimated_gas_limit_per_transaction: 55_000, - desired_gas_price_gwei: 150, + desired_transaction_fee_price_major: 150, }, )), response_skeleton_opt: None, @@ -1775,12 +1838,12 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: U256::from(5_544_000_000_000_000_u128 - 1), + transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_wei: U256::from(consuming_wallet_masq_balance), + masq_tokens_minor: consuming_wallet_masq_balance, }, estimated_gas_limit_per_transaction: 77_000, - desired_gas_price_gwei: 24, + desired_transaction_fee_price_major: 24, }, )), response_skeleton_opt: None, @@ -1829,18 +1892,18 @@ mod tests { todo!("write this occasional test") } - struct TransactionFeeTestConditions { + struct TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: u64, number_of_payments: usize, estimated_fee_limit_per_transaction: u64, - consuming_wallet_transaction_fee_major: u64, + cw_transaction_fee_balance_major: u64, } fn make_payable_setup_msg_coming_from_blockchain_bridge( - masq_balances_setup_opt: Option, - transaction_fee_per_unit_opt: Option, + masq_balances_opt: Option, + transaction_fee_conditions_opt: Option, ) -> PayablePaymentSetup { - let masq_balances_setup = masq_balances_setup_opt.unwrap_or(MasqBalancesTestSetup { + let masq_balances_setup = masq_balances_opt.unwrap_or(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![1, 1], cw_balance_major: u64::MAX, }); @@ -1852,12 +1915,12 @@ mod tests { number_of_payments, estimated_transaction_fee_limit_per_tx, cw_balance_transaction_fee_major, - ) = match transaction_fee_per_unit_opt { + ) = match transaction_fee_conditions_opt { Some(conditions) => ( conditions.desired_transaction_fee_price_per_major, conditions.number_of_payments, conditions.estimated_fee_limit_per_transaction, - conditions.consuming_wallet_transaction_fee_major, + conditions.cw_transaction_fee_balance_major, ), None => (120, accounts_count, 55_000, u64::MAX), }; @@ -1878,11 +1941,11 @@ mod tests { this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - gas_currency_wei: gwei_to_wei(cw_balance_transaction_fee_major), - masq_tokens_wei: gwei_to_wei(masq_balances_setup.cw_balance_major), + transaction_fee_minor: gwei_to_wei(cw_balance_transaction_fee_major), + masq_tokens_minor: gwei_to_wei(masq_balances_setup.cw_balance_major), }, estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, - desired_gas_price_gwei: desired_transaction_fee_price, + desired_transaction_fee_price_major: desired_transaction_fee_price, }, )), response_skeleton_opt: None, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 79914a695..acc181056 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -255,24 +255,36 @@ impl Scanner for PayableScanner { } impl PayableScannerMiddleProcedures for PayableScanner { - fn try_softly( + fn try_skipping_payment_adjustment( &self, msg: PayablePaymentSetup, - ) -> Result, String> { + logger: &Logger, + ) -> Result, ()> { match self .payment_adjuster .search_for_indispensable_adjustment(&msg) { - Ok(None) => Ok(Either::Left(OutcomingPaymentsInstructions { - accounts: msg.qualified_payables, - response_skeleton_opt: msg.response_skeleton_opt, - })), + Ok(None) => { + let blockchain_bridge_instructions = OutcomingPaymentsInstructions { + accounts: msg.qualified_payables, + response_skeleton_opt: msg.response_skeleton_opt, + }; + + Ok(Either::Left(blockchain_bridge_instructions)) + } Ok(Some(adjustment)) => Ok(Either::Right(AwaitedAdjustment::new(msg, adjustment))), - Err(_e) => todo!("be implemented with GH-711"), + Err(e) => { + warning!(logger, + "The current balances do not suffice for a payment for any of the recently qualified \ + payables by the larger part of each. Please fund your consuming wallet in order \ + to avoid being banned from your creditors. Failure reason: {}.", e); + + Err(()) + } } } - fn exacting_payments_instructions( + fn perform_payment_adjustment( &mut self, setup: AwaitedAdjustment, ) -> Result { diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs index 3e9765289..6ccab5abe 100644 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -31,7 +31,7 @@ pub enum StageData { #[derive(Debug, PartialEq, Eq, Clone)] pub struct FinancialAndTechDetails { pub consuming_wallet_balances: ConsumingWalletBalances, - pub desired_gas_price_gwei: u64, + pub desired_transaction_fee_price_major: u64, //rather technical stuff below pub estimated_gas_limit_per_transaction: u64, } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 7740ec6d4..9cd4da1e3 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -6,6 +6,7 @@ use crate::accountant::scanners::Scanner; use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use actix::Message; use itertools::Either; +use masq_lib::logger::Logger; pub trait PayableScannerWithMiddleProcedures: Scanner + PayableScannerMiddleProcedures @@ -16,13 +17,14 @@ where } pub trait PayableScannerMiddleProcedures { - fn try_softly( + fn try_skipping_payment_adjustment( &self, _msg: PayablePaymentSetup, - ) -> Result, String> { + _logger: &Logger, + ) -> Result, ()> { intentionally_blank!() } - fn exacting_payments_instructions( + fn perform_payment_adjustment( &mut self, _setup: AwaitedAdjustment, ) -> Result { diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index dcd542aa2..7c0316e20 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -267,7 +267,7 @@ impl BlockchainBridge { // TODO rewrite this into a batch call as soon as GH-629 gets into master // New card GH-707 will address this let gas_balance = match self.blockchain_interface.get_gas_balance(consuming_wallet) { - Ok(gas_balance) => gas_balance, + Ok(gas_balance) => todo!(), Err(e) => { return Err(format!( "Did not find out gas balance of the consuming wallet: {:?}", @@ -279,7 +279,7 @@ impl BlockchainBridge { .blockchain_interface .get_token_balance(consuming_wallet) { - Ok(token_balance) => token_balance, + Ok(token_balance) => todo!(), Err(e) => { return Err(format!( "Did not find out token balance of the consuming wallet: {:?}", @@ -289,8 +289,8 @@ impl BlockchainBridge { }; let consuming_wallet_balances = { ConsumingWalletBalances { - gas_currency_wei: gas_balance, - masq_tokens_wei: token_balance, + transaction_fee_minor: gas_balance, + masq_tokens_minor: token_balance, } }; let desired_gas_price_gwei = self @@ -302,7 +302,7 @@ impl BlockchainBridge { let this_stage_data = StageData::FinancialAndTechDetails(FinancialAndTechDetails { consuming_wallet_balances, estimated_gas_limit_per_transaction, - desired_gas_price_gwei, + desired_transaction_fee_price_major: desired_gas_price_gwei, }); let msg = PayablePaymentSetup::from((msg, this_stage_data)); @@ -663,17 +663,17 @@ mod tests { let get_gas_balance_params_arc = Arc::new(Mutex::new(vec![])); let get_token_balance_params_arc = Arc::new(Mutex::new(vec![])); let (accountant, _, accountant_recording_arc) = make_recorder(); - let gas_balance = U256::from(4455); - let token_balance = U256::from(112233); + let gas_balance = 4455; + let token_balance = 112233; let wallet_balances_found = ConsumingWalletBalances { - gas_currency_wei: gas_balance, - masq_tokens_wei: token_balance, + transaction_fee_minor: gas_balance, + masq_tokens_minor: token_balance, }; let blockchain_interface = BlockchainInterfaceMock::default() .get_gas_balance_params(&get_gas_balance_params_arc) - .get_gas_balance_result(Ok(gas_balance)) + .get_gas_balance_result(Ok(U256::from(gas_balance))) .get_token_balance_params(&get_token_balance_params_arc) - .get_token_balance_result(Ok(token_balance)) + .get_token_balance_result(Ok(U256::from(token_balance))) .estimated_gas_limit_per_payable_result(51_546); let consuming_wallet = make_paying_wallet(b"somewallet"); let persistent_configuration = @@ -733,7 +733,7 @@ mod tests { StageData::FinancialAndTechDetails(FinancialAndTechDetails { consuming_wallet_balances: wallet_balances_found, estimated_gas_limit_per_transaction: 51_546, - desired_gas_price_gwei: 146, + desired_transaction_fee_price_major: 146, }), ) .into(); diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index c7536e82a..5f27a626e 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -54,10 +54,10 @@ impl SkeletonOptHolder for OutcomingPaymentsInstructions { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConsumingWalletBalances { - pub gas_currency_wei: U256, - pub masq_tokens_wei: U256, //TODO should be u128 instead + pub transaction_fee_minor: u128, + pub masq_tokens_minor: u128, } #[cfg(test)] From d98a58207fbfb91ae31badc2d53b4ab235916b97 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 10 Aug 2023 17:33:40 +0200 Subject: [PATCH 070/250] GH-711: tests in accountant/mod.rs has the right count and do what they should do --- node/src/accountant/mod.rs | 167 ++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 22 +-- node/src/accountant/scanners/mod.rs | 57 +++--- .../scanners/scan_mid_procedures.rs | 11 +- node/src/accountant/test_utils.rs | 8 +- node/src/blockchain/blockchain_bridge.rs | 4 +- 6 files changed, 138 insertions(+), 131 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 070596312..e6170de80 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -59,6 +59,7 @@ use actix::Message; use actix::Recipient; use itertools::Either; use itertools::Itertools; +use log::logger; use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; use masq_lib::messages::UiFinancialsResponse; @@ -207,7 +208,7 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) -> Self::Result { - self.handle_payable_payment_setup(msg) + self.handle_payable_payment_setup_msg(msg) } } @@ -645,36 +646,36 @@ impl Accountant { }) } - fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) { + fn handle_payable_payment_setup_msg(&mut self, msg: PayablePaymentSetup) { let response_skeleton_opt = msg.response_skeleton_opt; - let payment_adjustment_result = match self + let blockchain_bridge_instructions_opt = match self .scanners .payable .try_skipping_payment_adjustment(msg, &self.logger) { - Ok(Either::Left(transformed_message)) => Ok(transformed_message), - Ok(Either::Right(adjustment_info)) => { + Some(Either::Left(transformed_message)) => Some(transformed_message), + Some(Either::Right(adjustment_info)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 match self .scanners .payable - .perform_payment_adjustment(adjustment_info) + .perform_payment_adjustment(adjustment_info, &self.logger) { - Ok(instructions) => Ok(instructions), - Err(e) => todo!(), + Some(instructions) => Some(instructions), + None => None, } } - Err(e) => Err(()), + None => None, }; - match payment_adjustment_result { - Ok(blockchain_bridge_instructions) => self + match blockchain_bridge_instructions_opt { + Some(blockchain_bridge_instructions) => self .outcoming_payments_instructions_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(blockchain_bridge_instructions) .expect("BlockchainBridge is dead"), - Err(e) => { + None => { error!( self.logger, "Payable scanner could not finish. Preventing to settle already mature \ @@ -686,12 +687,10 @@ impl Accountant { .as_ref() .expect("UI gateway unbound") .try_send(NodeToUiMessage { - target: MessageTarget::ClientId(response_skeleton.client_id), - body: UiScanResponse {}.tmb(response_skeleton.context_id), + target: MessageTarget::ClientId(11), + body: UiScanResponse {}.tmb(22), }) .expect("UI gateway is dead") - } else { - todo!("eliminate me") } } } @@ -1044,7 +1043,7 @@ mod tests { use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, StageData, }; - use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; + use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, @@ -1554,7 +1553,7 @@ mod tests { let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); assert_eq!( cwbqp_msg, - AwaitedAdjustment { + PreparedAdjustment { original_setup_msg: consuming_balances_and_qualified_payments, adjustment: Adjustment::MasqToken } @@ -1571,10 +1570,11 @@ mod tests { ); } - #[test] - fn payment_adjuster_spit_out_an_error_from_the_insolvency_check() { - init_test_logging(); - let test_name = "payment_adjuster_spit_out_an_error_from_the_insolvency_check"; + fn test_handling_payment_adjuster_error( + test_name: &str, + payment_adjuster: PaymentAdjusterMock, + cw_transaction_fee_balance_major_opt: Option, + ) { let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let blockchain_bridge_recipient = blockchain_bridge.start().recipient(); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); @@ -1587,16 +1587,6 @@ mod tests { client_id: 12, context_id: 55, }; - let cw_transaction_fee_balance_major = 123_u64; - let one_transaction_transaction_fee_requirement = 60 * 55_000; - let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: 1, - per_transaction_requirement_minor: one_transaction_transaction_fee_requirement, - cw_transaction_fee_balance_minor: gwei_to_wei(cw_transaction_fee_balance_major), - }, - ))); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -1615,7 +1605,7 @@ mod tests { FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { transaction_fee_minor: wei_to_gwei::( - cw_transaction_fee_balance_major, + cw_transaction_fee_balance_major_opt.unwrap_or(u64::MAX), ), masq_tokens_minor: 150_000_000_000, }, @@ -1648,6 +1638,23 @@ mod tests { body: UiScanResponse {}.tmb(55) } ); + } + + #[test] + fn payment_adjuster_spit_out_an_error_from_the_insolvency_check() { + init_test_logging(); + let test_name = "payment_adjuster_spit_out_an_error_from_the_insolvency_check"; + let payment_adjuster = PaymentAdjusterMock::default() + .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 1, + per_transaction_requirement_minor: 60 * 55_000, + cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), + }, + ))); + + test_handling_payment_adjuster_error(test_name, payment_adjuster, Some(123)); + let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ suffice for a payment for any of the recently qualified payables by the larger part of each. \ @@ -1665,88 +1672,70 @@ mod tests { #[test] fn payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment() { - todo!("write a test body for these two tests, only payment adjuster should go in, and the logs should stay outside"); init_test_logging(); let test_name = "payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; - let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let blockchain_bridge_recipient = blockchain_bridge.start().recipient(); - let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); - let ui_gateway_recipient = ui_gateway - .system_stop_conditions(match_every_type_id!(NodeToUiMessage)) - .start() - .recipient(); - let mut subject = AccountantBuilder::default().build(); - let response_skeleton = ResponseSkeleton { - client_id: 12, - context_id: 55, - }; - let cw_transaction_fee_balance_major = 123_u64; - let one_transaction_transaction_fee_requirement = 60 * 55_000; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsUnexpectedlyEliminated)); + + test_handling_payment_adjuster_error(test_name, payment_adjuster, None); + + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!("WARN: {test_name}: Payment adjustment did not \ + succeed arranging executable payments burdened by balance insufficiency. Please fund your \ + consuming wallet in order to avoid being banned from your creditors. Failure reason: Despite \ + the preliminary analysis had expected a possibility to compute some executable adjusted \ + payments, the algorithm eventually rejected them all")); + log_handler + .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); + log_handler.exists_log_containing(&format!( + "ERROR: {test_name}: Payable scanner could not finish. \ + Preventing to settle already mature payables could have serious consequences" + )); + } + + #[test] + fn error_from_payment_adjuster_is_not_sent_by_an_exclusive_message_to_ui_if_not_manually_requested( + ) { + init_test_logging(); + let test_name = "error_from_payment_adjuster_is_not_sent_by_an_exclusive_message_to_ui_if_not_manually_requested"; + let mut subject = AccountantBuilder::default().build(); + let payment_adjuster = PaymentAdjusterMock::default() + .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 20, + per_transaction_requirement_minor: 40_000_000_000, + cw_transaction_fee_balance_minor: 123, + }, + ))); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); - subject.outcoming_payments_instructions_sub_opt = Some(blockchain_bridge_recipient); - subject.ui_message_sub_opt = Some(ui_gateway_recipient); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); - let scan_started_at = SystemTime::now(); - subject.scanners.payable.mark_as_started(scan_started_at); - let subject_addr = subject.start(); let account_1 = make_payable_account(111_111); let system = System::new(test_name); - let consuming_balances_and_qualified_payments = PayablePaymentSetup { + let msg = PayablePaymentSetup { qualified_payables: vec![account_1], this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: wei_to_gwei::( - cw_transaction_fee_balance_major, - ), + transaction_fee_minor: u128::MAX, masq_tokens_minor: 150_000_000_000, }, estimated_gas_limit_per_transaction: 55_000, desired_transaction_fee_price_major: 60, }, )), - response_skeleton_opt: Some(response_skeleton), - }; - let assertion_message = AssertionsMessage { - assertions: Box::new(|accountant: &mut Accountant| { - assert_eq!(accountant.scanners.payable.scan_started_at(), None) // meaning the scan wa called off - }), + response_skeleton_opt: None, }; - subject_addr - .try_send(consuming_balances_and_qualified_payments) - .unwrap(); + subject.handle_payable_payment_setup_msg(msg); - subject_addr.try_send(assertion_message).unwrap(); - assert_eq!(system.run(), 0); - let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); - assert_eq!(blockchain_bridge_recording.len(), 0); - let ui_gateway_recording = ui_gateway_recording_arc.lock().unwrap(); - let response_to_user: &NodeToUiMessage = ui_gateway_recording.get_record(0); - assert_eq!( - response_to_user, - &NodeToUiMessage { - target: MessageTarget::ClientId(12), - body: UiScanResponse {}.tmb(55) - } - ); - let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ - suffice for a payment for any of the recently qualified payables by the larger part of each. \ - Please fund your consuming wallet in order to avoid being banned from your creditors. Failure \ - reason: Found less transaction fee balance than required by one payment. Number of canceled \ - payments: 1. Transaction fee for a single account: 3,300,000 wei. Current consuming wallet \ - balance: 123,000,000,000 wei.")); - log_handler - .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); - log_handler.exists_log_containing(&format!( + // test didn't blow up while the subject was disconnected and the system didn't + // run therefore we didn't attempt to send the NodeUiMessage + TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ Preventing to settle already mature payables could have serious consequences" )); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8f9c8e100..e3b690f67 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -45,7 +45,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; -use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; +use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; @@ -72,7 +72,7 @@ pub trait PaymentAdjuster { fn adjust_payments( &mut self, - setup: AwaitedAdjustment, + setup: PreparedAdjustment, now: SystemTime, ) -> Result; @@ -125,7 +125,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &mut self, - setup: AwaitedAdjustment, + setup: PreparedAdjustment, now: SystemTime, ) -> Result { let msg = setup.original_setup_msg; @@ -748,7 +748,7 @@ mod tests { use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; - use crate::accountant::scanners::scan_mid_procedures::AwaitedAdjustment; + use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::{ @@ -1300,7 +1300,7 @@ mod tests { )), response_skeleton_opt: None, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::MasqToken, }; @@ -1374,7 +1374,7 @@ mod tests { )), response_skeleton_opt: None, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations }; @@ -1462,7 +1462,7 @@ mod tests { )), response_skeleton_opt: None, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { affordable_transaction_count: 2, @@ -1542,7 +1542,7 @@ mod tests { )), response_skeleton_opt, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { affordable_transaction_count: 2, @@ -1621,7 +1621,7 @@ mod tests { context_id: 234, }), }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::MasqToken, }; @@ -1709,7 +1709,7 @@ mod tests { )), response_skeleton_opt: None, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::MasqToken, }; @@ -1848,7 +1848,7 @@ mod tests { )), response_skeleton_opt: None, }; - let adjustment_setup = AwaitedAdjustment { + let adjustment_setup = PreparedAdjustment { original_setup_msg: setup_msg, adjustment: Adjustment::PriorityTransactionFee { affordable_transaction_count: 2, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index acc181056..881ad0004 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -10,7 +10,7 @@ use crate::accountant::database_access_objects::receivable_dao::ReceivableDao; use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup}; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scan_mid_procedures::{ - AwaitedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, + PreparedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, @@ -259,39 +259,56 @@ impl PayableScannerMiddleProcedures for PayableScanner { &self, msg: PayablePaymentSetup, logger: &Logger, - ) -> Result, ()> { + ) -> Option> { match self .payment_adjuster .search_for_indispensable_adjustment(&msg) { - Ok(None) => { - let blockchain_bridge_instructions = OutcomingPaymentsInstructions { - accounts: msg.qualified_payables, - response_skeleton_opt: msg.response_skeleton_opt, - }; - - Ok(Either::Left(blockchain_bridge_instructions)) - } - Ok(Some(adjustment)) => Ok(Either::Right(AwaitedAdjustment::new(msg, adjustment))), + Ok(adjustment_opt) => match adjustment_opt { + None => { + let blockchain_bridge_instructions = OutcomingPaymentsInstructions { + accounts: msg.qualified_payables, + response_skeleton_opt: msg.response_skeleton_opt, + }; + Some(Either::Left(blockchain_bridge_instructions)) + } + Some(adjustment) => { + let prepared_adjustment = PreparedAdjustment::new(msg, adjustment); + Some((Either::Right(prepared_adjustment))) + } + }, Err(e) => { warning!(logger, - "The current balances do not suffice for a payment for any of the recently qualified \ - payables by the larger part of each. Please fund your consuming wallet in order \ - to avoid being banned from your creditors. Failure reason: {}.", e); + "The current balances do not suffice for a payment for any of the recently qualified \ + payables by the larger part of each. Please fund your consuming wallet in order \ + to avoid being banned from your creditors. Failure reason: {}.", e); - Err(()) + None } } } fn perform_payment_adjustment( &mut self, - setup: AwaitedAdjustment, - ) -> Result { + setup: PreparedAdjustment, + logger: &Logger, + ) -> Option { let now = SystemTime::now(); - self.payment_adjuster - .adjust_payments(setup, now) - .map_err(|e| todo!()) + match self.payment_adjuster.adjust_payments(setup, now) { + Ok(instructions) => Some(instructions), + Err(e) => { + warning!( + logger, + "Payment adjustment did not succeed \ + arranging executable payments burdened by balance insufficiency. \ + Please fund your consuming wallet in order to avoid being banned \ + from your creditors. Failure reason: {}", + e + ); + + None + } + } } } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 9cd4da1e3..8c10d11ae 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -21,24 +21,25 @@ pub trait PayableScannerMiddleProcedures { &self, _msg: PayablePaymentSetup, _logger: &Logger, - ) -> Result, ()> { + ) -> Option> { intentionally_blank!() } fn perform_payment_adjustment( &mut self, - _setup: AwaitedAdjustment, - ) -> Result { + _setup: PreparedAdjustment, + _logger: &Logger, + ) -> Option { intentionally_blank!() } } #[derive(Debug, PartialEq, Eq)] -pub struct AwaitedAdjustment { +pub struct PreparedAdjustment { pub original_setup_msg: PayablePaymentSetup, pub adjustment: Adjustment, } -impl AwaitedAdjustment { +impl PreparedAdjustment { pub fn new(original_setup_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { Self { original_setup_msg, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index bfcb0fda9..feb2ce9ba 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -16,7 +16,7 @@ use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError}; use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; use crate::accountant::scanners::scan_mid_procedures::{ - AwaitedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, + PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, PreparedAdjustment, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{ @@ -1394,7 +1394,7 @@ pub struct PaymentAdjusterMock { search_for_indispensable_adjustment_params: Arc>>, search_for_indispensable_adjustment_results: RefCell, PaymentAdjusterError>>>, - adjust_payments_params: Arc>>, + adjust_payments_params: Arc>>, adjust_payments_results: RefCell>>, } @@ -1415,7 +1415,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &mut self, - setup: AwaitedAdjustment, + setup: PreparedAdjustment, now: SystemTime, ) -> Result { self.adjust_payments_params @@ -1447,7 +1447,7 @@ impl PaymentAdjusterMock { pub fn adjust_payments_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.adjust_payments_params = params.clone(); self diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 7c0316e20..61945ecbb 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -267,7 +267,7 @@ impl BlockchainBridge { // TODO rewrite this into a batch call as soon as GH-629 gets into master // New card GH-707 will address this let gas_balance = match self.blockchain_interface.get_gas_balance(consuming_wallet) { - Ok(gas_balance) => todo!(), + Ok(gas_balance) => u128::try_from(gas_balance).expect("unexpected wealth"), Err(e) => { return Err(format!( "Did not find out gas balance of the consuming wallet: {:?}", @@ -279,7 +279,7 @@ impl BlockchainBridge { .blockchain_interface .get_token_balance(consuming_wallet) { - Ok(token_balance) => todo!(), + Ok(token_balance) => u128::try_from(token_balance).expect("unexpected wealth"), Err(e) => { return Err(format!( "Did not find out token balance of the consuming wallet: {:?}", From 68c9da02e87a2ca916427addcbb7b1364c19a708 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 10 Aug 2023 20:21:18 +0200 Subject: [PATCH 071/250] GH-711: some older tests fixed --- node/src/accountant/mod.rs | 29 +++++++------- .../miscellaneous/helper_functions.rs | 3 +- node/src/accountant/payment_adjuster/mod.rs | 39 +++++++++---------- node/src/accountant/scanners/mod.rs | 2 +- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index e6170de80..ae9de9638 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -653,18 +653,20 @@ impl Accountant { .payable .try_skipping_payment_adjustment(msg, &self.logger) { - Some(Either::Left(transformed_message)) => Some(transformed_message), - Some(Either::Right(adjustment_info)) => { - //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 - match self - .scanners - .payable - .perform_payment_adjustment(adjustment_info, &self.logger) - { - Some(instructions) => Some(instructions), - None => None, + Some(result) => match result { + Either::Left(transformed_message) => Some(transformed_message), + Either::Right(adjustment_info) => { + //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 + match self + .scanners + .payable + .perform_payment_adjustment(adjustment_info, &self.logger) + { + Some(instructions) => Some(instructions), + None => None, + } } - } + }, None => None, }; @@ -687,8 +689,8 @@ impl Accountant { .as_ref() .expect("UI gateway unbound") .try_send(NodeToUiMessage { - target: MessageTarget::ClientId(11), - body: UiScanResponse {}.tmb(22), + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), }) .expect("UI gateway is dead") } @@ -1715,7 +1717,6 @@ mod tests { subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); let account_1 = make_payable_account(111_111); - let system = System::new(test_name); let msg = PayablePaymentSetup { qualified_payables: vec![account_1], this_stage_data_opt: Some(StageData::FinancialAndTechDetails( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8002ddbf2..e9899c8ba 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -54,8 +54,7 @@ pub fn assess_potential_masq_adjustment_feasibility( Err(PaymentAdjusterError::AnalysisError( AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { number_of_accounts: accounts.len(), - cw_masq_balance_minor: todo!(), - //TODO think later if you wanna carry the info about count, we could fetch it at a different place too + cw_masq_balance_minor, }, )) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e3b690f67..6be794264 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -199,11 +199,10 @@ impl PaymentAdjusterReal { required_tx_count, ); if max_doable_tx_count_u16 == 0 { - let per_transaction_requirement = transaction_fee_required_per_transaction_major as u64; Err(PaymentAdjusterError::AnalysisError( AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: todo!(), - per_transaction_requirement_minor: per_transaction_requirement, + number_of_accounts: required_tx_count, + per_transaction_requirement_minor: transaction_fee_required_minor, cw_transaction_fee_balance_minor: available_balance_minor, }, )) @@ -699,7 +698,7 @@ pub enum PaymentAdjusterError { pub enum AnalysisError { NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: usize, - per_transaction_requirement_minor: u64, + per_transaction_requirement_minor: u128, cw_transaction_fee_balance_minor: u128, }, RiskOfAdjustmentWithTooLowMASQBalances { @@ -807,8 +806,8 @@ mod tests { None, Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 111, - number_of_payments: 5, - estimated_fee_limit_per_transaction: 53_000, + number_of_accounts: 5, + estimated_transaction_fee_units_limit_per_transaction: 53_000, cw_transaction_fee_balance_major: (111 * 5 * 53_000) + 1, }), ); @@ -817,8 +816,8 @@ mod tests { None, Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, - number_of_payments: 6, - estimated_fee_limit_per_transaction: 53_000, + number_of_accounts: 6, + estimated_transaction_fee_units_limit_per_transaction: 53_000, cw_transaction_fee_balance_major: 100 * 6 * 53_000, }), ); @@ -868,20 +867,20 @@ mod tests { let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; - let number_of_payments = 3; + let number_of_accounts = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( None, Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, - number_of_payments, - estimated_fee_limit_per_transaction: 55_000, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, }), ); let result = subject.search_for_indispensable_adjustment(&msg); - let expected_limiting_count = number_of_payments as u16 - 1; + let expected_limiting_count = number_of_accounts as u16 - 1; assert_eq!( result, Ok(Some(Adjustment::PriorityTransactionFee { @@ -902,7 +901,7 @@ mod tests { fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_transaction_fee( ) { let subject = PaymentAdjusterReal::new(); - let number_of_payments = 3; + let number_of_accounts = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( Some(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![123], @@ -910,8 +909,8 @@ mod tests { }), Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, - number_of_payments, - estimated_fee_limit_per_transaction: 55_000, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, cw_transaction_fee_balance_major: 54_000 * 100, }), ); @@ -922,7 +921,7 @@ mod tests { result, Err(PaymentAdjusterError::AnalysisError( AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: 1, + number_of_accounts, per_transaction_requirement_minor: 55_000 * 100 * 1_000_000_000, cw_transaction_fee_balance_minor: 54_000 * 100 * 1_000_000_000 } @@ -1894,8 +1893,8 @@ mod tests { struct TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: u64, - number_of_payments: usize, - estimated_fee_limit_per_transaction: u64, + number_of_accounts: usize, + estimated_transaction_fee_units_limit_per_transaction: u64, cw_transaction_fee_balance_major: u64, } @@ -1918,8 +1917,8 @@ mod tests { ) = match transaction_fee_conditions_opt { Some(conditions) => ( conditions.desired_transaction_fee_price_per_major, - conditions.number_of_payments, - conditions.estimated_fee_limit_per_transaction, + conditions.number_of_accounts, + conditions.estimated_transaction_fee_units_limit_per_transaction, conditions.cw_transaction_fee_balance_major, ), None => (120, accounts_count, 55_000, u64::MAX), diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 881ad0004..8d4bd1b21 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -274,7 +274,7 @@ impl PayableScannerMiddleProcedures for PayableScanner { } Some(adjustment) => { let prepared_adjustment = PreparedAdjustment::new(msg, adjustment); - Some((Either::Right(prepared_adjustment))) + Some(Either::Right(prepared_adjustment)) } }, Err(e) => { From b677f54005bf7d640a3e6384cd4fb4ec43fdff00 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 15 Aug 2023 11:33:06 +0200 Subject: [PATCH 072/250] GH-711: error branches todos gone --- node/src/accountant/mod.rs | 4 +- .../payment_adjuster/adjustment_runners.rs | 11 +- .../accountant/payment_adjuster/log_fns.rs | 21 +- .../miscellaneous/helper_functions.rs | 10 +- node/src/accountant/payment_adjuster/mod.rs | 324 ++++++++++++------ node/src/sub_lib/blockchain_bridge.rs | 1 - 6 files changed, 247 insertions(+), 124 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index ae9de9638..1d7043035 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -59,7 +59,6 @@ use actix::Message; use actix::Recipient; use itertools::Either; use itertools::Itertools; -use log::logger; use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; use masq_lib::messages::UiFinancialsResponse; @@ -1933,8 +1932,7 @@ mod tests { } #[test] - //TODO change this name!!!! - fn accountant_sends_asks_blockchain_bridge_about_consuming_wallet_balances_when_qualified_payable_found( + fn accountant_sends_payable_payment_setup_msg_to_blockchain_bridge_when_qualified_payable_found( ) { let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let now = SystemTime::now(); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 24659a6fd..170f355a9 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -26,12 +26,12 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { fn adjust( &self, payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { return payment_adjuster.begin_with_adjustment_by_transaction_fees( - accounts_with_individual_criteria_sorted, + criteria_and_accounts_in_descending_order, limit, ) } @@ -40,7 +40,7 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { Ok(Either::Left( payment_adjuster - .propose_adjustment_recursively(accounts_with_individual_criteria_sorted), + .propose_adjustment_recursively(criteria_and_accounts_in_descending_order), )) } } @@ -53,9 +53,9 @@ impl AdjustmentRunner for MasqOnlyRunner { fn adjust( &self, payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { - payment_adjuster.propose_adjustment_recursively(accounts_with_individual_criteria_sorted) + payment_adjuster.propose_adjustment_recursively(criteria_and_accounts_in_descending_order) } } @@ -71,7 +71,6 @@ mod tests { use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use std::time::{Duration, SystemTime}; - use web3::types::U256; #[test] fn masq_only_adjuster_is_not_meant_to_adjust_also_by_transaction_fee() { diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index b36e85eaf..8a7bc9b53 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -2,7 +2,6 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::DisqualifiedPayableAccount; -use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -141,7 +140,7 @@ pub fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_m pub fn log_insufficient_transaction_fee_balance( logger: &Logger, required_transactions_count: u16, - this_stage_data: &FinancialAndTechDetails, + transaction_fee_minor: u128, limiting_count: u16, ) { warning!( @@ -149,16 +148,21 @@ pub fn log_insufficient_transaction_fee_balance( "Gas amount {} wei cannot cover anticipated fees from sending {} \ transactions. Maximum is {}. The payments need to be adjusted in \ their count.", - this_stage_data - .consuming_wallet_balances - .masq_tokens_minor - .separate_with_commas(), + transaction_fee_minor.separate_with_commas(), required_transactions_count, limiting_count ); info!(logger, "{}", REFILL_RECOMMENDATION) } +pub fn log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient(logger: &Logger) { + error!( + logger, + "Passing successful payment adjustment by the transaction fee, \ + but facing critical scarcity of MASQ balance. Operation will abort." + ) +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::log_fns::REFILL_RECOMMENDATION; @@ -167,9 +171,8 @@ mod tests { fn constants_are_correct() { assert_eq!( REFILL_RECOMMENDATION, - "\ -In order to continue using services of other Nodes and avoid delinquency \ -bans you will need to put more funds into your consuming wallet." + "In order to continue using services of other Nodes and avoid delinquency \ + bans you will need to put more funds into your consuming wallet." ) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e9899c8ba..b8a7f172f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -10,7 +10,6 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::{diagnostics, AnalysisError, PaymentAdjusterError}; -use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use itertools::Itertools; use std::iter::successors; use thousands::Separable; @@ -45,6 +44,8 @@ pub fn assess_potential_masq_adjustment_feasibility( let largest_account = find_largest_debt_account_generic(accounts, |account| account.balance_wei); + //TODO you need to make this better !!!! What if the big one is too big against the other ones? + if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor <= cw_masq_balance_minor @@ -61,16 +62,16 @@ pub fn assess_potential_masq_adjustment_feasibility( } pub fn cut_back_by_excessive_transaction_fee( - weights_and_accounts: Vec<(u128, PayableAccount)>, + weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, limit: u16, ) -> Vec<(u128, PayableAccount)> { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", limit, - weights_and_accounts.len() + weights_and_accounts_in_descending_order.len() ); - weights_and_accounts + weights_and_accounts_in_descending_order .into_iter() .take(limit as usize) .collect() @@ -354,7 +355,6 @@ mod tests { }; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 6be794264..53a659e3d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -10,6 +10,7 @@ mod miscellaneous; mod test_utils; use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::gwei_to_wei; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, MasqAndTransactionFeeRunner, MasqOnlyRunner, }; @@ -25,8 +26,9 @@ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - before_and_after_debug_msg, log_adjustment_by_masq_required, log_info_for_disqualified_account, - log_insufficient_transaction_fee_balance, + before_and_after_debug_msg, log_adjustment_by_masq_required, + log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient, + log_info_for_disqualified_account, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, @@ -46,13 +48,11 @@ use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; -use crate::accountant::{gwei_to_wei, wei_to_gwei}; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; -use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutcomingPaymentsInstructions}; +use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use itertools::Either; -use itertools::Either::{Left, Right}; use masq_lib::logger::Logger; #[cfg(test)] use std::any::Any; @@ -93,33 +93,32 @@ impl PaymentAdjuster for PaymentAdjusterReal { let this_stage_data = match msg .this_stage_data_opt .as_ref() - .expect("always some at this level") + .expect("always some at this stage") { StageData::FinancialAndTechDetails(details) => details, }; - match Self::determine_transactions_count_limit_by_transaction_fee( - &this_stage_data, - qualified_payables.len(), - &self.logger, - ) { - Ok(None) => (), - Ok(Some(affordable_transaction_count)) => { - return Ok(Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count, - })) - } - Err(e) => return Err(e), - }; + let transaction_fee_adjustment_opt = + Self::determine_transactions_count_limit_by_transaction_fee( + &this_stage_data, + qualified_payables.len(), + &self.logger, + )?; - match Self::check_need_of_masq_balances_adjustment( + let is_masq_adjustment_needed = Self::check_need_of_masq_balances_adjustment( &self.logger, Either::Left(qualified_payables), this_stage_data.consuming_wallet_balances.masq_tokens_minor, - ) { - Ok(true) => Ok(Some(Adjustment::MasqToken)), - Ok(false) => Ok(None), - Err(e) => todo!(), + )?; + + match (transaction_fee_adjustment_opt, is_masq_adjustment_needed) { + (Some(affordable_transaction_count), _) => { + Ok(Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count, + })) + } + (None, true) => Ok(Some(Adjustment::MasqToken)), + _ => Ok(None), } } @@ -145,10 +144,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { .collect::>() }); - let adjusted_accounts = match self.run_adjustment(qualified_payables) { - Ok(adjusted_accounts) => adjusted_accounts, - Err(e) => todo!(), - }; + let adjusted_accounts = self.run_adjustment(qualified_payables)?; debug!( self.logger, @@ -194,7 +190,7 @@ impl PaymentAdjusterReal { let max_doable_tx_count_loose_boundaries = available_balance_minor / transaction_fee_required_minor; let (max_doable_tx_count_u16, required_tx_count_u16) = - Self::process_big_nums_and_look_out_for_ceiling( + Self::cast_big_numbers_down_with_possible_ceiling( max_doable_tx_count_loose_boundaries, required_tx_count, ); @@ -212,14 +208,14 @@ impl PaymentAdjusterReal { log_insufficient_transaction_fee_balance( logger, required_tx_count_u16, - tech_info, + tech_info.consuming_wallet_balances.transaction_fee_minor, max_doable_tx_count_u16, ); Ok(Some(max_doable_tx_count_u16)) } } - fn process_big_nums_and_look_out_for_ceiling( + fn cast_big_numbers_down_with_possible_ceiling( max_doable_tx_count: u128, required_tx_count: usize, ) -> (u16, u16) { @@ -249,20 +245,12 @@ impl PaymentAdjusterReal { if cw_masq_balance_minor >= required_masq_sum { Ok(false) } else { - match assess_potential_masq_adjustment_feasibility( + assess_potential_masq_adjustment_feasibility( &qualified_payables, cw_masq_balance_minor, - ) { - Ok(()) => { - log_adjustment_by_masq_required( - logger, - required_masq_sum, - cw_masq_balance_minor, - ); - Ok(true) - } - Err(e) => todo!(), - } + )?; + log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance_minor); + Ok(true) } } @@ -288,16 +276,16 @@ impl PaymentAdjusterReal { &mut self, qualified_accounts: Vec, ) -> Result, PaymentAdjusterError> { - match self.calculate_criteria_and_propose_adjustment_recursively( + let accounts = self.calculate_criteria_and_propose_adjustment_recursively( qualified_accounts, MasqAndTransactionFeeRunner {}, - ) { - Ok(Either::Left(non_exhausted_accounts)) => Ok(exhaust_cw_balance_totally( + )?; + match accounts { + Either::Left(non_exhausted_accounts) => Ok(exhaust_cw_balance_totally( non_exhausted_accounts, self.inner.original_cw_masq_balance(), )), - Ok(Either::Right(finalized_accounts)) => Ok(finalized_accounts), - Err(e) => todo!(), //TODO think about a question mark + Either::Right(finalized_accounts) => Ok(finalized_accounts), } } @@ -322,35 +310,41 @@ impl PaymentAdjusterReal { fn begin_with_adjustment_by_transaction_fees( &mut self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, already_known_count_limit: u16, ) -> Result< Either, Vec>, PaymentAdjusterError, > { let transaction_fee_affordable_weighted_accounts = cut_back_by_excessive_transaction_fee( - accounts_with_individual_criteria, + criteria_and_accounts_in_descending_order, already_known_count_limit, ); - match Self::check_need_of_masq_balances_adjustment( + let unallocated_balance = self.inner.unallocated_cw_masq_balance(); + let definite_answer = match Self::check_need_of_masq_balances_adjustment( &self.logger, Either::Right(&transaction_fee_affordable_weighted_accounts), - self.inner.unallocated_cw_masq_balance(), + unallocated_balance, ) { - Ok(definite_answer) => match definite_answer { - true => { - let result_awaiting_verification = self.propose_adjustment_recursively( - transaction_fee_affordable_weighted_accounts, - ); - Ok(Either::Left(result_awaiting_verification)) - } - false => { - let finalized_accounts = - rebuild_accounts(transaction_fee_affordable_weighted_accounts); - Ok(Either::Right(finalized_accounts)) - } - }, - Err(e) => todo!(), + Ok(answer) => answer, + Err(e) => { + log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient( + &self.logger, + ); + return Err(e); + } + }; + match definite_answer { + true => { + let result_awaiting_verification = self + .propose_adjustment_recursively(transaction_fee_affordable_weighted_accounts); + Ok(Either::Left(result_awaiting_verification)) + } + false => { + let finalized_accounts = + rebuild_accounts(transaction_fee_affordable_weighted_accounts); + Ok(Either::Right(finalized_accounts)) + } } } @@ -364,10 +358,10 @@ impl PaymentAdjusterReal { fn propose_adjustment_recursively( &mut self, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { let adjustment_iteration_result = - self.perform_masq_token_adjustment(accounts_with_individual_criteria_sorted); + self.perform_masq_token_adjustment(accounts_with_individual_criteria); let (here_decided_accounts, downstream_decided_accounts) = self.resolve_iteration_result(adjustment_iteration_result); @@ -417,7 +411,7 @@ impl PaymentAdjusterReal { vec![] } TreatOutweighedAccounts(outweighed) => { - self.adjust_cw_balance_down_for_next_round(&outweighed); + self.adjust_cw_balance_down_before_another_iteration(&outweighed); outweighed } }; @@ -451,12 +445,12 @@ impl PaymentAdjusterReal { &self, accounts_with_zero_criteria: impl Iterator, ) -> Vec<(u128, PayableAccount)> { - let weights_and_accounts = accounts_with_zero_criteria + let criteria_and_accounts = accounts_with_zero_criteria .iterate_for_criteria(AgeCriterionCalculator::new(self)) .iterate_for_criteria(BalanceCriterionCalculator::new()); let collected_accounts_with_criteria = - sort_in_descendant_order_by_weights(weights_and_accounts); + sort_in_descendant_order_by_weights(criteria_and_accounts); // effective only if the iterator is collected print_formulas_characteristics_for_diagnostics(); @@ -487,16 +481,16 @@ impl PaymentAdjusterReal { let unchecked_for_disqualified = match self.handle_possibly_outweighed_account(non_finalized_adjusted_accounts) { - Left(still_not_fully_checked) => still_not_fully_checked, - Right(with_some_outweighed) => return with_some_outweighed, + Either::Left(still_not_fully_checked) => still_not_fully_checked, + Either::Right(with_some_outweighed) => return with_some_outweighed, }; let verified_accounts = match Self::consider_accounts_disqualification( unchecked_for_disqualified, &self.logger, ) { - Left(verified_accounts) => verified_accounts, - Right(with_some_disqualified) => return with_some_disqualified, + Either::Left(verified_accounts) => verified_accounts, + Either::Right(with_some_disqualified) => return with_some_disqualified, }; AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) @@ -597,12 +591,12 @@ impl PaymentAdjusterReal { PayableAccount::from((account_info, ResolutionAfterFullyDetermined::Revert)) }) .collect(); - Right(AdjustmentIterationResult::SpecialTreatmentNeeded { + Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { special_case: TreatInsignificantAccount(debugable_disqualified), remaining: remaining_reverted, }) } else { - Left(non_finalized_adjusted_accounts) + Either::Left(non_finalized_adjusted_accounts) } } @@ -651,20 +645,20 @@ impl PaymentAdjusterReal { .fold((vec![], vec![]), possibly_outweighed_accounts_fold_guts); if outweighed.is_empty() { - Left(passing_through) + Either::Left(passing_through) } else { let remaining = AdjustedAccountBeforeFinalization::finalize_collection( passing_through, ResolutionAfterFullyDetermined::Revert, ); - Right(AdjustmentIterationResult::SpecialTreatmentNeeded { + Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { special_case: TreatOutweighedAccounts(outweighed), remaining, }) } } - fn adjust_cw_balance_down_for_next_round( + fn adjust_cw_balance_down_before_another_iteration( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { @@ -761,7 +755,6 @@ mod tests { use std::time::{Duration, SystemTime}; use std::{usize, vec}; use thousands::Separable; - use web3::types::U256; #[test] #[should_panic( @@ -860,16 +853,17 @@ mod tests { of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); } - #[test] - fn search_for_indispensable_adjustment_positive_for_transaction_fee() { + fn test_positive_transaction_fee_check_vs_masq_check( + test_name: &str, + masq_balances_setup_opt: Option, + ) -> Result, PaymentAdjusterError> { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_transaction_fee"; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let number_of_accounts = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - None, + masq_balances_setup_opt, Some(TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: 100, number_of_accounts, @@ -878,18 +872,13 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(&msg); + subject.search_for_indispensable_adjustment(&msg) + } - let expected_limiting_count = number_of_accounts as u16 - 1; - assert_eq!( - result, - Ok(Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count: expected_limiting_count - })) - ); + fn assert_logs(test_name: &str) { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Gas amount 18,446,744,073,709,551,615,000,000,000 wei \ + "WARN: {test_name}: Gas amount 16,499,999,000,000,000 wei \ cannot cover anticipated fees from sending 3 transactions. Maximum is 2. \ The payments need to be adjusted in their count." )); @@ -897,6 +886,71 @@ mod tests { of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); } + #[test] + fn search_on_three_accounts_positive_for_transaction_fee_when_masq_balance_is_enough() { + let test_name = + "search_on_three_accounts_positive_for_transaction_fee_when_masq_balance_is_enough"; + // means there is plenty of masq + let masq_balances_setup_opt = None; + + let result = + test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); + + assert_eq!( + result, + Ok(Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2 + })) + ); + assert_logs(test_name) + } + + #[test] + fn search_on_three_accounts_positive_for_transaction_fee_but_masq_balance_is_feasibly_adjustable( + ) { + let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_masq_balance_is_feasibly_adjustable"; + let masq_low_but_ok_major = 300 + 500; + let masq_balances_setup_opt = Some(PayableBalancesAndCWBAlanceInTest { + balances_of_accounts_major: vec![120, 300, 500], + cw_balance_major: masq_low_but_ok_major, + }); + + let result = + test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); + + assert_eq!( + result, + Ok(Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2 + })) + ); + assert_logs(test_name) + } + + #[test] + fn search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low() { + let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low"; + let masq_too_low_major = 500 / 2 - 1; // this will kick the limit + let masq_balances_setup_opt = Some(PayableBalancesAndCWBAlanceInTest { + balances_of_accounts_major: vec![120, 300, 500], + cw_balance_major: masq_too_low_major, + }); + + let result = + test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); + + assert_eq!( + result, + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: 3, + cw_masq_balance_minor: gwei_to_wei(masq_too_low_major) + } + )) + ); + assert_logs(test_name) + } + #[test] fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_transaction_fee( ) { @@ -979,7 +1033,7 @@ mod tests { }) .map(|max_doable_txs_count_u256| { let (doable_txs_count, _) = - PaymentAdjusterReal::process_big_nums_and_look_out_for_ceiling( + PaymentAdjusterReal::cast_big_numbers_down_with_possible_ceiling( max_doable_txs_count_u256, 123, ); @@ -1006,7 +1060,7 @@ mod tests { }) .map(|required_tx_count_usize| { let (_, required_tx_count) = - PaymentAdjusterReal::process_big_nums_and_look_out_for_ceiling( + PaymentAdjusterReal::cast_big_numbers_down_with_possible_ceiling( 123, required_tx_count_usize, ); @@ -1043,9 +1097,10 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let weights_and_accounts = subject.calculate_criteria_sums_for_accounts(qualified_payables); + let criteria_and_accounts = + subject.calculate_criteria_sums_for_accounts(qualified_payables); - let only_accounts = weights_and_accounts + let only_accounts = criteria_and_accounts .iter() .map(|(_, account)| account) .collect::>(); @@ -1243,10 +1298,10 @@ mod tests { } #[test] - fn there_are_safe_doors_out_if_we_have_a_disqualified_account_while_no_the_remaining_accounts_are_empty( + fn there_are_safe_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts( ) { init_test_logging(); - let test_name = "there_are_safe_doors_out_if_we_have_a_disqualified_account_while_no_the_remaining_accounts_are_empty"; + let test_name = "there_are_safe_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts"; let mut subject = make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); let insignificant_account = DisqualifiedPayableAccount { @@ -1886,6 +1941,75 @@ mod tests { TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } + #[test] + fn error_returns_after_transaction_fee_adjustment_done_but_mask_balance_is_found_completely_insufficient( + ) { + init_test_logging(); + let test_name = "error_returns_after_transaction_fee_adjustment_done_but_mask_balance_is_found_completely_insufficient"; + let now = SystemTime::now(); + //this account gets eliminated in the transaction-fee cut + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + let cw_masq_balance_minor = (333_000_000_000_000 / 2) - 1; //this is exactly the amount which will provoke an error + subject.logger = Logger::new(test_name); + let setup_msg = PayablePaymentSetup { + qualified_payables, + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + masq_tokens_minor: cw_masq_balance_minor, + }, + estimated_gas_limit_per_transaction: 77_000, + desired_transaction_fee_price_major: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = PreparedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2, + }, + }; + + let result = subject.adjust_payments(adjustment_setup, now); + + assert_eq!( + result, + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: 2, + cw_masq_balance_minor + } + )) + ); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Passing successful \ + payment adjustment by the transaction fee, but facing critical scarcity of MASQ balance. \ + Operation will abort." + )); + } + #[test] fn testing_reliability_by_long_loading_payment_adjuster_with_randomly_generated_accounts() { todo!("write this occasional test") diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 5f27a626e..7c8e56110 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -11,7 +11,6 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::ui_gateway::NodeFromUiMessage; use std::fmt; use std::fmt::{Debug, Formatter}; -use web3::types::U256; #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct BlockchainBridgeConfig { From 862094c3cf5477130677dc3b2866558a13eebcef Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 16 Aug 2023 12:35:00 +0200 Subject: [PATCH 073/250] GH-711: interim commit --- .../miscellaneous/helper_functions.rs | 104 +------------ node/src/accountant/payment_adjuster/mod.rs | 12 +- .../accountant/payment_adjuster/verifier.rs | 143 ++++++++++++++++++ 3 files changed, 152 insertions(+), 107 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/verifier.rs diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index b8a7f172f..e8f2d61ea 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -37,30 +37,6 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn assess_potential_masq_adjustment_feasibility( - accounts: &[&PayableAccount], - cw_masq_balance_minor: u128, -) -> Result<(), PaymentAdjusterError> { - let largest_account = - find_largest_debt_account_generic(accounts, |account| account.balance_wei); - - //TODO you need to make this better !!!! What if the big one is too big against the other ones? - - if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - <= cw_masq_balance_minor - { - Ok(()) - } else { - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { - number_of_accounts: accounts.len(), - cw_masq_balance_minor, - }, - )) - } -} - pub fn cut_back_by_excessive_transaction_fee( weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, limit: u16, @@ -343,10 +319,9 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - assess_potential_masq_adjustment_feasibility, compute_fraction_preventing_mul_coeff, - exhaust_cw_balance_totally, find_largest_debt_account_generic, - list_accounts_under_the_disqualification_limit, log_10, log_2, - possibly_outweighed_accounts_fold_guts, ExhaustionStatus, + compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, + find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, log_10, + log_2, possibly_outweighed_accounts_fold_guts, ExhaustionStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; @@ -368,79 +343,6 @@ mod tests { assert_eq!(ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor, 2) } - fn test_body_for_adjustment_feasibility_nearly_insufficient( - original_accounts: Vec, - cw_masq_balance: u128, - ) { - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - - let result = assess_potential_masq_adjustment_feasibility( - &accounts_in_expected_format, - cw_masq_balance, - ); - - assert_eq!(result, Ok(())) - } - - fn calculate_border_line(account_balance: u128) -> u128 { - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - } - - #[test] - fn adjustment_feasibility_nearly_insufficient_when_1_less() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_border_line(account_1.balance_wei) + 1; - let original_accounts = vec![account_1, account_2]; - - test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) - } - - #[test] - fn adjustment_feasibility_nearly_insufficient_when_equal() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_border_line(account_1.balance_wei); - let original_accounts = vec![account_1, account_2]; - - test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) - } - - #[test] - fn adjustment_feasibility_err_from_insufficient_balance() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(222); - account_2.balance_wei = 2_000_000_002; - let mut account_3 = make_payable_account(333); - account_3.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_border_line(account_2.balance_wei) - 1; - let original_accounts = vec![account_1, account_2, account_3]; - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - - let result = assess_potential_masq_adjustment_feasibility( - &accounts_in_expected_format, - cw_masq_balance, - ); - - assert_eq!( - result, - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { - number_of_accounts: 3, - cw_masq_balance_minor: cw_masq_balance - } - )) - ) - } - #[test] fn log_10_works() { [ diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 53a659e3d..72fb61d5a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -//keep these modules private +//try to keep these modules private mod adjustment_runners; mod criteria_calculators; mod diagnostics; @@ -8,6 +8,7 @@ mod inner; mod log_fns; mod miscellaneous; mod test_utils; +mod verifier; use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::gwei_to_wei; @@ -38,11 +39,10 @@ use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - assess_potential_masq_adjustment_feasibility, compute_fraction_preventing_mul_coeff, - criteria_total, cut_back_by_excessive_transaction_fee, exhaust_cw_balance_totally, - find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, - possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, - sum_as, + compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, + exhaust_cw_balance_totally, find_largest_debt_account_generic, + list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, + rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, }; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs new file mode 100644 index 000000000..68713df54 --- /dev/null +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -0,0 +1,143 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use itertools::Itertools; +use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + find_largest_debt_account_generic, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, +}; +use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; + +struct MasqAdjustmentPossibilityVerifier {} + +impl MasqAdjustmentPossibilityVerifier { + pub fn verify_adjustment_as_possible( + &self, + accounts: &[&PayableAccount], + cw_masq_balance_minor: u128, + ) -> Result<(), PaymentAdjusterError> { + let largest_account = + find_largest_debt_account_generic(accounts, |account| account.balance_wei); + + let sorted = accounts.iter().sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)).collect::>(); + + let largest_debt = sorted.first().expect("at least one must be here"); + + let minimum_required_portion = + + let remaining = sorted.iter().skip(1); + + + + //TODO you need to make this better !!!! What if the big one is too big against the other ones? + + if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + <= cw_masq_balance_minor + { + Ok(()) + } else { + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: accounts.len(), + cw_masq_balance_minor, + }, + )) + } + } + + fn calculate_breaking_line(account_balance: u128) -> u128 { + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; + use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; + use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; + use crate::accountant::test_utils::make_payable_account; + + #[test] + fn calculate_breaking_line_works(){ + let mut account = make_payable_account(111); + account.balance_wei = 300_000_000; + + let result = MasqAdjustmentPossibilityVerifier::calculate_breaking_line() + + assert_eq!(result) + } + + fn test_body_for_adjustment_feasibility_nearly_insufficient( + original_accounts: Vec, + cw_masq_balance: u128, + ) { + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + let subject = MasqAdjustmentPossibilityVerifier{}; + + let result = subject.verify_adjustment_as_possible( + &accounts_in_expected_format, + cw_masq_balance, + ); + + assert_eq!(result, Ok(())) + } + + + + #[test] + fn adjustment_feasibility_nearly_insufficient_when_1_less() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_breaking_line(account_1.balance_wei) + 1; + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + } + + #[test] + fn adjustment_feasibility_nearly_insufficient_when_equal() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_breaking_line(account_1.balance_wei); + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + } + + #[test] + fn adjustment_feasibility_err_from_insufficient_balance() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(222); + account_2.balance_wei = 2_000_000_002; + let mut account_3 = make_payable_account(333); + account_3.balance_wei = 1_000_000_000; + let cw_masq_balance = calculate_breaking_line(account_2.balance_wei) - 1; + let original_accounts = vec![account_1, account_2, account_3]; + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + let subject = MasqAdjustmentPossibilityVerifier{}; + + let result = assess_potential_masq_adjustment_feasibility( + &accounts_in_expected_format, + cw_masq_balance, + ); + + assert_eq!( + result, + Err(PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + number_of_accounts: 3, + cw_masq_balance_minor: cw_masq_balance + } + )) + ) + } +} From b3c12a7fad988d16d01ade13d6f377a6f2e00170 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 29 Aug 2023 16:24:38 +0200 Subject: [PATCH 074/250] GH-711: fixing the algorithm to an iteration with one remaining account specially --- .../payment_adjuster/adjustment_runners.rs | 211 ++++++++++++- .../payment_adjuster/diagnostics.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 12 +- .../accountant/payment_adjuster/log_fns.rs | 25 +- .../{data_sructures.rs => data_structures.rs} | 0 .../miscellaneous/helper_functions.rs | 243 ++++++++++++--- .../payment_adjuster/miscellaneous/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 292 ++++++++---------- .../accountant/payment_adjuster/verifier.rs | 89 +++--- 9 files changed, 607 insertions(+), 271 deletions(-) rename node/src/accountant/payment_adjuster/miscellaneous/{data_sructures.rs => data_structures.rs} (100%) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 170f355a9..f7d6fb148 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -1,14 +1,27 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; +use crate::accountant::payment_adjuster::diagnostics; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::maybe_find_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; +use std::vec; pub trait AdjustmentRunner { type ReturnType; - fn adjust( + // This method: + // a) helps with writing tests aimed at edge cases + // b) avoids performing an unnecessary computation for an obvious result + // c) makes the condition in the initial check for adjustment possibility achievable in its pureness + fn adjust_last_one( + &self, + payment_adjuster: &PaymentAdjusterReal, + last_account: PayableAccount, + ) -> Self::ReturnType; + + fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, @@ -23,7 +36,16 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { PaymentAdjusterError, >; - fn adjust( + fn adjust_last_one( + &self, + payment_adjuster: &PaymentAdjusterReal, + last_account: PayableAccount, + ) -> Self::ReturnType { + let adjusted_account_vec = adjust_last_one(payment_adjuster, last_account); + Ok(Either::Left(empty_or_single_element(adjusted_account_vec))) + } + + fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, @@ -50,7 +72,15 @@ pub struct MasqOnlyRunner {} impl AdjustmentRunner for MasqOnlyRunner { type ReturnType = Vec; - fn adjust( + fn adjust_last_one( + &self, + payment_adjuster: &PaymentAdjusterReal, + last_account: PayableAccount, + ) -> Self::ReturnType { + empty_or_single_element(adjust_last_one(payment_adjuster, last_account)) + } + + fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, @@ -59,21 +89,164 @@ impl AdjustmentRunner for MasqOnlyRunner { } } +fn adjust_last_one( + payment_adjuster: &PaymentAdjusterReal, + last_account: PayableAccount, +) -> Option { + let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance(); + let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { + last_account.balance_wei + } else { + diagnostics!( + "LAST REMAINING ACCOUNT", + "Balance adjusted to {} by exhausting the cw balance", + cw_balance + ); + + cw_balance + }; + let mut proposed_adjustment_vec = vec![AdjustedAccountBeforeFinalization::new( + last_account, + proposed_adjusted_balance, + 0, + )]; + + let logger = &payment_adjuster.logger; + + match maybe_find_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { + Some(_) => None, + None => Some(proposed_adjustment_vec.remove(0)), + } +} + +fn empty_or_single_element( + adjusted_account_opt: Option, +) -> Vec { + match adjusted_account_opt { + Some(elem) => vec![elem], + None => vec![], + } +} + #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, MasqOnlyRunner, + adjust_last_one, empty_or_single_element, AdjustmentRunner, MasqAndTransactionFeeRunner, + MasqOnlyRunner, }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; + use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; + use itertools::Either; + use std::fmt::Debug; use std::time::{Duration, SystemTime}; + fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { + let details = FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: 0, + masq_tokens_minor: cw_balance, + }, + desired_transaction_fee_price_major: 30, + estimated_gas_limit_per_transaction: 100, + }; + let adjustment = Adjustment::MasqToken; + let mut payment_adjuster = PaymentAdjusterReal::new(); + payment_adjuster.initialize_inner(details, adjustment, now); + payment_adjuster + } + + fn test_adjust_last_one( + subject: AR, + expected_return_type_finalizer: fn(Vec) -> R, + ) where + AR: AdjustmentRunner, + R: Debug + PartialEq, + { + let now = SystemTime::now(); + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 9_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let cw_balance = 8_645_123_505; + let mut payment_adjuster = prepare_payment_adjuster(cw_balance, now); + + let result = subject.adjust_last_one(&mut payment_adjuster, account_1.clone()); + + assert_eq!( + result, + expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { + original_account: account_1, + proposed_adjusted_balance: cw_balance, + criteria_sum: 0, //TODO you should delete this field, but when the time is right + }]) + ) + } + + #[test] + fn masq_and_transaction_fee_adjust_single_works() { + test_adjust_last_one(MasqAndTransactionFeeRunner {}, |expected_vec| { + Ok(Either::Left(expected_vec)) + }) + } + + #[test] + fn masq_only_adjust_single_works() { + test_adjust_last_one(MasqOnlyRunner {}, |expected_vec| expected_vec) + } + + #[test] + fn adjust_last_one_for_requested_balance_smaller_than_cw_balance() { + let now = SystemTime::now(); + let cw_balance = 8_645_123_505; + let account = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 4_333_222_111, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let payment_adjuster = prepare_payment_adjuster(cw_balance, now); + + let result = adjust_last_one(&payment_adjuster, account.clone()); + + assert_eq!( + result, + Some(AdjustedAccountBeforeFinalization { + original_account: account, + proposed_adjusted_balance: 4_333_222_111, + criteria_sum: 0, + }) + ) + } + + #[test] + fn adjust_last_one_decides_for_adjusted_account_disqualification() { + let now = SystemTime::now(); + let account_balance = 4_000_444; + let account = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: account_balance, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let cw_balance = 4_000_444 / 2; + let payment_adjuster = prepare_payment_adjuster(cw_balance, now); + + let result = adjust_last_one(&payment_adjuster, account.clone()); + + assert_eq!(result, None) + } + #[test] - fn masq_only_adjuster_is_not_meant_to_adjust_also_by_transaction_fee() { + fn masq_only_adjust_multiple_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); let cw_balance = 9_000_000; let details = FinancialAndTechDetails { @@ -100,16 +273,36 @@ mod tests { }; let mut payment_adjuster = PaymentAdjusterReal::new(); payment_adjuster.initialize_inner(details, adjustment, now); + let subject = MasqOnlyRunner {}; let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); - let adjustment_runner = MasqOnlyRunner {}; - let result = adjustment_runner.adjust(&mut payment_adjuster, seeds); + let result = subject.adjust_multiple(&mut payment_adjuster, seeds); let returned_accounts_accounts = result .into_iter() .map(|account| account.original_account.wallet) .collect::>(); assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) - //if the transaction_fee adjustment had been available, only one account would've been returned, the test passes + // if the transaction_fee adjustment had been available to perform, only one account would've been + // returned, therefore test passes + } + + #[test] + fn empty_or_single_element_for_none() { + let result = empty_or_single_element(None); + + assert_eq!(result, vec![]) + } + + #[test] + fn empty_or_single_element_for_some() { + let account_info = AdjustedAccountBeforeFinalization { + original_account: make_payable_account(123), + proposed_adjusted_balance: 123_456_789, + criteria_sum: 987_654_321, + }; + let result = empty_or_single_element(Some(account_info.clone())); + + assert_eq!(result, vec![account_info]) } } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 6d820f0fb..2f6d14138 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -55,7 +55,7 @@ pub mod separately_defined_diagnostic_functions { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::NamedCalculator; use crate::accountant::payment_adjuster::diagnostics; - use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::sub_lib::wallet::Wallet; use thousands::Separable; diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 1c9727867..77e3af27e 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -16,8 +16,8 @@ pub trait PaymentAdjusterInner { PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance()") } - fn lower_unallocated_cw_balance(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation("lower_unallocated_cw_balance()") + fn update_unallocated_cw_balance(&mut self, _subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance()") } } @@ -56,7 +56,7 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn unallocated_cw_masq_balance(&self) -> u128 { self.unallocated_cw_masq_balance } - fn lower_unallocated_cw_balance(&mut self, subtrahend: u128) { + fn update_unallocated_cw_balance(&mut self, subtrahend: u128) { let updated_thought_cw_balance = self .unallocated_cw_masq_balance .checked_sub(subtrahend) @@ -144,11 +144,11 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the lower_unallocated_cw_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the update_unallocated_cw_balance() method in PaymentAdjusterInner" )] - fn inner_null_calling_lower_unallocated_cw_balance() { + fn inner_null_calling_update_unallocated_cw_balance() { let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.lower_unallocated_cw_balance(123); + let _ = subject.update_unallocated_cw_balance(123); } } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 8a7bc9b53..21c15df95 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,7 +1,9 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_sructures::DisqualifiedPayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, DisqualifiedPayableAccount, +}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -114,16 +116,19 @@ pub fn before_and_after_debug_msg( ) } -pub fn log_info_for_disqualified_account(logger: &Logger, account: &DisqualifiedPayableAccount) { +pub fn log_info_for_disqualified_account( + logger: &Logger, + account: &AdjustedAccountBeforeFinalization, +) { info!( - logger, - "Consuming wallet is short of MASQ. Seems unavoidable to disregard payable {} \ - at the moment. Reason is the computed possible payment of {} wei would not be at least half of \ - the original debt {}.", - account.wallet, - account.proposed_adjusted_balance.separate_with_commas(), - account.original_balance.separate_with_commas() - ) + logger, + "Dealing with the consuming wallet being short of MASQ. \ + Seems unavoidable to disregard payable {} at the moment. Reason is the computed \ + possible payment of {} wei would not be at least half of the original debt {}.", + account.original_account.wallet, + account.proposed_adjusted_balance.separate_with_commas(), + account.original_account.balance_wei.separate_with_commas() + ) } pub fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_masq_balance: u128) { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs similarity index 100% rename from node/src/accountant/payment_adjuster/miscellaneous/data_sructures.rs rename to node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e8f2d61ea..6ca778ff4 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -1,17 +1,21 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ - exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, - possibly_outweighed_accounts_diagnostics, + exhausting_cw_balance_diagnostics, maybe_find_an_account_to_disqualify_diagnostics, + not_exhausting_cw_balance_diagnostics, possibly_outweighed_accounts_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ +use crate::accountant::payment_adjuster::log_fns::log_info_for_disqualified_account; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, ResolutionAfterFullyDetermined, }; -use crate::accountant::payment_adjuster::{diagnostics, AnalysisError, PaymentAdjusterError}; +use crate::sub_lib::wallet::Wallet; use itertools::Itertools; +use masq_lib::logger::Logger; use std::iter::successors; +use std::ops::Not; use thousands::Separable; const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; @@ -91,31 +95,30 @@ pub fn possibly_outweighed_accounts_fold_guts( } } -pub fn find_largest_debt_account_generic(accounts: &[A], balance_fetcher: fn(&A) -> u128) -> &A { - struct Largest<'a, A> { - account: &'a A, - balance: u128, - } - - let first_account = accounts.first().expect("collection was empty"); - let init = Largest { - account: first_account, - balance: balance_fetcher(first_account), - }; +pub fn find_largest_disqualified_account<'a>( + accounts: &'a [&'a AdjustedAccountBeforeFinalization], +) -> &'a AdjustedAccountBeforeFinalization { + let first_account = &accounts.first().expect("collection was empty"); accounts .iter() - .fold(init, |largest_so_far, current| { - let balance = balance_fetcher(current); - if balance <= largest_so_far.balance { + .fold(**first_account, |largest_so_far, current| { + if current.original_account.balance_wei < largest_so_far.original_account.balance_wei { largest_so_far - } else { - Largest { - account: current, - balance, + } else if current.original_account.balance_wei + == largest_so_far.original_account.balance_wei + { + // bigger value means younger + if current.original_account.last_paid_timestamp + > largest_so_far.original_account.last_paid_timestamp + { + current + } else { + largest_so_far } + } else { + current } }) - .account } pub fn exhaust_cw_balance_totally( @@ -184,6 +187,42 @@ pub fn exhaust_cw_balance_totally( .collect() } +pub fn maybe_find_an_account_to_disqualify_in_this_iteration( + non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], + logger: &Logger, +) -> Option { + let disqualification_suspected_accounts = + list_accounts_under_the_disqualification_limit(non_finalized_adjusted_accounts); + disqualification_suspected_accounts + .is_empty() + .not() + .then(|| { + let account_to_disqualify = find_largest_disqualified_account( + &disqualification_suspected_accounts + ); + + let wallet = account_to_disqualify.original_account.wallet.clone(); + + maybe_find_an_account_to_disqualify_diagnostics( + &disqualification_suspected_accounts, + &wallet, + ); + + debug!( + logger, + "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ + for disqualification. Chose the least desirable disqualified account as the one \ + with the biggest balance, which is {}. To be thrown away in this iteration.", + disqualification_suspected_accounts, + wallet + ); + + log_info_for_disqualified_account(logger, account_to_disqualify); + + wallet + }) +} + struct ExhaustionStatus { remainder: u128, already_finalized_accounts: Vec, @@ -317,23 +356,24 @@ impl #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_sructures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, exhaust_cw_balance_totally, - find_largest_debt_account_generic, list_accounts_under_the_disqualification_limit, log_10, - log_2, possibly_outweighed_accounts_fold_guts, ExhaustionStatus, + compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, + find_largest_disqualified_account, list_accounts_under_the_disqualification_limit, log_10, + log_2, maybe_find_an_account_to_disqualify_in_this_iteration, + possibly_outweighed_accounts_fold_guts, ExhaustionStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; - use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use std::time::SystemTime; + use masq_lib::logger::Logger; + use std::time::{Duration, SystemTime}; #[test] fn constants_are_correct() { @@ -497,21 +537,86 @@ mod tests { ) } + fn envelope_payable_accounts_to_adjusted_accounts_before_finalization( + payable_accounts: Vec<(PayableAccount, u128)>, + ) -> Vec { + payable_accounts + .into_iter() + .map(|(original_account, proposed_adjusted_balance)| { + AdjustedAccountBeforeFinalization { + original_account, + proposed_adjusted_balance, + criteria_sum: 0, + } + }) + .collect() + } + + fn give_by_reference( + adjusted_accounts: &[AdjustedAccountBeforeFinalization], + ) -> Vec<&AdjustedAccountBeforeFinalization> { + adjusted_accounts.iter().collect() + } + #[test] - fn find_largest_debt_account_generic_works() { + fn find_largest_disqualified_account_works_for_unequal_balances() { let account_1 = make_payable_account(111); let account_2 = make_payable_account(333); let account_3 = make_payable_account(222); let account_4 = make_payable_account(332); - let accounts = vec![account_1, account_2.clone(), account_3, account_4]; + let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + (account_1, 100_000_000), + (account_2.clone(), 222_000_000), + (account_3, 300_000_000), + (account_4, 400_000_000), + ]); + let referenced_accounts = give_by_reference(&accounts); - let result = find_largest_debt_account_generic(&accounts, |account| account.balance_wei); + let result = find_largest_disqualified_account(&referenced_accounts); - assert_eq!(result, &account_2) + assert_eq!( + result, + &AdjustedAccountBeforeFinalization { + original_account: account_2, + proposed_adjusted_balance: 222_000_000, + criteria_sum: 0 + } + ) } #[test] - fn find_largest_debt_account_generic_when_accounts_with_equal_balances() { + fn find_largest_disqualified_account_for_equal_balances_chooses_younger_account() { + // because it will help do the impact on the cw balance that can be spread among less accounts, + // yet we prefer to keep the older and so more important account in the game + let now = SystemTime::now(); + let account_1 = make_payable_account(111); + let mut account_2 = make_payable_account(333); + account_2.last_paid_timestamp = now.checked_sub(Duration::from_secs(10000)).unwrap(); + let account_3 = make_payable_account(222); + let mut account_4 = make_payable_account(333); + account_4.last_paid_timestamp = now.checked_sub(Duration::from_secs(9999)).unwrap(); + let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + (account_1, 100_000_000), + (account_2, 200_000_000), + (account_3, 300_000_000), + (account_4.clone(), 444_000_000), + ]); + let referenced_accounts = give_by_reference(&accounts); + + let result = find_largest_disqualified_account(&referenced_accounts); + + assert_eq!( + result, + &AdjustedAccountBeforeFinalization { + original_account: account_4, + proposed_adjusted_balance: 444_000_000, + criteria_sum: 0, + } + ) + } + + #[test] + fn find_largest_disqualified_account_for_accounts_with_equal_balances_as_well_as_age() { let account = make_payable_account(111); let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); @@ -519,15 +624,26 @@ mod tests { account_1.wallet = wallet_1.clone(); let mut account_2 = account; account_2.wallet = wallet_2; - let accounts = vec![account_1.clone(), account_2]; + let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + (account_1.clone(), 100_111_000), + (account_2, 200_000_000), + ]); + let referenced_accounts = give_by_reference(&accounts); - let result = find_largest_debt_account_generic(&accounts, |account| account.balance_wei); + let result = find_largest_disqualified_account(&referenced_accounts); - assert_eq!(result, &account_1) + assert_eq!( + result, + &AdjustedAccountBeforeFinalization { + original_account: account_1, + proposed_adjusted_balance: 100_111_000, + criteria_sum: 0 + } + ) } #[test] - fn algorithm_is_left_cold_by_accounts_with_original_balance_equal_to_proposed_one() { + fn ignores_accounts_with_original_balance_equal_to_the_proposed_ones() { let account_info = AdjustedAccountBeforeFinalization { original_account: PayableAccount { wallet: make_wallet("blah"), @@ -546,6 +662,57 @@ mod tests { assert_eq!(ok, vec![account_info]) } + #[test] + fn pick_only_the_biggest_and_youngest_debt_from_all_disqualified_accounts_in_one_iteration() { + let test_name = "pick_only_the_biggest_and_youngest_debt_from_all_disqualified_accounts_in_one_iteration"; + let now = SystemTime::now(); + let cw_masq_balance = 2_000_000_000_000; + let logger = Logger::new(test_name); + let subject = make_initialized_subject(now, Some(cw_masq_balance), None); + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 700_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 700_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(99_999)).unwrap(), + pending_payable_opt: None, + }; + let wallet_4 = make_wallet("jkl"); + let account_4 = PayableAccount { + wallet: wallet_4.clone(), + balance_wei: 120_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(7_000)).unwrap(), + pending_payable_opt: None, + }; + let accounts_with_individual_criteria = subject + .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); + let criteria_total = criteria_total(&accounts_with_individual_criteria); + let non_finalized_adjusted_accounts = subject.compute_non_finalized_adjusted_accounts( + accounts_with_individual_criteria, + criteria_total, + ); + + let result = maybe_find_an_account_to_disqualify_in_this_iteration( + &non_finalized_adjusted_accounts, + &logger, + ); + + assert_eq!(result, Some(wallet_3)); + } + fn make_non_finalized_adjusted_account( wallet: &Wallet, original_balance: u128, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs index 65eec000a..ceaef4c54 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs @@ -1,4 +1,4 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod data_sructures; +pub mod data_structures; pub mod helper_functions; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 72fb61d5a..59e916e57 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -19,9 +19,7 @@ use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_cal use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ - maybe_find_an_account_to_disqualify_diagnostics, non_finalized_adjusted_accounts_diagnostics, -}; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_for_collections}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -31,19 +29,20 @@ use crate::accountant::payment_adjuster::log_fns::{ log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient, log_info_for_disqualified_account, log_insufficient_transaction_fee_balance, }; -use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::{ +use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DisqualifiedPayableAccount, ResolutionAfterFullyDetermined, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, - exhaust_cw_balance_totally, find_largest_debt_account_generic, - list_accounts_under_the_disqualification_limit, possibly_outweighed_accounts_fold_guts, - rebuild_accounts, sort_in_descendant_order_by_weights, sum_as, + exhaust_cw_balance_totally, maybe_find_an_account_to_disqualify_in_this_iteration, + possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, + sum_as, }; +use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, }; @@ -59,7 +58,6 @@ use std::any::Any; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::iter::once; -use std::ops::Not; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; @@ -245,10 +243,8 @@ impl PaymentAdjusterReal { if cw_masq_balance_minor >= required_masq_sum { Ok(false) } else { - assess_potential_masq_adjustment_feasibility( - &qualified_payables, - cw_masq_balance_minor, - )?; + MasqAdjustmentPossibilityVerifier {} + .verify_adjustment_possibility(&qualified_payables, cw_masq_balance_minor)?; log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance_minor); Ok(true) } @@ -291,7 +287,7 @@ impl PaymentAdjusterReal { fn calculate_criteria_and_propose_adjustment_recursively( &mut self, - unresolved_qualified_accounts: Vec, + mut unresolved_qualified_accounts: Vec, purpose_specific_adjuster: A, ) -> R where @@ -302,10 +298,15 @@ impl PaymentAdjusterReal { &unresolved_qualified_accounts, ); + if unresolved_qualified_accounts.len() == 1 { + return purpose_specific_adjuster + .adjust_last_one(self, unresolved_qualified_accounts.remove(0)); + } + let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); - purpose_specific_adjuster.adjust(self, accounts_with_individual_criteria_sorted) + purpose_specific_adjuster.adjust_multiple(self, accounts_with_individual_criteria_sorted) } fn begin_with_adjustment_by_transaction_fees( @@ -391,20 +392,11 @@ impl PaymentAdjusterReal { } => { let here_decided_accounts = match special_case { TreatInsignificantAccount(disqualified) => { - log_info_for_disqualified_account(&self.logger, &disqualified); + //log_info_for_disqualified_account(&self.logger, &disqualified); if remaining.is_empty() { - // We've reached a niche. The performed combination has eliminated all - // accounts in spite of the initial mechanism, that tried to weight - // whether it was possible to return minimally one adjusted account for - // the payments, was pushing hard to the edge, it actually did not manage - // to get it right. Being here means the prevention of possibly unnecessary - // computation has failed. - // - // To get out of here we want to be gentle and avoid another iteration - // (probably wouldn't be fatal though). Anyway, it's a win pf the choice for - // well determined behavior instead of letting the situation unwind down to - // waters we don't exactly know. + debug!(self.logger, "Last remaining account ended up disqualified"); + return (vec![], vec![]); } @@ -474,19 +466,19 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self.compute_non_finalized_already_adjusted_accounts( + let non_finalized_adjusted_accounts = self.compute_non_finalized_adjusted_accounts( accounts_with_individual_criteria, criteria_total, ); - let unchecked_for_disqualified = - match self.handle_possibly_outweighed_account(non_finalized_adjusted_accounts) { - Either::Left(still_not_fully_checked) => still_not_fully_checked, + let still_unchecked_for_disqualified = + match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { + Either::Left(first_check_passing_accounts) => first_check_passing_accounts, Either::Right(with_some_outweighed) => return with_some_outweighed, }; - let verified_accounts = match Self::consider_accounts_disqualification( - unchecked_for_disqualified, + let verified_accounts = match Self::consider_account_disqualification( + still_unchecked_for_disqualified, &self.logger, ) { Either::Left(verified_accounts) => verified_accounts, @@ -496,7 +488,7 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) } - fn compute_non_finalized_already_adjusted_accounts( + fn compute_non_finalized_adjusted_accounts( &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, @@ -541,16 +533,14 @@ impl PaymentAdjusterReal { .collect() } - fn consider_accounts_disqualification( + fn consider_account_disqualification( non_finalized_adjusted_accounts: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { - if let Some(disq_account_wallet) = - Self::maybe_find_an_account_to_disqualify_in_this_iteration( - &non_finalized_adjusted_accounts, - logger, - ) - { + if let Some(disq_account_wallet) = maybe_find_an_account_to_disqualify_in_this_iteration( + &non_finalized_adjusted_accounts, + logger, + ) { let init = ( None, Vec::with_capacity(non_finalized_adjusted_accounts.len() - 1), @@ -600,43 +590,7 @@ impl PaymentAdjusterReal { } } - fn maybe_find_an_account_to_disqualify_in_this_iteration( - non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], - logger: &Logger, - ) -> Option { - let disqualification_suspected_accounts = - list_accounts_under_the_disqualification_limit(non_finalized_adjusted_accounts); - disqualification_suspected_accounts - .is_empty() - .not() - .then(|| { - // comparison based on the criteria sum is more accurate than on the proposed adjusted balance - // which comes rounded up to here - let account_to_disqualify = find_largest_debt_account_generic( - &disqualification_suspected_accounts, - |account_info| account_info.criteria_sum, - ); - - let wallet = account_to_disqualify.original_account.wallet.clone(); - - maybe_find_an_account_to_disqualify_diagnostics( - &disqualification_suspected_accounts, - &wallet, - ); - - trace!( - logger, - "Found accounts {:?} whose proposed new, adjusted balances laid under \ - the limit for disqualification. Choose the least desirable proposal of account {} to \ - be thrown away in this iteration.", - disqualification_suspected_accounts, - wallet - ); - wallet - }) - } - - fn handle_possibly_outweighed_account( + fn handle_possibly_outweighed_accounts( &self, non_finalized_adjusted_accounts: Vec, ) -> Either, AdjustmentIterationResult> { @@ -665,7 +619,7 @@ impl PaymentAdjusterReal { let subtrahend_total: u128 = sum_as(processed_outweighed, |account| { account.proposed_adjusted_balance }); - self.inner.lower_unallocated_cw_balance(subtrahend_total); + self.inner.update_unallocated_cw_balance(subtrahend_total); diagnostics!( "LOWERED CW BALANCE", @@ -727,8 +681,8 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; - use crate::accountant::payment_adjuster::miscellaneous::data_sructures::SpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_sructures::{ + use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::TreatInsignificantAccount; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, DisqualifiedPayableAccount, SpecialTreatment, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; @@ -930,7 +884,7 @@ mod tests { #[test] fn search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low() { let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low"; - let masq_too_low_major = 500 / 2 - 1; // this will kick the limit + let masq_too_low_major = 120 / 2 - 1; // this will kick the limit let masq_balances_setup_opt = Some(PayableBalancesAndCWBAlanceInTest { balances_of_accounts_major: vec![120, 300, 500], cw_balance_major: masq_too_low_major, @@ -1107,60 +1061,6 @@ mod tests { assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) } - #[test] - fn only_the_most_demanding_one_is_picked_from_disqualified_accounts() { - let test_name = "only_the_most_demanding_one_is_picked_from_disqualified_accounts"; - let now = SystemTime::now(); - let cw_masq_balance = 2_000_000_000_000; - let logger = Logger::new(test_name); - let subject = make_initialized_subject(now, Some(cw_masq_balance), None); - let wallet_1 = make_wallet("abc"); - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 700_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 333_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), - pending_payable_opt: None, - }; - let wallet_3 = make_wallet("ghi"); - // balance big enough to be hard to match it up to the disqualification limit and 1 s more - // than the first account - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 700_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_001)).unwrap(), - pending_payable_opt: None, - }; - let wallet_4 = make_wallet("jkl"); - let account_4 = PayableAccount { - wallet: wallet_4.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(7_000)).unwrap(), - pending_payable_opt: None, - }; - let accounts_with_individual_criteria = subject - .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); - let criteria_total = criteria_total(&accounts_with_individual_criteria); - let non_finalized_adjusted_accounts = subject - .compute_non_finalized_already_adjusted_accounts( - accounts_with_individual_criteria, - criteria_total, - ); - - let result = PaymentAdjusterReal::maybe_find_an_account_to_disqualify_in_this_iteration( - &non_finalized_adjusted_accounts, - &logger, - ); - - assert_eq!(result, Some(wallet_3)); - } - #[test] fn smaller_debt_with_extreme_age_is_picked_prioritized_as_outweighed_but_not_with_more_money_than_required( ) { @@ -1221,13 +1121,11 @@ mod tests { assert_eq!(first_account.proposed_adjusted_balance, balance_2); let second_account = result.remove(0); assert_eq!(second_account.original_account, account_1); - let upper_limit = ((1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000) - * 999_999_999_999) - / 1_000_000_000_000; + let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; let lower_limit = (upper_limit * 9) / 10; assert!( - lower_limit < second_account.proposed_adjusted_balance - && second_account.proposed_adjusted_balance < upper_limit, + lower_limit <= second_account.proposed_adjusted_balance + && second_account.proposed_adjusted_balance <= upper_limit, "we expected the roughly adjusted account to be between {} and {} but was {}", lower_limit, upper_limit, @@ -1319,7 +1217,10 @@ mod tests { assert!(here_decided_accounts.is_empty()); assert!(downstream_decided_accounts.is_empty()); - TestLogHandler::new().exists_log_matching(&format!("INFO: {test_name}: .*unavoidable to disregard payable 0x0000000000000000000000000000000000616263")); + //Even though we normally don't assert on DEBUG logs, this one hardens the test + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: Last remaining account ended up disqualified" + )); } #[test] @@ -1364,10 +1265,14 @@ mod tests { //because the proposed final balances all all way lower than (at least) the half of the original balances assert_eq!(result.accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { - format!("INFO: {test_name}: Consuming wallet is short of MASQ. Seems unavoidable to \ - disregard payable {wallet} at the moment. Reason is the computed possible payment of {\ - proposed_adjusted_balance_in_this_iteration} wei would not be at least half of the original \ - debt {}", (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas()) + format!( + "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ + Seems unavoidable to disregard payable {wallet} at the moment. \ + Reason is the computed possible payment of {} wei \ + would not be at least half of the original debt {}", + proposed_adjusted_balance_in_this_iteration.separate_with_commas(), + (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() + ) }; let log_handler = TestLogHandler::new(); // notice that the proposals grow by dropping one disqualified account in each iteration @@ -1381,7 +1286,7 @@ mod tests { )); log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616832", - 999, + 1000, )); } @@ -1605,14 +1510,14 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // account_1, being the least important one, was eliminated as there wouldn't be enough - // the transaction fee balance for all of them + // account_1, the least important one, was eliminated for missing enough + // transaction fee balance let expected_accounts = { let account_2_adjusted = PayableAccount { balance_wei: 222_000_000_000_000, ..account_2 }; - vec![account_2_adjusted, account_3] + vec![account_3, account_2_adjusted] }; assert_eq!( result, @@ -1697,8 +1602,8 @@ mod tests { context_id: 234 }) ); - TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Consuming wallet is \ - short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ + TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ + being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ would not be at least half of the original debt 600,000,000,000")); } @@ -1909,19 +1814,17 @@ mod tests { }, }; - let mut result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let only_account = result.accounts.remove(0); assert_eq!( - only_account, - PayableAccount { + result.accounts, + vec![PayableAccount { wallet: wallet_3, balance_wei: consuming_wallet_masq_balance, last_paid_timestamp: last_paid_timestamp_3, pending_payable_opt: None, - } + }] ); - assert_eq!(result.accounts.len(), 0); assert_eq!(result.response_skeleton_opt, None); let log_msg = format!( "DEBUG: {test_name}: \n\ @@ -1968,7 +1871,7 @@ mod tests { }; let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); - let cw_masq_balance_minor = (333_000_000_000_000 / 2) - 1; //this is exactly the amount which will provoke an error + let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; //this is exactly the amount which will provoke an error subject.logger = Logger::new(test_name); let setup_msg = PayablePaymentSetup { qualified_payables, @@ -2010,6 +1913,77 @@ mod tests { )); } + #[test] + fn strategies_for_the_initial_check_and_the_actual_adjustment_algorithm_complement_each_other() + { + let test_name = "strategies_for_the_initial_check_and_the_actual_adjustment_algorithm_complement_each_other"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 10_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 550_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); + let balance_3 = 100_000_000_000; + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: balance_3, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + let logger = Logger::new(test_name); + subject.logger = logger.clone(); + // this should fulfill the initial check, but after that the win of the third account will be really tight + let consuming_wallet_masq_balance = (balance_3 / 2) + 1; + let setup_msg = PayablePaymentSetup { + qualified_payables: qualified_payables.clone(), + this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: u128::MAX, + masq_tokens_minor: consuming_wallet_masq_balance, + }, + estimated_gas_limit_per_transaction: 77_000, + desired_transaction_fee_price_major: 24, + }, + )), + response_skeleton_opt: None, + }; + let adjustment_setup = PreparedAdjustment { + original_setup_msg: setup_msg, + adjustment: Adjustment::MasqToken, + }; + + let check_result = PaymentAdjusterReal::check_need_of_masq_balances_adjustment( + &logger, + Either::Left(&qualified_payables), + consuming_wallet_masq_balance, + ); + let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + assert_eq!(check_result, Ok(true)); + assert_eq!( + adjustment_result.accounts, + vec![PayableAccount { + wallet: wallet_3, + balance_wei: consuming_wallet_masq_balance, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }] + ); + assert_eq!(adjustment_result.response_skeleton_opt, None); + } + #[test] fn testing_reliability_by_long_loading_payment_adjuster_with_randomly_generated_accounts() { todo!("write this occasional test") diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index 68713df54..bf709bf4a 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -1,36 +1,30 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use itertools::Itertools; use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - find_largest_debt_account_generic, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, -}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; +use crate::masq_lib::utils::ExpectValue; +use itertools::Itertools; -struct MasqAdjustmentPossibilityVerifier {} +pub struct MasqAdjustmentPossibilityVerifier {} impl MasqAdjustmentPossibilityVerifier { - pub fn verify_adjustment_as_possible( + pub fn verify_adjustment_possibility( &self, accounts: &[&PayableAccount], cw_masq_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - let largest_account = - find_largest_debt_account_generic(accounts, |account| account.balance_wei); - - let sorted = accounts.iter().sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)).collect::>(); - - let largest_debt = sorted.first().expect("at least one must be here"); - - let minimum_required_portion = - - let remaining = sorted.iter().skip(1); - - + // the reasoning is that if the cw balance is extremely low compared to the set of accounts, the real adjustment algorithm will proceed by eliminating the biggest account in each iteration - //TODO you need to make this better !!!! What if the big one is too big against the other ones? + let sorted = accounts + .iter() + .sorted_by(|account_b, account_a| { + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + }) + .collect::>(); + let smallest_account = sorted.first().expectv("qualified payable account"); - if (largest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) + if (smallest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor <= cw_masq_balance_minor { @@ -55,80 +49,83 @@ impl MasqAdjustmentPossibilityVerifier { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; - use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; + use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; #[test] - fn calculate_breaking_line_works(){ + fn calculate_breaking_line_works() { let mut account = make_payable_account(111); account.balance_wei = 300_000_000; - let result = MasqAdjustmentPossibilityVerifier::calculate_breaking_line() + let result = + MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account.balance_wei); - assert_eq!(result) + assert_eq!( + result, + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account.balance_wei) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + ) } - fn test_body_for_adjustment_feasibility_nearly_insufficient( + fn test_body_for_adjustment_possibility_nearly_rejected( original_accounts: Vec, cw_masq_balance: u128, ) { let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = MasqAdjustmentPossibilityVerifier{}; + let subject = MasqAdjustmentPossibilityVerifier {}; - let result = subject.verify_adjustment_as_possible( - &accounts_in_expected_format, - cw_masq_balance, - ); + let result = + subject.verify_adjustment_possibility(&accounts_in_expected_format, cw_masq_balance); assert_eq!(result, Ok(())) } - - #[test] - fn adjustment_feasibility_nearly_insufficient_when_1_less() { + fn adjustment_possibility_nearly_rejected_when_cw_balance_one_more() { let mut account_1 = make_payable_account(111); account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_breaking_line(account_1.balance_wei) + 1; + let cw_masq_balance = + MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_2.balance_wei) + 1; let original_accounts = vec![account_1, account_2]; - test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) } #[test] - fn adjustment_feasibility_nearly_insufficient_when_equal() { + fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { let mut account_1 = make_payable_account(111); account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_breaking_line(account_1.balance_wei); + let cw_masq_balance = + MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_2.balance_wei); let original_accounts = vec![account_1, account_2]; - test_body_for_adjustment_feasibility_nearly_insufficient(original_accounts, cw_masq_balance) + test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) } #[test] - fn adjustment_feasibility_err_from_insufficient_balance() { + fn adjustment_possibility_err_from_insufficient_balance_for_at_least_single_account_adjustment() + { let mut account_1 = make_payable_account(111); account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(222); account_2.balance_wei = 2_000_000_002; let mut account_3 = make_payable_account(333); - account_3.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_breaking_line(account_2.balance_wei) - 1; + account_3.balance_wei = 1_000_000_002; + let cw_masq_balance = + MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_3.balance_wei) - 1; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = MasqAdjustmentPossibilityVerifier{}; + let subject = MasqAdjustmentPossibilityVerifier {}; - let result = assess_potential_masq_adjustment_feasibility( - &accounts_in_expected_format, - cw_masq_balance, - ); + let result = + subject.verify_adjustment_possibility(&accounts_in_expected_format, cw_masq_balance); assert_eq!( result, From c57ab24c4d1beb5c68f141eafda0b904074fd3d6 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 29 Aug 2023 21:54:52 +0200 Subject: [PATCH 075/250] GH-711: refactoring first part --- .../payment_adjuster/adjustment_runners.rs | 13 +- .../payment_adjuster/diagnostics.rs | 2 +- node/src/accountant/payment_adjuster/inner.rs | 54 +-- .../accountant/payment_adjuster/log_fns.rs | 4 +- .../miscellaneous/data_structures.rs | 32 +- .../miscellaneous/helper_functions.rs | 50 +-- node/src/accountant/payment_adjuster/mod.rs | 387 ++++++++---------- .../accountant/payment_adjuster/verifier.rs | 4 +- 8 files changed, 241 insertions(+), 305 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index f7d6fb148..2c71ed5d4 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -52,7 +52,7 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { - return payment_adjuster.begin_with_adjustment_by_transaction_fees( + return payment_adjuster.begin_adjustment_by_transaction_fee( criteria_and_accounts_in_descending_order, limit, ) @@ -62,7 +62,7 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { Ok(Either::Left( payment_adjuster - .propose_adjustment_recursively(criteria_and_accounts_in_descending_order), + .propose_possible_adjustment_recursively(criteria_and_accounts_in_descending_order), )) } } @@ -85,7 +85,8 @@ impl AdjustmentRunner for MasqOnlyRunner { payment_adjuster: &mut PaymentAdjusterReal, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { - payment_adjuster.propose_adjustment_recursively(criteria_and_accounts_in_descending_order) + payment_adjuster + .propose_possible_adjustment_recursively(criteria_and_accounts_in_descending_order) } } @@ -93,7 +94,7 @@ fn adjust_last_one( payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Option { - let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance(); + let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance_minor(); let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { last_account.balance_wei } else { @@ -108,7 +109,6 @@ fn adjust_last_one( let mut proposed_adjustment_vec = vec![AdjustedAccountBeforeFinalization::new( last_account, proposed_adjusted_balance, - 0, )]; let logger = &payment_adjuster.logger; @@ -186,7 +186,6 @@ mod tests { expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { original_account: account_1, proposed_adjusted_balance: cw_balance, - criteria_sum: 0, //TODO you should delete this field, but when the time is right }]) ) } @@ -222,7 +221,6 @@ mod tests { Some(AdjustedAccountBeforeFinalization { original_account: account, proposed_adjusted_balance: 4_333_222_111, - criteria_sum: 0, }) ) } @@ -299,7 +297,6 @@ mod tests { let account_info = AdjustedAccountBeforeFinalization { original_account: make_payable_account(123), proposed_adjusted_balance: 123_456_789, - criteria_sum: 987_654_321, }; let result = empty_or_single_element(Some(account_info.clone())); diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 2f6d14138..4ea846b0f 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 77e3af27e..02f051103 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -9,23 +9,23 @@ pub trait PaymentAdjusterInner { fn transaction_fee_count_limit_opt(&self) -> Option { PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") } - fn original_cw_masq_balance(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance()") + fn original_cw_masq_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance_minor()") } - fn unallocated_cw_masq_balance(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance()") + fn unallocated_cw_masq_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance_minor()") } - fn update_unallocated_cw_balance(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance()") + fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance_minor()") } } pub struct PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, - original_cw_masq_balance: u128, - unallocated_cw_masq_balance: u128, + original_cw_masq_balance_minor: u128, + unallocated_cw_masq_balance_minor: u128, } impl PaymentAdjusterInnerReal { @@ -37,8 +37,8 @@ impl PaymentAdjusterInnerReal { Self { now, transaction_fee_count_limit_opt, - original_cw_masq_balance: cw_masq_balance, - unallocated_cw_masq_balance: cw_masq_balance, + original_cw_masq_balance_minor: cw_masq_balance, + unallocated_cw_masq_balance_minor: cw_masq_balance, } } } @@ -50,18 +50,18 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn transaction_fee_count_limit_opt(&self) -> Option { self.transaction_fee_count_limit_opt } - fn original_cw_masq_balance(&self) -> u128 { - self.original_cw_masq_balance + fn original_cw_masq_balance_minor(&self) -> u128 { + self.original_cw_masq_balance_minor } - fn unallocated_cw_masq_balance(&self) -> u128 { - self.unallocated_cw_masq_balance + fn unallocated_cw_masq_balance_minor(&self) -> u128 { + self.unallocated_cw_masq_balance_minor } - fn update_unallocated_cw_balance(&mut self, subtrahend: u128) { + fn update_unallocated_cw_balance_minor(&mut self, subtrahend: u128) { let updated_thought_cw_balance = self - .unallocated_cw_masq_balance + .unallocated_cw_masq_balance_minor .checked_sub(subtrahend) .expect("subtracting a small enough number"); - self.unallocated_cw_masq_balance = updated_thought_cw_balance + self.unallocated_cw_masq_balance_minor = updated_thought_cw_balance } } @@ -98,8 +98,8 @@ mod tests { result.transaction_fee_count_limit_opt, transaction_fee_count_limit_opt ); - assert_eq!(result.original_cw_masq_balance, cw_masq_balance); - assert_eq!(result.unallocated_cw_masq_balance, cw_masq_balance) + assert_eq!(result.original_cw_masq_balance_minor, cw_masq_balance); + assert_eq!(result.unallocated_cw_masq_balance_minor, cw_masq_balance) } #[test] @@ -124,31 +124,31 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the original_cw_masq_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the original_cw_masq_balance_minor() method in PaymentAdjusterInner" )] - fn inner_null_calling_original_cw_masq_balance() { + fn inner_null_calling_original_cw_masq_balance_minor() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.original_cw_masq_balance(); + let _ = subject.original_cw_masq_balance_minor(); } #[test] #[should_panic( - expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_unallocated_cw_balance() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.unallocated_cw_masq_balance(); + let _ = subject.unallocated_cw_masq_balance_minor(); } #[test] #[should_panic( - expected = "Called the null implementation of the update_unallocated_cw_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the update_unallocated_cw_balance_minor() method in PaymentAdjusterInner" )] - fn inner_null_calling_update_unallocated_cw_balance() { + fn inner_null_calling_update_unallocated_cw_balance_minor() { let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.update_unallocated_cw_balance(123); + let _ = subject.update_unallocated_cw_balance_minor(123); } } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 21c15df95..9be9ab895 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,9 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, DisqualifiedPayableAccount, -}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 2b003c17a..1cab8e7e1 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::sub_lib::wallet::Wallet; #[derive(Debug)] pub enum AdjustmentIterationResult { @@ -14,7 +13,7 @@ pub enum AdjustmentIterationResult { #[derive(Debug)] pub enum SpecialTreatment { - TreatInsignificantAccount(DisqualifiedPayableAccount), + TreatInsignificantAccount, TreatOutweighedAccounts(Vec), } @@ -22,25 +21,19 @@ pub enum SpecialTreatment { pub struct AdjustedAccountBeforeFinalization { pub original_account: PayableAccount, pub proposed_adjusted_balance: u128, - pub criteria_sum: u128, } impl AdjustedAccountBeforeFinalization { - pub fn new( - original_account: PayableAccount, - proposed_adjusted_balance: u128, - criteria_sum: u128, - ) -> Self { + pub fn new(original_account: PayableAccount, proposed_adjusted_balance: u128) -> Self { Self { original_account, proposed_adjusted_balance, - criteria_sum, } } pub fn finalize_collection( account_infos: Vec, - resolution: ResolutionAfterFullyDetermined, + resolution: ProposedAdjustmentResolution, ) -> Vec { account_infos .into_iter() @@ -50,28 +43,11 @@ impl AdjustedAccountBeforeFinalization { } #[derive(Clone, Copy)] -pub enum ResolutionAfterFullyDetermined { +pub enum ProposedAdjustmentResolution { Finalize, Revert, } -#[derive(Debug, PartialEq, Eq)] -pub struct DisqualifiedPayableAccount { - pub wallet: Wallet, - pub proposed_adjusted_balance: u128, - pub original_balance: u128, -} - -impl DisqualifiedPayableAccount { - pub fn new(wallet: Wallet, original_balance: u128, proposed_adjusted_balance: u128) -> Self { - Self { - wallet, - proposed_adjusted_balance, - original_balance, - } - } -} - // sets the minimal percentage of the original balance that must be // proposed after the adjustment or the account will be eliminated for insignificance #[derive(Debug, PartialEq, Eq)] diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 6ca778ff4..85e12951b 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnos use crate::accountant::payment_adjuster::log_fns::log_info_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, - ResolutionAfterFullyDetermined, + ProposedAdjustmentResolution, }; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -41,17 +41,17 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn cut_back_by_excessive_transaction_fee( - weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, +pub fn reduce_collection_size_by_affordable_transaction_fee( + criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, limit: u16, ) -> Vec<(u128, PayableAccount)> { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", limit, - weights_and_accounts_in_descending_order.len() + criteria_and_accounts_in_descending_order.len() ); - weights_and_accounts_in_descending_order + criteria_and_accounts_in_descending_order .into_iter() .take(limit as usize) .collect() @@ -123,7 +123,7 @@ pub fn find_largest_disqualified_account<'a>( pub fn exhaust_cw_balance_totally( verified_accounts: Vec, - original_cw_masq_balance: u128, + original_cw_masq_balance_minor: u128, ) -> Vec { fn fold_guts( status: ExhaustionStatus, @@ -162,12 +162,12 @@ pub fn exhaust_cw_balance_totally( account_info.proposed_adjusted_balance }); - let cw_reminder = original_cw_masq_balance + let cw_reminder = original_cw_masq_balance_minor .checked_sub(adjusted_balances_total) .unwrap_or_else(|| { panic!( "remainder should've been a positive number but was not after {} - {}", - original_cw_masq_balance, adjusted_balances_total + original_cw_masq_balance_minor, adjusted_balances_total ) }); @@ -256,7 +256,7 @@ impl ExhaustionStatus { fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { let finalized_account = PayableAccount::from(( non_finalized_account_info, - ResolutionAfterFullyDetermined::Finalize, + ProposedAdjustmentResolution::Finalize, )); self.already_finalized_accounts.push(finalized_account); self @@ -334,21 +334,21 @@ pub fn x_or_1(x: u128) -> u128 { impl From<( AdjustedAccountBeforeFinalization, - ResolutionAfterFullyDetermined, + ProposedAdjustmentResolution, )> for PayableAccount { fn from( (account_info, resolution): ( AdjustedAccountBeforeFinalization, - ResolutionAfterFullyDetermined, + ProposedAdjustmentResolution, ), ) -> Self { match resolution { - ResolutionAfterFullyDetermined::Finalize => PayableAccount { + ProposedAdjustmentResolution::Finalize => PayableAccount { balance_wei: account_info.proposed_adjusted_balance, ..account_info.original_account }, - ResolutionAfterFullyDetermined::Revert => account_info.original_account, + ProposedAdjustmentResolution::Revert => account_info.original_account, } } } @@ -546,7 +546,6 @@ mod tests { AdjustedAccountBeforeFinalization { original_account, proposed_adjusted_balance, - criteria_sum: 0, } }) .collect() @@ -578,8 +577,7 @@ mod tests { result, &AdjustedAccountBeforeFinalization { original_account: account_2, - proposed_adjusted_balance: 222_000_000, - criteria_sum: 0 + proposed_adjusted_balance: 222_000_000 } ) } @@ -609,8 +607,7 @@ mod tests { result, &AdjustedAccountBeforeFinalization { original_account: account_4, - proposed_adjusted_balance: 444_000_000, - criteria_sum: 0, + proposed_adjusted_balance: 444_000_000 } ) } @@ -636,8 +633,7 @@ mod tests { result, &AdjustedAccountBeforeFinalization { original_account: account_1, - proposed_adjusted_balance: 100_111_000, - criteria_sum: 0 + proposed_adjusted_balance: 100_111_000 } ) } @@ -652,7 +648,6 @@ mod tests { pending_payable_opt: None, }, proposed_adjusted_balance: 9_000_000_000, - criteria_sum: 123456789, }; let (outweighed, ok) = @@ -700,7 +695,7 @@ mod tests { let accounts_with_individual_criteria = subject .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); let criteria_total = criteria_total(&accounts_with_individual_criteria); - let non_finalized_adjusted_accounts = subject.compute_non_finalized_adjusted_accounts( + let non_finalized_adjusted_accounts = subject.compute_adjusted_but_non_finalized_accounts( accounts_with_individual_criteria, criteria_total, ); @@ -726,7 +721,6 @@ mod tests { pending_payable_opt: None, }, proposed_adjusted_balance, - criteria_sum: 123456, } } @@ -916,26 +910,20 @@ mod tests { let payable_account_1 = prepare_account(1); let payable_account_2 = prepare_account(2); let payable_account_3 = prepare_account(3); - const IRRELEVANT_CRITERIA_SUM: u128 = 1111; let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; let proposed_ok_balance = edge + 1; - let account_info_1 = AdjustedAccountBeforeFinalization::new( - payable_account_1, - proposed_ok_balance, - IRRELEVANT_CRITERIA_SUM, - ); + let account_info_1 = + AdjustedAccountBeforeFinalization::new(payable_account_1, proposed_ok_balance); let proposed_bad_balance_because_equal = edge; let account_info_2 = AdjustedAccountBeforeFinalization::new( payable_account_2, proposed_bad_balance_because_equal, - IRRELEVANT_CRITERIA_SUM, ); let proposed_bad_balance_because_smaller = edge - 1; let account_info_3 = AdjustedAccountBeforeFinalization::new( payable_account_3, proposed_bad_balance_because_smaller, - IRRELEVANT_CRITERIA_SUM, ); let accounts_with_unchecked_adjustment = vec![ account_info_1, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 59e916e57..2e62f667f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -27,20 +27,19 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::log_fns::{ before_and_after_debug_msg, log_adjustment_by_masq_required, log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient, - log_info_for_disqualified_account, log_insufficient_transaction_fee_balance, + log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DisqualifiedPayableAccount, - ResolutionAfterFullyDetermined, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, cut_back_by_excessive_transaction_fee, - exhaust_cw_balance_totally, maybe_find_an_account_to_disqualify_in_this_iteration, - possibly_outweighed_accounts_fold_guts, rebuild_accounts, sort_in_descendant_order_by_weights, - sum_as, + compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, + maybe_find_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, + rebuild_accounts, reduce_collection_size_by_affordable_transaction_fee, + sort_in_descendant_order_by_weights, sum_as, }; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::scanners::payable_scan_setup_msgs::{ @@ -88,6 +87,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { msg: &PayablePaymentSetup, ) -> Result, PaymentAdjusterError> { let qualified_payables = msg.qualified_payables.as_slice(); + let required_tx_count = qualified_payables.len(); + let this_stage_data = match msg .this_stage_data_opt .as_ref() @@ -97,27 +98,27 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let transaction_fee_adjustment_opt = - Self::determine_transactions_count_limit_by_transaction_fee( + Self::determine_transaction_count_limit_by_transaction_fee( &this_stage_data, - qualified_payables.len(), + required_tx_count, &self.logger, )?; - let is_masq_adjustment_needed = Self::check_need_of_masq_balances_adjustment( + let is_masq_adjustment_needed = Self::check_need_of_masq_adjustment( &self.logger, Either::Left(qualified_payables), this_stage_data.consuming_wallet_balances.masq_tokens_minor, )?; - match (transaction_fee_adjustment_opt, is_masq_adjustment_needed) { - (Some(affordable_transaction_count), _) => { - Ok(Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count, - })) - } - (None, true) => Ok(Some(Adjustment::MasqToken)), - _ => Ok(None), - } + let adjustment_opt = match (transaction_fee_adjustment_opt, is_masq_adjustment_needed) { + (Some(affordable_transaction_count), _) => Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count, + }), + (None, true) => Some(Adjustment::MasqToken), + _ => None, + }; + + Ok(adjustment_opt) } fn adjust_payments( @@ -135,12 +136,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.initialize_inner(current_stage_data, required_adjustment, now); - let debug_info_opt = self.logger.debug_enabled().then(|| { - qualified_payables - .iter() - .map(|account| (account.wallet.clone(), account.balance_wei)) - .collect::>() - }); + let debug_info_opt = self.debug_info_opt(&qualified_payables); let adjusted_accounts = self.run_adjustment(qualified_payables)?; @@ -173,7 +169,7 @@ impl PaymentAdjusterReal { } } - fn determine_transactions_count_limit_by_transaction_fee( + fn determine_transaction_count_limit_by_transaction_fee( tech_info: &FinancialAndTechDetails, required_tx_count: usize, logger: &Logger, @@ -182,22 +178,27 @@ impl PaymentAdjusterReal { tech_info.estimated_gas_limit_per_transaction as u128 * tech_info.desired_transaction_fee_price_major as u128; - let transaction_fee_required_minor: u128 = + let per_transaction_requirement_minor: u128 = gwei_to_wei(transaction_fee_required_per_transaction_major); - let available_balance_minor = tech_info.consuming_wallet_balances.transaction_fee_minor; - let max_doable_tx_count_loose_boundaries = - available_balance_minor / transaction_fee_required_minor; + + let cw_transaction_fee_balance_minor = + tech_info.consuming_wallet_balances.transaction_fee_minor; + + let max_doable_tx_count = + cw_transaction_fee_balance_minor / per_transaction_requirement_minor; + let (max_doable_tx_count_u16, required_tx_count_u16) = - Self::cast_big_numbers_down_with_possible_ceiling( - max_doable_tx_count_loose_boundaries, + Self::put_big_unsigned_integers_under_u16_ceiling( + max_doable_tx_count, required_tx_count, ); + if max_doable_tx_count_u16 == 0 { Err(PaymentAdjusterError::AnalysisError( AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: required_tx_count, - per_transaction_requirement_minor: transaction_fee_required_minor, - cw_transaction_fee_balance_minor: available_balance_minor, + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, }, )) } else if max_doable_tx_count_u16 >= required_tx_count_u16 { @@ -206,14 +207,14 @@ impl PaymentAdjusterReal { log_insufficient_transaction_fee_balance( logger, required_tx_count_u16, - tech_info.consuming_wallet_balances.transaction_fee_minor, + cw_transaction_fee_balance_minor, max_doable_tx_count_u16, ); Ok(Some(max_doable_tx_count_u16)) } } - fn cast_big_numbers_down_with_possible_ceiling( + fn put_big_unsigned_integers_under_u16_ceiling( max_doable_tx_count: u128, required_tx_count: usize, ) -> (u16, u16) { @@ -223,7 +224,7 @@ impl PaymentAdjusterReal { ) } - fn check_need_of_masq_balances_adjustment( + fn check_need_of_masq_adjustment( logger: &Logger, payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, cw_masq_balance_minor: u128, @@ -245,6 +246,7 @@ impl PaymentAdjusterReal { } else { MasqAdjustmentPossibilityVerifier {} .verify_adjustment_possibility(&qualified_payables, cw_masq_balance_minor)?; + log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance_minor); Ok(true) } @@ -263,8 +265,10 @@ impl PaymentAdjusterReal { Adjustment::MasqToken => None, }; let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_minor; + let inner = PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); + self.inner = Box::new(inner); } @@ -272,23 +276,26 @@ impl PaymentAdjusterReal { &mut self, qualified_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let accounts = self.calculate_criteria_and_propose_adjustment_recursively( + let accounts = self.calculate_criteria_and_propose_adjustments_recursively( qualified_accounts, MasqAndTransactionFeeRunner {}, )?; match accounts { - Either::Left(non_exhausted_accounts) => Ok(exhaust_cw_balance_totally( - non_exhausted_accounts, - self.inner.original_cw_masq_balance(), - )), + Either::Left(non_exhausted_accounts) => { + let accounts_by_fully_exhausted_cw = exhaust_cw_balance_totally( + non_exhausted_accounts, + self.inner.original_cw_masq_balance_minor(), + ); + Ok(accounts_by_fully_exhausted_cw) + } Either::Right(finalized_accounts) => Ok(finalized_accounts), } } - fn calculate_criteria_and_propose_adjustment_recursively( + fn calculate_criteria_and_propose_adjustments_recursively( &mut self, mut unresolved_qualified_accounts: Vec, - purpose_specific_adjuster: A, + adjustment_runner: A, ) -> R where A: AdjustmentRunner, @@ -299,32 +306,34 @@ impl PaymentAdjusterReal { ); if unresolved_qualified_accounts.len() == 1 { - return purpose_specific_adjuster + return adjustment_runner .adjust_last_one(self, unresolved_qualified_accounts.remove(0)); } let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); - purpose_specific_adjuster.adjust_multiple(self, accounts_with_individual_criteria_sorted) + adjustment_runner.adjust_multiple(self, accounts_with_individual_criteria_sorted) } - fn begin_with_adjustment_by_transaction_fees( + fn begin_adjustment_by_transaction_fee( &mut self, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, - already_known_count_limit: u16, + already_known_affordable_transaction_count: u16, ) -> Result< Either, Vec>, PaymentAdjusterError, > { - let transaction_fee_affordable_weighted_accounts = cut_back_by_excessive_transaction_fee( - criteria_and_accounts_in_descending_order, - already_known_count_limit, - ); - let unallocated_balance = self.inner.unallocated_cw_masq_balance(); - let definite_answer = match Self::check_need_of_masq_balances_adjustment( + let weighted_accounts_affordable_by_transaction_fee = + reduce_collection_size_by_affordable_transaction_fee( + criteria_and_accounts_in_descending_order, + already_known_affordable_transaction_count, + ); + let unallocated_balance = self.inner.unallocated_cw_masq_balance_minor(); + + let is_masq_adjustment_needed = match Self::check_need_of_masq_adjustment( &self.logger, - Either::Right(&transaction_fee_affordable_weighted_accounts), + Either::Right(&weighted_accounts_affordable_by_transaction_fee), unallocated_balance, ) { Ok(answer) => answer, @@ -335,15 +344,18 @@ impl PaymentAdjusterReal { return Err(e); } }; - match definite_answer { + + match is_masq_adjustment_needed { true => { - let result_awaiting_verification = self - .propose_adjustment_recursively(transaction_fee_affordable_weighted_accounts); - Ok(Either::Left(result_awaiting_verification)) + let adjustment_result_before_verification = self + .propose_possible_adjustment_recursively( + weighted_accounts_affordable_by_transaction_fee, + ); + Ok(Either::Left(adjustment_result_before_verification)) } false => { let finalized_accounts = - rebuild_accounts(transaction_fee_affordable_weighted_accounts); + rebuild_accounts(weighted_accounts_affordable_by_transaction_fee); Ok(Either::Right(finalized_accounts)) } } @@ -353,16 +365,16 @@ impl PaymentAdjusterReal { &self, accounts: Vec, ) -> Vec<(u128, PayableAccount)> { - let zero_criteria_accounts = Self::initialize_zero_criteria(accounts); - self.apply_criteria(zero_criteria_accounts) + let accounts_with_zero_criteria = Self::initialize_zero_criteria(accounts); + self.apply_criteria(accounts_with_zero_criteria) } - fn propose_adjustment_recursively( + fn propose_possible_adjustment_recursively( &mut self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> Vec { let adjustment_iteration_result = - self.perform_masq_token_adjustment(accounts_with_individual_criteria); + self.perform_masq_adjustment(accounts_with_individual_criteria); let (here_decided_accounts, downstream_decided_accounts) = self.resolve_iteration_result(adjustment_iteration_result); @@ -391,9 +403,7 @@ impl PaymentAdjusterReal { remaining, } => { let here_decided_accounts = match special_case { - TreatInsignificantAccount(disqualified) => { - //log_info_for_disqualified_account(&self.logger, &disqualified); - + TreatInsignificantAccount => { if remaining.is_empty() { debug!(self.logger, "Last remaining account ended up disqualified"); @@ -403,13 +413,13 @@ impl PaymentAdjusterReal { vec![] } TreatOutweighedAccounts(outweighed) => { - self.adjust_cw_balance_down_before_another_iteration(&outweighed); + self.adjust_cw_balance_down_as_result_of_this_last_iteration(&outweighed); outweighed } }; let down_stream_decided_accounts = self - .calculate_criteria_and_propose_adjustment_recursively( + .calculate_criteria_and_propose_adjustments_recursively( remaining, MasqOnlyRunner {}, ); @@ -450,23 +460,12 @@ impl PaymentAdjusterReal { collected_accounts_with_criteria } - fn perform_masq_token_adjustment( + fn perform_masq_adjustment( &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { let criteria_total = criteria_total(&accounts_with_individual_criteria); - self.recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria, - criteria_total, - ) - } - - fn recreate_accounts_with_proportioned_balances( - &self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - criteria_total: u128, - ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self.compute_non_finalized_adjusted_accounts( + let non_finalized_adjusted_accounts = self.compute_adjusted_but_non_finalized_accounts( accounts_with_individual_criteria, criteria_total, ); @@ -488,28 +487,20 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) } - fn compute_non_finalized_adjusted_accounts( + fn compute_adjusted_but_non_finalized_accounts( &self, accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> Vec { - let cw_masq_balance = self.inner.unallocated_cw_masq_balance(); - let multiplication_coeff = - compute_fraction_preventing_mul_coeff(cw_masq_balance, criteria_total); - let cw_masq_balance_u256 = U256::from(cw_masq_balance); - let criteria_total_u256 = U256::from(criteria_total); - let multiplication_coeff_u256 = U256::from(multiplication_coeff); + let cw_masq_balance = self.inner.unallocated_cw_masq_balance_minor(); + let cpm_coeff = compute_fraction_preventing_mul_coeff(cw_masq_balance, criteria_total); + let multiplication_coeff_u256 = U256::from(cpm_coeff); - let proportional_fragment_of_cw_balance = cw_masq_balance_u256 - .checked_mul(multiplication_coeff_u256) - .unwrap_or_else(|| { - panic!( - "mul overflow from {} * {}", - criteria_total_u256, multiplication_coeff_u256 - ) - }) - .checked_div(criteria_total_u256) - .expect("div overflow"); + let proportional_fragment_of_cw_balance = Self::compute_proportional_fragment( + cw_masq_balance, + criteria_total, + multiplication_coeff_u256, + ); let turn_account_into_adjusted_account_before_finalization = |(criteria_sum, account): (u128, PayableAccount)| { @@ -520,11 +511,7 @@ impl PaymentAdjusterReal { non_finalized_adjusted_accounts_diagnostics(&account, proposed_adjusted_balance); - AdjustedAccountBeforeFinalization::new( - account, - proposed_adjusted_balance, - criteria_sum, - ) + AdjustedAccountBeforeFinalization::new(account, proposed_adjusted_balance) }; accounts_with_individual_criteria @@ -533,56 +520,52 @@ impl PaymentAdjusterReal { .collect() } + fn compute_proportional_fragment( + cw_masq_balance: u128, + criteria_total: u128, + multiplication_coeff: U256, + ) -> U256 { + let cw_masq_balance_u256 = U256::from(cw_masq_balance); + let criteria_total_u256 = U256::from(criteria_total); + + cw_masq_balance_u256 + .checked_mul(multiplication_coeff) + .unwrap_or_else(|| { + panic!( + "mul overflow from {} * {}", + criteria_total_u256, multiplication_coeff + ) + }) + .checked_div(criteria_total_u256) + .expect("div overflow") + } + fn consider_account_disqualification( non_finalized_adjusted_accounts: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { - if let Some(disq_account_wallet) = maybe_find_an_account_to_disqualify_in_this_iteration( - &non_finalized_adjusted_accounts, - logger, - ) { - let init = ( - None, - Vec::with_capacity(non_finalized_adjusted_accounts.len() - 1), - ); - - type FoldAccumulator = ( - Option, - Vec, - ); - let fold_guts = |(disqualified_acc_opt, mut remaining_accounts): FoldAccumulator, - current_account: AdjustedAccountBeforeFinalization| - -> FoldAccumulator { - if current_account.original_account.wallet == disq_account_wallet { - (Some(current_account), remaining_accounts) - } else { - remaining_accounts.push(current_account); - (disqualified_acc_opt, remaining_accounts) - } - }; - - let (single_disqualified, remaining) = non_finalized_adjusted_accounts + if let Some(disqualified_account_wallet) = + maybe_find_an_account_to_disqualify_in_this_iteration( + &non_finalized_adjusted_accounts, + logger, + ) + { + let remaining = non_finalized_adjusted_accounts .into_iter() - .fold(init, fold_guts); - - let debugable_disqualified = { - let account_info = - single_disqualified.expect("already verified disqualified account is gone"); - DisqualifiedPayableAccount::new( - account_info.original_account.wallet, - account_info.original_account.balance_wei, - account_info.proposed_adjusted_balance, - ) - }; + .filter(|account_info| { + account_info.original_account.wallet != disqualified_account_wallet + }) + .collect::>(); let remaining_reverted = remaining .into_iter() .map(|account_info| { - PayableAccount::from((account_info, ResolutionAfterFullyDetermined::Revert)) + PayableAccount::from((account_info, ProposedAdjustmentResolution::Revert)) }) .collect(); + Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatInsignificantAccount(debugable_disqualified), + special_case: TreatInsignificantAccount, remaining: remaining_reverted, }) } else { @@ -594,16 +577,17 @@ impl PaymentAdjusterReal { &self, non_finalized_adjusted_accounts: Vec, ) -> Either, AdjustmentIterationResult> { + let init = (vec![], vec![]); let (outweighed, passing_through) = non_finalized_adjusted_accounts .into_iter() - .fold((vec![], vec![]), possibly_outweighed_accounts_fold_guts); + .fold(init, possibly_outweighed_accounts_fold_guts); if outweighed.is_empty() { Either::Left(passing_through) } else { let remaining = AdjustedAccountBeforeFinalization::finalize_collection( passing_through, - ResolutionAfterFullyDetermined::Revert, + ProposedAdjustmentResolution::Revert, ); Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { special_case: TreatOutweighedAccounts(outweighed), @@ -612,22 +596,35 @@ impl PaymentAdjusterReal { } } - fn adjust_cw_balance_down_before_another_iteration( + fn adjust_cw_balance_down_as_result_of_this_last_iteration( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { let subtrahend_total: u128 = sum_as(processed_outweighed, |account| { account.proposed_adjusted_balance }); - self.inner.update_unallocated_cw_balance(subtrahend_total); + self.inner + .update_unallocated_cw_balance_minor(subtrahend_total); diagnostics!( "LOWERED CW BALANCE", "Unallocated balance lowered by {} to {}", subtrahend_total, - self.inner.unallocated_cw_masq_balance() + self.inner.unallocated_cw_masq_balance_minor() ) } + + fn debug_info_opt( + &self, + qualified_payables: &[PayableAccount], + ) -> Option> { + self.logger.debug_enabled().then(|| { + qualified_payables + .iter() + .map(|account| (account.wallet.clone(), account.balance_wei)) + .collect::>() + }) + } } #[derive(Debug, PartialEq, Eq)] @@ -649,7 +646,7 @@ pub enum AnalysisError { per_transaction_requirement_minor: u128, cw_transaction_fee_balance_minor: u128, }, - RiskOfAdjustmentWithTooLowMASQBalances { + RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: usize, cw_masq_balance_minor: u128, }, @@ -662,13 +659,15 @@ impl Display for PaymentAdjusterError { AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx {number_of_accounts,per_transaction_requirement_minor, cw_transaction_fee_balance_minor } => write!(f, "Found less transaction fee balance than required by one payment. \ Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ - Current consuming wallet balance: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), + Current consuming wallet balance: {} wei", number_of_accounts, + per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas()), - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances {number_of_accounts, cw_masq_balance_minor } => - write!(f,"Analysis has revealed possibly unfavourable payment adjustments given \ - the anticipated resulting payments from the limited resources. Please, provide more \ - funds instead. Number of canceled payments: {}. Current consuming wallet balance: \ - {} wei of MASQ", number_of_accounts.separate_with_commas(),cw_masq_balance_minor.separate_with_commas()) + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated {number_of_accounts, cw_masq_balance_minor } => + write!(f,"Analysis has projected likely unacceptable adjustment leaving all \ + accounts with too low resulted balances. Please, provide more funds instead. \ + Number of canceled payments: {}. Current consuming wallet balance: {} wei of MASQ", + number_of_accounts.separate_with_commas(), + cw_masq_balance_minor.separate_with_commas()) }, PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!(f, "Despite \ the preliminary analysis had expected a possibility to compute some executable \ @@ -681,10 +680,8 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, DisqualifiedPayableAccount, SpecialTreatment, - }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, @@ -712,15 +709,15 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the unallocated_cw_masq_balance() method in PaymentAdjusterInner" + expected = "Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" )] fn payment_adjuster_new_is_created_with_inner_null() { let result = PaymentAdjusterReal::new(); - let _ = result.inner.unallocated_cw_masq_balance(); + let _ = result.inner.unallocated_cw_masq_balance_minor(); } - struct PayableBalancesAndCWBAlanceInTest { + struct PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: Vec, cw_balance_major: u64, } @@ -734,7 +731,7 @@ mod tests { subject.logger = logger; //masq balance > payments let msg_1 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBAlanceInTest { + Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![85, 14], cw_balance_major: 100, }), @@ -742,7 +739,7 @@ mod tests { ); //masq balance = payments let msg_2 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBAlanceInTest { + Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![85, 15], cw_balance_major: 100, }), @@ -789,7 +786,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBAlanceInTest { + Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![85, 16], cw_balance_major: 100, }), @@ -809,7 +806,7 @@ mod tests { fn test_positive_transaction_fee_check_vs_masq_check( test_name: &str, - masq_balances_setup_opt: Option, + masq_balances_setup_opt: Option, ) -> Result, PaymentAdjusterError> { init_test_logging(); let logger = Logger::new(test_name); @@ -864,7 +861,7 @@ mod tests { ) { let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_masq_balance_is_feasibly_adjustable"; let masq_low_but_ok_major = 300 + 500; - let masq_balances_setup_opt = Some(PayableBalancesAndCWBAlanceInTest { + let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![120, 300, 500], cw_balance_major: masq_low_but_ok_major, }); @@ -885,7 +882,7 @@ mod tests { fn search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low() { let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low"; let masq_too_low_major = 120 / 2 - 1; // this will kick the limit - let masq_balances_setup_opt = Some(PayableBalancesAndCWBAlanceInTest { + let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![120, 300, 500], cw_balance_major: masq_too_low_major, }); @@ -896,7 +893,7 @@ mod tests { assert_eq!( result, Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, cw_masq_balance_minor: gwei_to_wei(masq_too_low_major) } @@ -911,7 +908,7 @@ mod tests { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBAlanceInTest { + Some(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![123], cw_balance_major: 444, }), @@ -942,15 +939,15 @@ mod tests { vec![ ( PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 5, cw_masq_balance_minor: 333_000_000, }, ), - "Analysis has revealed possibly unfavourable payment adjustments given \ - the anticipated resulting payments from the limited resources. Please, \ - provide more funds instead. Number of canceled payments: 5. Current \ - consuming wallet balance: 333,000,000 wei of MASQ", + "Analysis has projected likely unacceptable adjustment leaving all accounts \ + with too low resulted balances. Please, provide more funds instead. Number \ + of canceled payments: 5. Current consuming wallet balance: 333,000,000 wei \ + of MASQ", ), ( PaymentAdjusterError::AnalysisError( @@ -987,7 +984,7 @@ mod tests { }) .map(|max_doable_txs_count_u256| { let (doable_txs_count, _) = - PaymentAdjusterReal::cast_big_numbers_down_with_possible_ceiling( + PaymentAdjusterReal::put_big_unsigned_integers_under_u16_ceiling( max_doable_txs_count_u256, 123, ); @@ -1014,7 +1011,7 @@ mod tests { }) .map(|required_tx_count_usize| { let (_, required_tx_count) = - PaymentAdjusterReal::cast_big_numbers_down_with_possible_ceiling( + PaymentAdjusterReal::put_big_unsigned_integers_under_u16_ceiling( 123, required_tx_count_usize, ); @@ -1087,7 +1084,7 @@ mod tests { let qualified_payables = vec![account_1.clone(), account_2.clone()]; let mut result = subject - .calculate_criteria_and_propose_adjustment_recursively( + .calculate_criteria_and_propose_adjustments_recursively( qualified_payables.clone(), MasqAndTransactionFeeRunner {}, ) @@ -1172,26 +1169,16 @@ mod tests { let accounts = vec![account_1.clone(), account_2.clone()]; let accounts_with_individual_criteria = subject.calculate_criteria_sums_for_accounts(accounts); - let criteria_total = criteria_total(&accounts_with_individual_criteria); - let result = subject.recreate_accounts_with_proportioned_balances( - accounts_with_individual_criteria.clone(), - criteria_total, - ); + let result = subject.perform_masq_adjustment(accounts_with_individual_criteria.clone()); - let (disqualified, remaining) = match result { + let remaining = match result { AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatInsignificantAccount(disqualified), + special_case: TreatInsignificantAccount, remaining, - } => (disqualified, remaining), + } => remaining, x => panic!("we expected to see a disqualified account but got: {:?}", x), }; - let expected_disqualified_account = DisqualifiedPayableAccount { - wallet: wallet_2, - proposed_adjusted_balance: 890_785_846, - original_balance: balance_2, - }; - assert_eq!(disqualified, expected_disqualified_account); assert_eq!(remaining, vec![account_1]) } @@ -1202,13 +1189,8 @@ mod tests { let test_name = "there_are_safe_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts"; let mut subject = make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); - let insignificant_account = DisqualifiedPayableAccount { - wallet: make_wallet("abc"), - proposed_adjusted_balance: 123, - original_balance: 600, - }; let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: SpecialTreatment::TreatInsignificantAccount(insignificant_account), + special_case: TreatInsignificantAccount, remaining: vec![], }; @@ -1900,7 +1882,7 @@ mod tests { assert_eq!( result, Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 2, cw_masq_balance_minor } @@ -1964,7 +1946,7 @@ mod tests { adjustment: Adjustment::MasqToken, }; - let check_result = PaymentAdjusterReal::check_need_of_masq_balances_adjustment( + let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( &logger, Either::Left(&qualified_payables), consuming_wallet_masq_balance, @@ -1984,11 +1966,6 @@ mod tests { assert_eq!(adjustment_result.response_skeleton_opt, None); } - #[test] - fn testing_reliability_by_long_loading_payment_adjuster_with_randomly_generated_accounts() { - todo!("write this occasional test") - } - struct TransactionFeeConditionsInTest { desired_transaction_fee_price_per_major: u64, number_of_accounts: usize, @@ -1997,10 +1974,10 @@ mod tests { } fn make_payable_setup_msg_coming_from_blockchain_bridge( - masq_balances_opt: Option, + masq_balances_opt: Option, transaction_fee_conditions_opt: Option, ) -> PayablePaymentSetup { - let masq_balances_setup = masq_balances_opt.unwrap_or(PayableBalancesAndCWBAlanceInTest { + let masq_balances_setup = masq_balances_opt.unwrap_or(PayableBalancesAndCWBalanceInTest { balances_of_accounts_major: vec![1, 1], cw_balance_major: u64::MAX, }); diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index bf709bf4a..f73c91be5 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -31,7 +31,7 @@ impl MasqAdjustmentPossibilityVerifier { Ok(()) } else { Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: accounts.len(), cw_masq_balance_minor, }, @@ -130,7 +130,7 @@ mod tests { assert_eq!( result, Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfAdjustmentWithTooLowMASQBalances { + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, cw_masq_balance_minor: cw_masq_balance } From dbab578dc84ed9bcf40b2123d27d671091a0771c Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 1 Sep 2023 21:43:33 +0200 Subject: [PATCH 076/250] GH-711: finished the main mod file by auto review --- node/src/accountant/mod.rs | 8 +- .../payment_adjuster/adjustment_runners.rs | 4 +- .../payment_adjuster/diagnostics.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 28 +- node/src/accountant/payment_adjuster/mod.rs | 644 +++++++++--------- .../accountant/payment_adjuster/verifier.rs | 5 +- .../scanners/payable_scan_setup_msgs.rs | 2 +- node/src/blockchain/blockchain_bridge.rs | 4 +- 8 files changed, 362 insertions(+), 335 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 1d7043035..22ef3b6e1 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1444,7 +1444,7 @@ mod tests { masq_tokens_minor: u32::MAX as u128, }, estimated_gas_limit_per_transaction: 112_000, - desired_transaction_fee_price_major: 123, + agreed_transaction_fee_per_computed_unit_major: 123, }, )), response_skeleton_opt: Some(ResponseSkeleton { @@ -1537,7 +1537,7 @@ mod tests { masq_tokens_minor: 150_000_000_000, }, estimated_gas_limit_per_transaction: 110_000, - desired_transaction_fee_price_major: 0, + agreed_transaction_fee_per_computed_unit_major: 0, }, )), response_skeleton_opt: Some(response_skeleton), @@ -1611,7 +1611,7 @@ mod tests { masq_tokens_minor: 150_000_000_000, }, estimated_gas_limit_per_transaction: 55_000, - desired_transaction_fee_price_major: 60, + agreed_transaction_fee_per_computed_unit_major: 60, }, )), response_skeleton_opt: Some(response_skeleton), @@ -1725,7 +1725,7 @@ mod tests { masq_tokens_minor: 150_000_000_000, }, estimated_gas_limit_per_transaction: 55_000, - desired_transaction_fee_price_major: 60, + agreed_transaction_fee_per_computed_unit_major: 60, }, )), response_skeleton_opt: None, diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 2c71ed5d4..ef0a62c5f 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -152,7 +152,7 @@ mod tests { transaction_fee_minor: 0, masq_tokens_minor: cw_balance, }, - desired_transaction_fee_price_major: 30, + agreed_transaction_fee_per_computed_unit_major: 30, estimated_gas_limit_per_transaction: 100, }; let adjustment = Adjustment::MasqToken; @@ -252,7 +252,7 @@ mod tests { transaction_fee_minor: 0, masq_tokens_minor: cw_balance, }, - desired_transaction_fee_price_major: 30, + agreed_transaction_fee_per_computed_unit_major: 30, estimated_gas_limit_per_transaction: 100, }; let wallet_1 = make_wallet("abc"); diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 4ea846b0f..2f6d14138 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 9be9ab895..2513a9bab 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -13,8 +13,11 @@ use std::ops::Not; use thousands::Separable; const REFILL_RECOMMENDATION: &str = "\ -In order to continue using services of other Nodes and avoid delinquency \ -bans you will need to put more funds into your consuming wallet."; +In order to continue consuming services from other Nodes and avoid delinquency bans it is necessary \ +to allocate more funds in your consuming wallet."; +const LATER_DETECTED_MASQ_SCARCITY: &str = "\ +Passing successful payment adjustment by the transaction fee, but facing critical scarcity of MASQ \ +balance. Operation will abort."; const BLANK_SPACE: &str = ""; @@ -48,7 +51,7 @@ pub fn format_brief_adjustment_summary( fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { let title = once(format!( "\n{: details, }; - let transaction_fee_adjustment_opt = - Self::determine_transaction_count_limit_by_transaction_fee( - &this_stage_data, - required_tx_count, - &self.logger, - )?; + match Self::determine_transaction_count_limit_by_transaction_fee( + &this_stage_data, + required_tx_count, + &self.logger, + ) { + Ok(None) => (), + Ok(Some(affordable_transaction_count)) => { + return Ok(Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count, + })) + } + Err(e) => return Err(e), + }; - let is_masq_adjustment_needed = Self::check_need_of_masq_adjustment( + match Self::check_need_of_masq_adjustment( &self.logger, Either::Left(qualified_payables), this_stage_data.consuming_wallet_balances.masq_tokens_minor, - )?; - - let adjustment_opt = match (transaction_fee_adjustment_opt, is_masq_adjustment_needed) { - (Some(affordable_transaction_count), _) => Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count, - }), - (None, true) => Some(Adjustment::MasqToken), - _ => None, - }; - - Ok(adjustment_opt) + ) { + Ok(false) => Ok(None), + Ok(true) => Ok(Some(Adjustment::MasqToken)), + Err(e) => Err(e), + } } fn adjust_payments( @@ -176,7 +173,7 @@ impl PaymentAdjusterReal { ) -> Result, PaymentAdjusterError> { let transaction_fee_required_per_transaction_major = tech_info.estimated_gas_limit_per_transaction as u128 - * tech_info.desired_transaction_fee_price_major as u128; + * tech_info.agreed_transaction_fee_per_computed_unit_major as u128; let per_transaction_requirement_minor: u128 = gwei_to_wei(transaction_fee_required_per_transaction_major); @@ -188,7 +185,7 @@ impl PaymentAdjusterReal { cw_transaction_fee_balance_minor / per_transaction_requirement_minor; let (max_doable_tx_count_u16, required_tx_count_u16) = - Self::put_big_unsigned_integers_under_u16_ceiling( + Self::put_bigger_unsigned_integers_under_u16_ceiling( max_doable_tx_count, required_tx_count, ); @@ -214,7 +211,7 @@ impl PaymentAdjusterReal { } } - fn put_big_unsigned_integers_under_u16_ceiling( + fn put_bigger_unsigned_integers_under_u16_ceiling( max_doable_tx_count: u128, required_tx_count: usize, ) -> (u16, u16) { @@ -655,23 +652,39 @@ pub enum AnalysisError { impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PaymentAdjusterError::AnalysisError(analysis_error) => match analysis_error{ - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx {number_of_accounts,per_transaction_requirement_minor, cw_transaction_fee_balance_minor } => - write!(f, "Found less transaction fee balance than required by one payment. \ + PaymentAdjusterError::AnalysisError(analysis_error) => match analysis_error { + AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts, + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, + } => write!( + f, + "Found smaller transaction fee balance than does for a single payment. \ Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ - Current consuming wallet balance: {} wei", number_of_accounts, - per_transaction_requirement_minor.separate_with_commas(), - cw_transaction_fee_balance_minor.separate_with_commas()), - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated {number_of_accounts, cw_masq_balance_minor } => - write!(f,"Analysis has projected likely unacceptable adjustment leaving all \ - accounts with too low resulted balances. Please, provide more funds instead. \ - Number of canceled payments: {}. Current consuming wallet balance: {} wei of MASQ", - number_of_accounts.separate_with_commas(), - cw_masq_balance_minor.separate_with_commas()) + Current consuming wallet balance: {} wei", + number_of_accounts, + per_transaction_requirement_minor.separate_with_commas(), + cw_transaction_fee_balance_minor.separate_with_commas() + ), + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts, + cw_masq_balance_minor, + } => write!( + f, + "Analysis has projected a likely unacceptable adjustment leaving each \ + of the payable accounts with too a low adjusted amount to pay. Please, proceed by \ + sending funds to your wallet. Number of canceled payments: {}. Current consuming \ + wallet balance: {} wei of MASQ", + number_of_accounts.separate_with_commas(), + cw_masq_balance_minor.separate_with_commas() + ), }, - PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!(f, "Despite \ + PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( + f, + "Despite \ the preliminary analysis had expected a possibility to compute some executable \ - adjusted payments, the algorithm eventually rejected them all") + adjusted payments, the algorithm eventually rejected them all" + ), } } } @@ -717,8 +730,8 @@ mod tests { let _ = result.inner.unallocated_cw_masq_balance_minor(); } - struct PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: Vec, + struct PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either, Vec>, cw_balance_major: u64, } @@ -729,37 +742,40 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); subject.logger = logger; - //masq balance > payments - let msg_1 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![85, 14], + // MASQ balance > payments + let msg_1 = make_payable_setup_msg_sent_from_blockchain_bridge( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Right(vec![ + gwei_to_wei::(85), + gwei_to_wei::(15) - 1, + ]), cw_balance_major: 100, }), None, ); - //masq balance = payments - let msg_2 = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![85, 15], + // MASQ balance == payments + let msg_2 = make_payable_setup_msg_sent_from_blockchain_bridge( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Left(vec![85, 15]), cw_balance_major: 100, }), None, ); - //transaction_fee balance > payments - let msg_3 = make_payable_setup_msg_coming_from_blockchain_bridge( + // transaction fee balance > payments + let msg_3 = make_payable_setup_msg_sent_from_blockchain_bridge( None, - Some(TransactionFeeConditionsInTest { - desired_transaction_fee_price_per_major: 111, - number_of_accounts: 5, + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts: 6, estimated_transaction_fee_units_limit_per_transaction: 53_000, - cw_transaction_fee_balance_major: (111 * 5 * 53_000) + 1, + cw_transaction_fee_balance_major: (100 * 6 * 53_000) + 1, }), ); - //transaction_fee balance = payments - let msg_4 = make_payable_setup_msg_coming_from_blockchain_bridge( + // transaction fee balance == payments + let msg_4 = make_payable_setup_msg_sent_from_blockchain_bridge( None, - Some(TransactionFeeConditionsInTest { - desired_transaction_fee_price_per_major: 100, + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_limit_per_transaction: 53_000, cw_transaction_fee_balance_major: 100 * 6 * 53_000, @@ -779,116 +795,88 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_positive_for_masq_token() { - init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_masq_token"; - let logger = Logger::new(test_name); - let mut subject = PaymentAdjusterReal::new(); - subject.logger = logger; - let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![85, 16], - cw_balance_major: 100, - }), - None, - ); - - let result = subject.search_for_indispensable_adjustment(&msg); - - assert_eq!(result, Ok(Some(Adjustment::MasqToken))); - let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 101,000,000,000 \ - wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ - Adjustment in their count or the amounts is required.")); - log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue using services \ - of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); - } - - fn test_positive_transaction_fee_check_vs_masq_check( - test_name: &str, - masq_balances_setup_opt: Option, - ) -> Result, PaymentAdjusterError> { + fn search_for_indispensable_adjustment_positive_for_transaction_fee() { init_test_logging(); + let test_name = "search_for_indispensable_adjustment_positive_for_transaction_fee"; + // means a confidently big balance is picked in the behind + let masq_balances_setup_opt = None; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let number_of_accounts = 3; - let msg = make_payable_setup_msg_coming_from_blockchain_bridge( + let msg = make_payable_setup_msg_sent_from_blockchain_bridge( masq_balances_setup_opt, - Some(TransactionFeeConditionsInTest { - desired_transaction_fee_price_per_major: 100, + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, estimated_transaction_fee_units_limit_per_transaction: 55_000, cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, }), ); - subject.search_for_indispensable_adjustment(&msg) - } + let result = subject.search_for_indispensable_adjustment(&msg); - fn assert_logs(test_name: &str) { + assert_eq!( + result, + Ok(Some(Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2 + })) + ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( "WARN: {test_name}: Gas amount 16,499,999,000,000,000 wei \ cannot cover anticipated fees from sending 3 transactions. Maximum is 2. \ The payments need to be adjusted in their count." )); - log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue using services \ - of other Nodes and avoid delinquency bans you will need to put more funds into your consuming wallet.")); + log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue consuming \ + services from other Nodes and avoid delinquency bans it is necessary to allocate more funds in \ + your consuming wallet.")); } #[test] - fn search_on_three_accounts_positive_for_transaction_fee_when_masq_balance_is_enough() { - let test_name = - "search_on_three_accounts_positive_for_transaction_fee_when_masq_balance_is_enough"; - // means there is plenty of masq - let masq_balances_setup_opt = None; - - let result = - test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); - - assert_eq!( - result, - Ok(Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2 - })) + fn search_for_indispensable_adjustment_positive_for_masq_token() { + init_test_logging(); + let test_name = "search_for_indispensable_adjustment_positive_for_masq_token"; + let logger = Logger::new(test_name); + let mut subject = PaymentAdjusterReal::new(); + subject.logger = logger; + let msg = make_payable_setup_msg_sent_from_blockchain_bridge( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Right(vec![ + gwei_to_wei::(85), + gwei_to_wei::(15) + 1, + ]), + cw_balance_major: 100, + }), + None, ); - assert_logs(test_name) - } - #[test] - fn search_on_three_accounts_positive_for_transaction_fee_but_masq_balance_is_feasibly_adjustable( - ) { - let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_masq_balance_is_feasibly_adjustable"; - let masq_low_but_ok_major = 300 + 500; - let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![120, 300, 500], - cw_balance_major: masq_low_but_ok_major, - }); - - let result = - test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); + let result = subject.search_for_indispensable_adjustment(&msg); - assert_eq!( - result, - Ok(Some(Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2 - })) - ); - assert_logs(test_name) + assert_eq!(result, Ok(Some(Adjustment::MasqToken))); + let log_handler = TestLogHandler::new(); + log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,000,001 \ + wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ + Adjustment in their count or the amounts is required.")); + log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue consuming services \ + from other Nodes and avoid delinquency bans it is necessary to allocate more funds in your consuming \ + wallet.")); } #[test] - fn search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low() { - let test_name = "search_on_three_accounts_positive_for_transaction_fee_but_mask_balance_is_desperately_low"; - let masq_too_low_major = 120 / 2 - 1; // this will kick the limit - let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![120, 300, 500], + fn checking_three_accounts_positive_for_transaction_fee_but_mask_balance_is_unbearably_low() { + let test_name = "checking_three_accounts_positive_for_transaction_fee_but_mask_balance_is_unbearably_low"; + let masq_too_low_major = 120 / 2 - 1; // this would normally kick a serious error + let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Left(vec![120, 300, 500]), cw_balance_major: masq_too_low_major, }); + let msg = make_payable_setup_msg_sent_from_blockchain_bridge(masq_balances_setup_opt, None); + let mut subject = PaymentAdjusterReal::new(); + let logger = Logger::new(test_name); + subject.logger = logger; - let result = - test_positive_transaction_fee_check_vs_masq_check(test_name, masq_balances_setup_opt); + let result = subject.search_for_indispensable_adjustment(&msg); assert_eq!( result, @@ -899,21 +887,19 @@ mod tests { } )) ); - assert_logs(test_name) } #[test] - fn search_for_indispensable_adjustment_unable_to_pay_even_for_a_single_transaction_because_of_transaction_fee( - ) { + fn not_enough_transaction_fee_balance_for_even_a_single_transaction() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; - let msg = make_payable_setup_msg_coming_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![123], + let msg = make_payable_setup_msg_sent_from_blockchain_bridge( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Left(vec![123]), cw_balance_major: 444, }), - Some(TransactionFeeConditionsInTest { - desired_transaction_fee_price_per_major: 100, + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, estimated_transaction_fee_units_limit_per_transaction: 55_000, cw_transaction_fee_balance_major: 54_000 * 100, @@ -927,8 +913,8 @@ mod tests { Err(PaymentAdjusterError::AnalysisError( AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts, - per_transaction_requirement_minor: 55_000 * 100 * 1_000_000_000, - cw_transaction_fee_balance_minor: 54_000 * 100 * 1_000_000_000 + per_transaction_requirement_minor: 55_000 * gwei_to_wei::(100), + cw_transaction_fee_balance_minor: 54_000 * gwei_to_wei::(100) } )) ); @@ -944,10 +930,10 @@ mod tests { cw_masq_balance_minor: 333_000_000, }, ), - "Analysis has projected likely unacceptable adjustment leaving all accounts \ - with too low resulted balances. Please, provide more funds instead. Number \ - of canceled payments: 5. Current consuming wallet balance: 333,000,000 wei \ - of MASQ", + "Analysis has projected a likely unacceptable adjustment leaving each of the payable \ + accounts with too a low adjusted amount to pay. Please, proceed by sending funds to \ + your wallet. Number of canceled payments: 5. Current consuming wallet balance: \ + 333,000,000 wei of MASQ", ), ( PaymentAdjusterError::AnalysisError( @@ -957,7 +943,7 @@ mod tests { cw_transaction_fee_balance_minor: 90_000, }, ), - "Found less transaction fee balance than required by one payment. \ + "Found smaller transaction fee balance than does for a single payment. \ Number of canceled payments: 4. Transaction fee for a single account: \ 70,000,000,000,000 wei. Current consuming wallet balance: 90,000 wei", ), @@ -971,20 +957,22 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } + fn u16_max_plus_minus_correction(correction: i8) -> usize { + if correction < 0 { + (u16::MAX - correction.abs() as u16) as usize + } else { + u16::MAX as usize + correction as usize + } + } + #[test] - fn there_is_u16_max_ceiling_for_doable_txs_count() { + fn there_is_u16_ceiling_for_doable_txs_count() { let result = [-3_i8, -1, 0, 1, 10] .into_iter() - .map(|correction| { - if correction < 0 { - (u16::MAX - correction.abs() as u16) as u128 - } else { - u16::MAX as u128 + correction as u128 - } - }) + .map(|correction| u16_max_plus_minus_correction(correction) as u128) .map(|max_doable_txs_count_u256| { let (doable_txs_count, _) = - PaymentAdjusterReal::put_big_unsigned_integers_under_u16_ceiling( + PaymentAdjusterReal::put_bigger_unsigned_integers_under_u16_ceiling( max_doable_txs_count_u256, 123, ); @@ -999,19 +987,13 @@ mod tests { } #[test] - fn there_is_u16_max_ceiling_for_required_txs_count() { + fn there_is_u16_ceiling_for_required_txs_count() { let result = [-9_i8, -1, 0, 1, 5] .into_iter() - .map(|correction| { - if correction < 0 { - (u16::MAX - (correction.abs() as u16)) as usize - } else { - u16::MAX as usize + correction as usize - } - }) + .map(|correction| u16_max_plus_minus_correction(correction)) .map(|required_tx_count_usize| { let (_, required_tx_count) = - PaymentAdjusterReal::put_big_unsigned_integers_under_u16_ceiling( + PaymentAdjusterReal::put_bigger_unsigned_integers_under_u16_ceiling( 123, required_tx_count_usize, ); @@ -1026,7 +1008,7 @@ mod tests { } #[test] - fn apply_criteria_returns_accounts_sorted_by_final_weights_in_descending_order() { + fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { let now = SystemTime::now(); let subject = make_initialized_subject(now, None, None); let account_1 = PayableAccount { @@ -1048,20 +1030,30 @@ mod tests { pending_payable_opt: None, }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(qualified_payables); + let mut previous_criteria_sum = u128::MAX; let only_accounts = criteria_and_accounts - .iter() - .map(|(_, account)| account) - .collect::>(); - assert_eq!(only_accounts, vec![&account_3, &account_1, &account_2]) + .into_iter() + .map(|(criteria_sum, account)| { + assert!( + previous_criteria_sum > criteria_sum, + "Previous criteria {} wasn't larger than {} but should've been", + previous_criteria_sum, + criteria_sum + ); + previous_criteria_sum = criteria_sum; + account + }) + .collect::>(); + assert_eq!(only_accounts, vec![account_3, account_1, account_2]) } #[test] - fn smaller_debt_with_extreme_age_is_picked_prioritized_as_outweighed_but_not_with_more_money_than_required( + fn minor_but_highly_aged_debt_takes_priority_as_called_outweighed_and_stays_at_its_original_balance( ) { - const SAFETY_MULTIPLIER: u128 = 1_000_000_000_000_000; let now = SystemTime::now(); let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); @@ -1092,19 +1084,17 @@ mod tests { .left() .unwrap(); - //first a presentation of why this test is important + // First, a presentation of why this test is important let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(qualified_payables); let criteria_total = criteria_total(&criteria_and_accounts); - let account_2_criterion = criteria_and_accounts[1].0; - let cw_balance_fractional_safe = cw_masq_balance * SAFETY_MULTIPLIER; - let proportional_fragment_of_cw_balance = cw_balance_fractional_safe / criteria_total; - let proposed_adjusted_balance_2 = - (account_2_criterion * proportional_fragment_of_cw_balance) / SAFETY_MULTIPLIER; - //the weight of the second account grew very progressively due to the effect of the long age; - //consequences are that redistributing the new balances according to the computed weights would've attributed - //the second account with more tokens to pay than it'd had before the test started; - //to prevent it, we've got a rule that no account can ever demand more than its 100% + let proposed_adjustments = subject + .compute_adjusted_but_non_finalized_accounts(criteria_and_accounts, criteria_total); + let proposed_adjusted_balance_2 = proposed_adjustments[1].proposed_adjusted_balance; + // The criteria sum of the second account grew very progressively due to the effect of the greater age; + // consequences would've been that redistributing the new balances according to the computed criteria would've + // attributed the second account with more tokens to pay than it would've had before the test started; + // to prevent it, we set a logical rule that no account can ever demand more than the 100% of itself assert!( proposed_adjusted_balance_2 > 10 * balance_2, "we expected the proposed balance \ @@ -1112,48 +1102,43 @@ mod tests { balance_2, proposed_adjusted_balance_2 ); - let first_account = result.remove(0); - //outweighed account takes the first place - assert_eq!(first_account.original_account, account_2); - assert_eq!(first_account.proposed_adjusted_balance, balance_2); - let second_account = result.remove(0); - assert_eq!(second_account.original_account, account_1); + let first_returned_account = result.remove(0); + // Outweighed accounts always take the first places + assert_eq!(first_returned_account.original_account, account_2); + assert_eq!(first_returned_account.proposed_adjusted_balance, balance_2); + let second_returned_account = result.remove(0); + assert_eq!(second_returned_account.original_account, account_1); let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; let lower_limit = (upper_limit * 9) / 10; assert!( - lower_limit <= second_account.proposed_adjusted_balance - && second_account.proposed_adjusted_balance <= upper_limit, + lower_limit <= second_returned_account.proposed_adjusted_balance + && second_returned_account.proposed_adjusted_balance <= upper_limit, "we expected the roughly adjusted account to be between {} and {} but was {}", lower_limit, upper_limit, - second_account.proposed_adjusted_balance + second_returned_account.proposed_adjusted_balance ); assert!(result.is_empty()); } #[test] - fn an_account_never_becomes_outweighed_and_needing_more_than_the_cw_balance_has_because_disqualified_accounts_need_to_be_eliminated_first( + fn account_never_becomes_outweighed_and_stuck_with_balance_higher_than_the_cw_balance_has_because_there_are_accounts_to_disqualify_first( ) { - // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test at all. + // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test either. // This test answers what is happening when the cw MASQ balance cannot cover the outweighed accounts at the first try but if there are outweighed accounts - // some other accounts must be also around of which one would definitely be heading to disqualification. - // With enough money, the other account might not need to meet disqualification which means the concern about an outweighed account that - // could not be paid by its full volume turns out to be groundless. Also meaning, that the algorithm strives not to cause another form of - // insufficient, different from that one we had at the beginning + // some other accounts must be also around of which some are under the disqualification limit and one of these would definitely be heading to disqualification. + // With enough money, the other account might not need to meet disqualification which means the initial concern is still groundless: there must be enough money + // to cover the outweighed account if there is other one which is qualified neither as outweighed or disqualified. const SECONDS_IN_3_DAYS: u64 = 259_200; let test_name = - "an_account_never_becomes_outweighed_and_balance_full_while_cw_balance_smaller_than_that_because_disqualified_accounts_will_be_eliminated_first"; + "account_never_becomes_outweighed_and_stuck_with_balance_higher_than_the_cw_balance_has_because_there_are_accounts_to_disqualify_first"; let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; - let subject = make_initialized_subject( - now, - Some(consuming_wallet_balance), - Some(Logger::new(test_name)), - ); let account_1 = PayableAccount { wallet: make_wallet("blah"), balance_wei: 1_000_000_000_000, last_paid_timestamp: now + // Greater age like this together with smaller balance usually causes the account to be qualified as outweighed .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) .unwrap(), pending_payable_opt: None, @@ -1167,6 +1152,11 @@ mod tests { pending_payable_opt: None, }; let accounts = vec![account_1.clone(), account_2.clone()]; + let subject = make_initialized_subject( + now, + Some(consuming_wallet_balance), + Some(Logger::new(test_name)), + ); let accounts_with_individual_criteria = subject.calculate_criteria_sums_for_accounts(accounts); @@ -1183,10 +1173,10 @@ mod tests { } #[test] - fn there_are_safe_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts( + fn there_are_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts( ) { init_test_logging(); - let test_name = "there_are_safe_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts"; + let test_name = "there_are_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts"; let mut subject = make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { @@ -1199,19 +1189,19 @@ mod tests { assert!(here_decided_accounts.is_empty()); assert!(downstream_decided_accounts.is_empty()); - //Even though we normally don't assert on DEBUG logs, this one hardens the test + // Even though we normally don't assert on DEBUG logs, this one hardens the test TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: Last remaining account ended up disqualified" )); } #[test] - fn loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations( + fn loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers( ) { init_test_logging(); - let test_name = "loading_the_complete_process_with_exaggerated_debt_conditions_without_blowing_up_on_math_operations"; + let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; let now = SystemTime::now(); - //each of 3 accounts contains the full token supply and a 10-years-old debt which generates extremely big numbers in the criteria + // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria let qualified_payables = { let debt_age_in_months = vec![120, 120, 120]; make_extreme_accounts( @@ -1221,7 +1211,7 @@ mod tests { }; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - //for change extremely small cw balance + // for change extremely small cw balance let cw_masq_balance = 1_000; let setup_msg = PayablePaymentSetup { qualified_payables, @@ -1232,7 +1222,7 @@ mod tests { masq_tokens_minor: cw_masq_balance, }, estimated_gas_limit_per_transaction: 70_000, - desired_transaction_fee_price_major: 120, + agreed_transaction_fee_per_computed_unit_major: 120, }, )), response_skeleton_opt: None, @@ -1244,7 +1234,8 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - //because the proposed final balances all all way lower than (at least) the half of the original balances + // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; + // normally, the initial feasibility check wouldn't allow this assert_eq!(result.accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( @@ -1257,7 +1248,7 @@ mod tests { ) }; let log_handler = TestLogHandler::new(); - // notice that the proposals grow by dropping one disqualified account in each iteration + // Notice that the proposals grow as one disqualified account drops in each iteration log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616830", 333, @@ -1310,7 +1301,7 @@ mod tests { masq_tokens_minor: consuming_wallet_masq_balance_minor, }, estimated_gas_limit_per_transaction: 70_000, - desired_transaction_fee_price_major: 120, + agreed_transaction_fee_per_computed_unit_major: 120, }, )), response_skeleton_opt: None, @@ -1362,10 +1353,10 @@ mod tests { } #[test] - fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_masq_will_do_after_the_transaction_fee_cut( + fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large( ) { init_test_logging(); - let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_masq_will_do_after_the_transaction_fee_cut"; + let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1398,7 +1389,7 @@ mod tests { masq_tokens_minor: 10_u128.pow(22), }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt: None, @@ -1431,7 +1422,7 @@ mod tests { |0x0000000000000000000000000000000000676869 222000000000000 | 222000000000000 | -|Ruled Out in Favor of the Others Original +|Ruled Out Original | |0x0000000000000000000000000000000000616263 111000000000000" ); @@ -1439,8 +1430,7 @@ mod tests { } #[test] - fn both_balances_are_insufficient_but_adjustment_by_masq_cuts_down_no_accounts_it_just_adjusts_their_balances( - ) { + fn both_balances_are_insufficient_but_adjustment_by_masq_will_not_affect_the_accounts_count() { init_test_logging(); let now = SystemTime::now(); let account_1 = PayableAccount { @@ -1478,7 +1468,7 @@ mod tests { masq_tokens_minor: consuming_wallet_masq_balance, }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt, @@ -1511,9 +1501,9 @@ mod tests { } #[test] - fn adjust_payments_when_only_masq_token_limits_the_final_transaction_count() { + fn adjust_payments_when_only_masq_balance_limits_the_final_transaction_count() { init_test_logging(); - let test_name = "adjust_payments_when_only_masq_token_limits_the_final_transaction_count"; + let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; let now = SystemTime::now(); let wallet_1 = make_wallet("def"); // account to be adjusted up to maximum @@ -1540,7 +1530,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let consuming_wallet_masq_balance_minor = 333_000_000_000 + 50_000_000_000; @@ -1554,7 +1544,7 @@ mod tests { masq_tokens_minor: consuming_wallet_masq_balance_minor, }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt: Some(ResponseSkeleton { @@ -1590,10 +1580,10 @@ mod tests { would not be at least half of the original debt 600,000,000,000")); } - struct CompetitiveAccountsTestInput<'a> { + struct CompetitiveAccountsTestInputs<'a> { common: WalletsSetup<'a>, - balance_correction_account_1: u128, - balance_correction_account_2: u128, + balance_correction_minor_account_1: u128, + balance_correction_minor_account_2: u128, age_correction_secs_account_1: u64, age_correction_secs_account_2: u64, } @@ -1604,48 +1594,47 @@ mod tests { wallet_2: &'a Wallet, } - fn test_competitive_accounts<'a>( + fn test_two_competitive_accounts_with_one_disqualified<'a>( test_scenario_name: &str, - inputs: CompetitiveAccountsTestInput, - expected_winning_account: &'a Wallet, + inputs: CompetitiveAccountsTestInputs, + expected_winning_account_wallet: &'a Wallet, ) { - let consuming_wallet_balance_minor = 100_000_000_000_000_u128 - 1; - let common_balance_account = 100_000_000_000_000; - let common_age_secs_account = 12000; let now = SystemTime::now(); + let consuming_wallet_balance_minor = 100_000_000_000_000_u128 - 1; + let standard_balance_per_account = 100_000_000_000_000; + let standard_age_per_account = 12000; let account_1 = PayableAccount { wallet: inputs.common.wallet_1.clone(), - balance_wei: inputs.balance_correction_account_1 + common_balance_account, + balance_wei: standard_balance_per_account + inputs.balance_correction_minor_account_1, last_paid_timestamp: now .checked_sub(Duration::from_secs( - inputs.age_correction_secs_account_1 + common_age_secs_account, + standard_age_per_account + inputs.age_correction_secs_account_1, )) .unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: inputs.common.wallet_2.clone(), - balance_wei: inputs.balance_correction_account_2 + common_balance_account, + balance_wei: standard_balance_per_account + inputs.balance_correction_minor_account_2, last_paid_timestamp: now .checked_sub(Duration::from_secs( - inputs.age_correction_secs_account_2 + common_age_secs_account, + standard_age_per_account + inputs.age_correction_secs_account_2, )) .unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1.clone(), account_2.clone()]; + let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance_wei = consuming_wallet_balance_minor; let setup_msg = PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { transaction_fee_minor: u128::MAX, - masq_tokens_minor: consuming_wallet_masq_balance_wei, + masq_tokens_minor: consuming_wallet_balance_minor, }, estimated_gas_limit_per_transaction: 55_000, - desired_transaction_fee_price_major: 150, + agreed_transaction_fee_per_computed_unit_major: 150, }, )), response_skeleton_opt: None, @@ -1662,9 +1651,9 @@ mod tests { let winning_account = result.remove(0); assert_eq!( - &winning_account.wallet, expected_winning_account, - "{}: expected {} but got {}", - test_scenario_name, winning_account.wallet, expected_winning_account + &winning_account.wallet, expected_winning_account_wallet, + "{}: expected wallet {} but got {}", + test_scenario_name, winning_account.wallet, expected_winning_account_wallet ); assert_eq!( winning_account.balance_wei, consuming_wallet_balance_minor, @@ -1680,10 +1669,10 @@ mod tests { } #[test] - fn adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account() { - fn merge_main_test_name_with_test_case(description: &str) -> String { + fn adjust_payments_when_not_enough_masq_to_pay_both_at_least_by_their_half() { + fn merge_test_name_with_test_case(description: &str) -> String { format!( - "adjust_payments_when_not_enough_masq_to_pay_at_least_half_of_each_account_{}", + "adjust_payments_when_not_enough_masq_to_pay_both_at_least_by_their_half{}", description ) } @@ -1695,15 +1684,15 @@ mod tests { wallet_2: &wallet_2, }; // scenario A - let first_scenario_name = merge_main_test_name_with_test_case("when equal"); + let first_scenario_name = merge_test_name_with_test_case("when equal"); let expected_winning_account = &wallet_2; - test_competitive_accounts( + test_two_competitive_accounts_with_one_disqualified( &first_scenario_name, - CompetitiveAccountsTestInput { + CompetitiveAccountsTestInputs { common: common_input, - balance_correction_account_1: 0, - balance_correction_account_2: 0, + balance_correction_minor_account_1: 0, + balance_correction_minor_account_2: 0, age_correction_secs_account_1: 0, age_correction_secs_account_2: 0, }, @@ -1711,15 +1700,15 @@ mod tests { ); // scenario B - let second_scenario_name = merge_main_test_name_with_test_case("first heavier by balance"); + let second_scenario_name = merge_test_name_with_test_case("first heavier by balance"); let expected_winning_account = &wallet_2; - test_competitive_accounts( + test_two_competitive_accounts_with_one_disqualified( &second_scenario_name, - CompetitiveAccountsTestInput { + CompetitiveAccountsTestInputs { common: common_input, - balance_correction_account_1: 1, - balance_correction_account_2: 0, + balance_correction_minor_account_1: 1, + balance_correction_minor_account_2: 0, age_correction_secs_account_1: 0, age_correction_secs_account_2: 0, }, @@ -1727,15 +1716,15 @@ mod tests { ); // scenario C - let third_scenario_name = merge_main_test_name_with_test_case("second heavier by age"); + let third_scenario_name = merge_test_name_with_test_case("second heavier by age"); let expected_winning_account = &wallet_1; - test_competitive_accounts( + test_two_competitive_accounts_with_one_disqualified( &third_scenario_name, - CompetitiveAccountsTestInput { + CompetitiveAccountsTestInputs { common: common_input, - balance_correction_account_1: 0, - balance_correction_account_2: 0, + balance_correction_minor_account_1: 0, + balance_correction_minor_account_2: 0, age_correction_secs_account_1: 1, age_correction_secs_account_2: 0, }, @@ -1744,18 +1733,18 @@ mod tests { } #[test] - fn adjust_payments_when_masq_as_well_as_transaction_fee_will_limit_the_count() { + fn adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count() { init_test_logging(); - let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_will_limit_the_count"; + let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; let now = SystemTime::now(); - //thrown away as the second one because of its insignificance (proposed adjusted balance is smaller than half the original) + // Thrown away as the second for the proposed balance insignificance let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 10_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - //thrown away as the first one for insufficient transaction_fee + // Thrown away as the first one for the proposed balance insignificance let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 55_000_000_000, @@ -1770,7 +1759,7 @@ mod tests { last_paid_timestamp: last_paid_timestamp_3, pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let qualified_payables = vec![account_1, account_2, account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let consuming_wallet_masq_balance = 300_000_000_000_000_u128; @@ -1784,7 +1773,7 @@ mod tests { masq_tokens_minor: consuming_wallet_masq_balance, }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt: None, @@ -1818,7 +1807,7 @@ mod tests { |0x0000000000000000000000000000000000676869 333000000000000 | 300000000000000 | -|Ruled Out in Favor of the Others Original +|Ruled Out Original | |0x0000000000000000000000000000000000616263 10000000000000 |0x0000000000000000000000000000000000646566 55000000000" @@ -1827,10 +1816,10 @@ mod tests { } #[test] - fn error_returns_after_transaction_fee_adjustment_done_but_mask_balance_is_found_completely_insufficient( + fn error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient( ) { init_test_logging(); - let test_name = "error_returns_after_transaction_fee_adjustment_done_but_mask_balance_is_found_completely_insufficient"; + let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; let now = SystemTime::now(); //this account gets eliminated in the transaction-fee cut let account_1 = PayableAccount { @@ -1851,9 +1840,10 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let qualified_payables = vec![account_1, account_2, account_3]; let mut subject = PaymentAdjusterReal::new(); - let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; //this is exactly the amount which will provoke an error + // This is exactly the amount which will provoke an error + let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; subject.logger = Logger::new(test_name); let setup_msg = PayablePaymentSetup { qualified_payables, @@ -1861,11 +1851,11 @@ mod tests { FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // Gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei masq_tokens_minor: cw_masq_balance_minor, }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt: None, @@ -1889,29 +1879,31 @@ mod tests { )) ); TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Passing successful \ - payment adjustment by the transaction fee, but facing critical scarcity of MASQ balance. \ - Operation will abort." + "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ + facing critical scarcity of MASQ balance. Operation will abort." )); } #[test] - fn strategies_for_the_initial_check_and_the_actual_adjustment_algorithm_complement_each_other() - { - let test_name = "strategies_for_the_initial_check_and_the_actual_adjustment_algorithm_complement_each_other"; + fn the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies( + ) { + let test_name = "the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies"; let now = SystemTime::now(); + // Disqualified in the first iteration let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 10_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; + // Disqualified in the second iteration let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 550_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), pending_payable_opt: None, }; + // Eventually picked fulfilling the keep condition and returned let wallet_3 = make_wallet("ghi"); let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); let balance_3 = 100_000_000_000; @@ -1925,7 +1917,13 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); subject.logger = logger.clone(); - // this should fulfill the initial check, but after that the win of the third account will be really tight + // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, + // the final winning resolution of the third account will work out because of the only + // additional one. + // The strategies advance reversed. The initial check seeks the smallest account, + // the disqualification strategy always takes from the largest accounts first. + // As a result, we can forecast the chances if the adjustment would succeed, not having to + // move forward beyond the entry check. let consuming_wallet_masq_balance = (balance_3 / 2) + 1; let setup_msg = PayablePaymentSetup { qualified_payables: qualified_payables.clone(), @@ -1936,7 +1934,7 @@ mod tests { masq_tokens_minor: consuming_wallet_masq_balance, }, estimated_gas_limit_per_transaction: 77_000, - desired_transaction_fee_price_major: 24, + agreed_transaction_fee_per_computed_unit_major: 24, }, )), response_skeleton_opt: None, @@ -1966,32 +1964,45 @@ mod tests { assert_eq!(adjustment_result.response_skeleton_opt, None); } - struct TransactionFeeConditionsInTest { - desired_transaction_fee_price_per_major: u64, + struct TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: u64, number_of_accounts: usize, estimated_transaction_fee_units_limit_per_transaction: u64, cw_transaction_fee_balance_major: u64, } - fn make_payable_setup_msg_coming_from_blockchain_bridge( - masq_balances_opt: Option, - transaction_fee_conditions_opt: Option, + fn make_payable_setup_msg_sent_from_blockchain_bridge( + masq_balances_config_opt: Option, + transaction_fee_config_opt: Option, ) -> PayablePaymentSetup { - let masq_balances_setup = masq_balances_opt.unwrap_or(PayableBalancesAndCWBalanceInTest { - balances_of_accounts_major: vec![1, 1], - cw_balance_major: u64::MAX, - }); + let masq_balances_setup = match masq_balances_config_opt { + Some(config) => config, + None => PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Left(vec![1, 1]), + cw_balance_major: u64::MAX, + }, + }; - let accounts_count = masq_balances_setup.balances_of_accounts_major.len(); + let cw_masq_balance_minor: u128 = gwei_to_wei(masq_balances_setup.cw_balance_major); + + let balances_of_accounts_minor = match masq_balances_setup.balances_of_accounts { + Either::Left(in_major) => in_major + .into_iter() + .map(|major| gwei_to_wei(major)) + .collect(), + Either::Right(in_minor) => in_minor, + }; + + let accounts_count = balances_of_accounts_minor.len(); let ( desired_transaction_fee_price, number_of_payments, estimated_transaction_fee_limit_per_tx, cw_balance_transaction_fee_major, - ) = match transaction_fee_conditions_opt { + ) = match transaction_fee_config_opt { Some(conditions) => ( - conditions.desired_transaction_fee_price_per_major, + conditions.agreed_transaction_fee_per_computed_unit_major, conditions.number_of_accounts, conditions.estimated_transaction_fee_units_limit_per_transaction, conditions.cw_transaction_fee_balance_major, @@ -1999,27 +2010,34 @@ mod tests { None => (120, accounts_count, 55_000, u64::MAX), }; - let qualified_payables: Vec<_> = match number_of_payments != accounts_count { - true => (0..number_of_payments) + let qualified_payables: Vec<_> = if number_of_payments != accounts_count { + (0..number_of_payments) .map(|idx| make_payable_account(idx as u64)) - .collect(), - false => masq_balances_setup - .balances_of_accounts_major + .collect() + } else { + balances_of_accounts_minor .into_iter() - .map(|balance| make_payable_account(balance)) - .collect(), + .enumerate() + .map(|(idx, balance)| { + let mut account = make_payable_account(idx as u64); + account.balance_wei = balance; + account + }) + .collect() }; + let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); + PayablePaymentSetup { qualified_payables, this_stage_data_opt: Some(StageData::FinancialAndTechDetails( FinancialAndTechDetails { consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: gwei_to_wei(cw_balance_transaction_fee_major), - masq_tokens_minor: gwei_to_wei(masq_balances_setup.cw_balance_major), + transaction_fee_minor: cw_transaction_fee_minor, + masq_tokens_minor: cw_masq_balance_minor, }, estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, - desired_transaction_fee_price_major: desired_transaction_fee_price, + agreed_transaction_fee_per_computed_unit_major: desired_transaction_fee_price, }, )), response_skeleton_opt: None, diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index f73c91be5..98bb88b72 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -14,7 +14,10 @@ impl MasqAdjustmentPossibilityVerifier { accounts: &[&PayableAccount], cw_masq_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - // the reasoning is that if the cw balance is extremely low compared to the set of accounts, the real adjustment algorithm will proceed by eliminating the biggest account in each iteration + // The reasoning is that the real adjustment algorithm will proceed by eliminating the biggest + // account in each iteration, reaching out the smallest one eventually; if the smallest one + // reduced by the disqualification margin turned out possible to be paid by the available + // balance, we can state the Node is going to perform at least one blockchain transaction let sorted = accounts .iter() diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs index 6ccab5abe..9a0f7a986 100644 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -31,7 +31,7 @@ pub enum StageData { #[derive(Debug, PartialEq, Eq, Clone)] pub struct FinancialAndTechDetails { pub consuming_wallet_balances: ConsumingWalletBalances, - pub desired_transaction_fee_price_major: u64, + pub agreed_transaction_fee_per_computed_unit_major: u64, //rather technical stuff below pub estimated_gas_limit_per_transaction: u64, } diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 61945ecbb..f2f68d622 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -302,7 +302,7 @@ impl BlockchainBridge { let this_stage_data = StageData::FinancialAndTechDetails(FinancialAndTechDetails { consuming_wallet_balances, estimated_gas_limit_per_transaction, - desired_transaction_fee_price_major: desired_gas_price_gwei, + agreed_transaction_fee_per_computed_unit_major: desired_gas_price_gwei, }); let msg = PayablePaymentSetup::from((msg, this_stage_data)); @@ -733,7 +733,7 @@ mod tests { StageData::FinancialAndTechDetails(FinancialAndTechDetails { consuming_wallet_balances: wallet_balances_found, estimated_gas_limit_per_transaction: 51_546, - desired_transaction_fee_price_major: 146, + agreed_transaction_fee_per_computed_unit_major: 146, }), ) .into(); From f94ffe7e785b40be7462d57398f55c60ed87fe8b Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 2 Sep 2023 22:36:26 +0200 Subject: [PATCH 077/250] GH-711: cleaned up all stuff my worn eyes could catch in the auto review --- .../payment_adjuster/adjustment_runners.rs | 82 ++- .../age_criterion_calculator.rs | 41 +- .../balance_criterion_calculator.rs | 39 +- .../criteria_calculators/mod.rs | 12 +- .../payment_adjuster/diagnostics.rs | 54 +- node/src/accountant/payment_adjuster/inner.rs | 59 ++- .../accountant/payment_adjuster/log_fns.rs | 12 +- .../miscellaneous/data_structures.rs | 20 +- .../miscellaneous/helper_functions.rs | 495 +++++++++--------- node/src/accountant/payment_adjuster/mod.rs | 51 +- .../accountant/payment_adjuster/test_utils.rs | 18 +- .../accountant/payment_adjuster/verifier.rs | 53 +- 12 files changed, 479 insertions(+), 457 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index ef0a62c5f..067b78988 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -3,7 +3,7 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::maybe_find_an_account_to_disqualify_in_this_iteration; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; use std::vec; @@ -41,8 +41,10 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - let adjusted_account_vec = adjust_last_one(payment_adjuster, last_account); - Ok(Either::Left(empty_or_single_element(adjusted_account_vec))) + Ok(Either::Left(empty_or_single_element(adjust_last_one( + payment_adjuster, + last_account, + )))) } fn adjust_multiple( @@ -95,12 +97,13 @@ fn adjust_last_one( last_account: PayableAccount, ) -> Option { let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance_minor(); + eprintln!("cw balance: {}", cw_balance); let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { last_account.balance_wei } else { diagnostics!( "LAST REMAINING ACCOUNT", - "Balance adjusted to {} by exhausting the cw balance", + "Balance adjusted to {} by exhausting the cw balance fully", cw_balance ); @@ -113,7 +116,7 @@ fn adjust_last_one( let logger = &payment_adjuster.logger; - match maybe_find_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { + match try_finding_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { Some(_) => None, None => Some(proposed_adjustment_vec.remove(0)), } @@ -136,6 +139,7 @@ mod tests { MasqOnlyRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; use crate::accountant::test_utils::make_payable_account; @@ -203,12 +207,15 @@ mod tests { } #[test] - fn adjust_last_one_for_requested_balance_smaller_than_cw_balance() { + fn adjust_last_one_for_requested_balance_smaller_than_cw_but_not_needed_disqualified() { let now = SystemTime::now(); - let cw_balance = 8_645_123_505; + let account_balance = 4_500_600; + let cw_balance = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) + + 1; let account = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 4_333_222_111, + balance_wei: account_balance, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; @@ -220,22 +227,19 @@ mod tests { result, Some(AdjustedAccountBeforeFinalization { original_account: account, - proposed_adjusted_balance: 4_333_222_111, + proposed_adjusted_balance: account_balance, }) ) } - #[test] - fn adjust_last_one_decides_for_adjusted_account_disqualification() { + fn test_adjust_last_one_when_disqualified(cw_balance: u128, account_balance: u128) { let now = SystemTime::now(); - let account_balance = 4_000_444; let account = PayableAccount { wallet: make_wallet("abc"), balance_wei: account_balance, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; - let cw_balance = 4_000_444 / 2; let payment_adjuster = prepare_payment_adjuster(cw_balance, now); let result = adjust_last_one(&payment_adjuster, account.clone()); @@ -244,17 +248,28 @@ mod tests { } #[test] - fn masq_only_adjust_multiple_is_not_supposed_to_care_about_transaction_fee() { + fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_on_edge() { + let account_balance = 4_000_444; + let cw_balance = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + + test_adjust_last_one_when_disqualified(cw_balance, account_balance) + } + + #[test] + fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() + { + let account_balance = 4_000_444; + let cw_balance = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) + - 1; + + test_adjust_last_one_when_disqualified(cw_balance, account_balance) + } + + #[test] + fn adjust_multiple_for_the_masq_only_runner_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); - let cw_balance = 9_000_000; - let details = FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 0, - masq_tokens_minor: cw_balance, - }, - agreed_transaction_fee_per_computed_unit_major: 30, - estimated_gas_limit_per_transaction: 100, - }; let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { wallet: wallet_1.clone(), @@ -270,19 +285,28 @@ mod tests { affordable_transaction_count: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); + let cw_balance = 9_000_000; + let details = FinancialAndTechDetails { + consuming_wallet_balances: ConsumingWalletBalances { + transaction_fee_minor: 0, + masq_tokens_minor: cw_balance, + }, + agreed_transaction_fee_per_computed_unit_major: 30, + estimated_gas_limit_per_transaction: 100, + }; payment_adjuster.initialize_inner(details, adjustment, now); let subject = MasqOnlyRunner {}; - let seeds = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); + let criteria_and_accounts = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); - let result = subject.adjust_multiple(&mut payment_adjuster, seeds); + let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); - let returned_accounts_accounts = result + let returned_accounts = result .into_iter() .map(|account| account.original_account.wallet) .collect::>(); - assert_eq!(returned_accounts_accounts, vec![wallet_1, wallet_2]) - // if the transaction_fee adjustment had been available to perform, only one account would've been - // returned, therefore test passes + assert_eq!(returned_accounts, vec![wallet_1, wallet_2]) + // If the transaction fee adjustment had been available to perform, only one account would've been + // returned. This test passes } #[test] diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index e7b6f1a1e..d2d63abcc 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -2,7 +2,7 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, NamedCalculator, + CriterionCalculator, CalculatorWithNamedMainParameter, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ DiagnosticsConfig, @@ -111,8 +111,8 @@ impl CriterionCalculator for AgeCriterionCalculator { } } -impl NamedCalculator for AgeCriterionCalculator { - fn parameter_name(&self) -> &'static str { +impl CalculatorWithNamedMainParameter for AgeCriterionCalculator { + fn main_parameter_name(&self) -> &'static str { "AGE" } } @@ -139,7 +139,7 @@ pub mod characteristics_config { lazy_static! { pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let now = SystemTime::now(); - let x_axis_supply = { + let horisontal_axis_data_suply = { [ Left(1), Left(5), @@ -169,14 +169,15 @@ pub mod characteristics_config { .collect() }; Mutex::new(Some(DiagnosticsConfig { - label: "AGE", - x_axis_progressive_supply: x_axis_supply, - x_axis_native_type_formatter: Box::new(move |secs_since_last_paid_payable| { - let native_time = now - .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling"); - AgeInput(native_time) - }), + horizontal_axis_progressive_supply: horisontal_axis_data_suply, + horizontal_axis_native_type_formatter: Box::new( + move |secs_since_last_paid_payable| { + let native_time = now + .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling"); + AgeInput(native_time) + }, + ), })) }; } @@ -191,7 +192,7 @@ mod tests { AGE_MAIN_EXPONENT, AGE_MULTIPLIER, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, NamedCalculator, + CalculatorWithNamedMainParameter, CriterionCalculator, }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; @@ -221,6 +222,7 @@ mod tests { fn nonzero_elapsed_works() { let now = SystemTime::now(); let result: Vec<_> = [ + // The first entry is normally considered 0 s now.checked_sub(Duration::from_nanos(55)).unwrap(), now.checked_sub(Duration::from_secs(1)).unwrap(), now.checked_sub(Duration::from_secs(2)).unwrap(), @@ -264,11 +266,11 @@ mod tests { } #[test] - fn calculator_has_the_right_name() { + fn calculator_returns_the_right_main_param_name() { let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); let subject = AgeCriterionCalculator::new(&payment_adjuster); - let result = subject.parameter_name(); + let result = subject.main_parameter_name(); assert_eq!(result, "AGE") } @@ -278,16 +280,19 @@ mod tests { let now = SystemTime::now(); let payment_adjuster = make_initialized_subject(now, None, None); let subject = AgeCriterionCalculator::new(&payment_adjuster); - let last_paid_timestamp = AgeInput( + let last_paid_timestamp_wrapped = AgeInput( SystemTime::now() .checked_sub(Duration::from_secs(1500)) .unwrap(), ); - let result = subject.formula()(last_paid_timestamp); + let result = subject.formula()(last_paid_timestamp_wrapped); let expected_criterion = { - let elapsed_secs: u64 = now.duration_since(last_paid_timestamp.0).unwrap().as_secs(); + let elapsed_secs: u64 = now + .duration_since(last_paid_timestamp_wrapped.0) + .unwrap() + .as_secs(); let divisor = AgeCriterionCalculator::nonzero_compute_divisor(elapsed_secs); let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 174389aad..cfc957d35 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -2,7 +2,7 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, NamedCalculator, + CriterionCalculator, CalculatorWithNamedMainParameter, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ DiagnosticsConfig, @@ -11,7 +11,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2 use std::sync::Mutex; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; -// this parameter affects the steepness (sensitivity on balance increase) +// This parameter affects the steepness (sensitivity to balance increase) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; pub struct BalanceCriterionCalculator { @@ -20,8 +20,8 @@ pub struct BalanceCriterionCalculator { impl BalanceCriterionCalculator { pub fn new() -> Self { - let formula = Box::new(|wrapped_balance_wei: BalanceInput| { - let balance_minor = wrapped_balance_wei.0; + let formula = Box::new(|wrapped_balance_minor: BalanceInput| { + let balance_minor = wrapped_balance_minor.0; let binary_weight = log_2(Self::calculate_binary_argument(balance_minor)); balance_minor .checked_mul(binary_weight as u128) @@ -57,8 +57,8 @@ impl CriterionCalculator for BalanceCriterionCalculator { } } -impl NamedCalculator for BalanceCriterionCalculator { - fn parameter_name(&self) -> &'static str { +impl CalculatorWithNamedMainParameter for BalanceCriterionCalculator { + fn main_parameter_name(&self) -> &'static str { "BALANCE" } } @@ -81,14 +81,15 @@ pub mod characteristics_config { lazy_static! { pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let x_axis_decimal_exponens = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] + let horisontal_axis_decimal_exponents = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] .into_iter() .map(|exp| 10_u128.pow(exp)) .collect(); Mutex::new(Some(DiagnosticsConfig { - label: "BALANCE", - x_axis_progressive_supply: x_axis_decimal_exponens, - x_axis_native_type_formatter: Box::new(|balance_wei| BalanceInput(balance_wei)), + horizontal_axis_progressive_supply: horisontal_axis_decimal_exponents, + horizontal_axis_native_type_formatter: Box::new(|balance_wei| { + BalanceInput(balance_wei) + }), })) }; } @@ -100,7 +101,7 @@ mod tests { BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, NamedCalculator, + CalculatorWithNamedMainParameter, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; @@ -111,7 +112,7 @@ mod tests { #[test] fn compute_binary_argument_works() { - let inputs = [ + let arg_values = [ 1, BALANCE_LOG_2_ARG_DIVISOR - 1, BALANCE_LOG_2_ARG_DIVISOR, @@ -119,7 +120,7 @@ mod tests { BALANCE_LOG_2_ARG_DIVISOR + 1000, ]; - let result: Vec<_> = inputs + let result: Vec<_> = arg_values .into_iter() .map(|arg| BalanceCriterionCalculator::calculate_binary_argument(arg)) .collect(); @@ -147,10 +148,10 @@ mod tests { } #[test] - fn calculator_has_the_right_name() { + fn calculator_returns_the_right_main_param_name() { let subject = BalanceCriterionCalculator::new(); - let result = subject.parameter_name(); + let result = subject.main_parameter_name(); assert_eq!(result, "BALANCE") } @@ -158,15 +159,15 @@ mod tests { #[test] fn balance_criteria_calculation_works() { let subject = BalanceCriterionCalculator::new(); - let balance_wei = BalanceInput(111_333_555_777); + let balance_wei_wrapped = BalanceInput(111_333_555_777); - let result = subject.formula()(balance_wei); + let result = subject.formula()(balance_wei_wrapped); let expected_result = { let binary_weight = log_2(BalanceCriterionCalculator::calculate_binary_argument( - balance_wei.0, + balance_wei_wrapped.0, )); - balance_wei + balance_wei_wrapped .0 .checked_mul(binary_weight as u128) .expect("mul overflow") diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 7b25e7d63..115f02556 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -13,7 +13,7 @@ use std::fmt::Debug; use std::sync::Mutex; // Caution: always remember to use checked math operations in the formula! -pub trait CriterionCalculator: NamedCalculator { +pub trait CriterionCalculator: CalculatorWithNamedMainParameter { // The additional trait constrain comes from efforts convert write the API more Rust-like. // This implementation has its own pros and cons; the little cons for you are that whenever // you must see the pattern of defining a wrapper for the input of your calculator. Refrain @@ -58,7 +58,11 @@ pub trait CriterionCalculator: NamedCalculator { ::Input: Debug, { if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { - compute_progressive_characteristics(self.diagnostics_config_opt(), self.formula()) + compute_progressive_characteristics( + self.main_parameter_name(), + self.diagnostics_config_opt(), + self.formula(), + ) } } } @@ -101,6 +105,6 @@ impl CriteriaIteratorAdaptor for I { } } -pub trait NamedCalculator { - fn parameter_name(&self) -> &'static str; +pub trait CalculatorWithNamedMainParameter { + fn main_parameter_name(&self) -> &'static str; } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 2f6d14138..4be973351 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -53,7 +53,7 @@ pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { pub mod separately_defined_diagnostic_functions { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::NamedCalculator; + use crate::accountant::payment_adjuster::criteria_calculators::CalculatorWithNamedMainParameter; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::sub_lib::wallet::Wallet; @@ -76,6 +76,20 @@ pub mod separately_defined_diagnostic_functions { ); } + pub fn account_nominated_for_disqualification_diagnostics( + account_info: &AdjustedAccountBeforeFinalization, + proposed_adjusted_balance: u128, + disqualification_edge: u128, + ) { + diagnostics!( + account_info.original_account.wallet, + "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", + "Proposed: {}, disqualification limit: {}", + proposed_adjusted_balance.separate_with_commas(), + disqualification_edge.separate_with_commas() + ); + } + pub fn exhausting_cw_balance_diagnostics( non_finalized_account_info: &AdjustedAccountBeforeFinalization, possible_extra_addition: u128, @@ -113,7 +127,7 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn maybe_find_an_account_to_disqualify_diagnostics( + pub fn try_finding_an_account_to_disqualify_diagnostics( disqualification_suspected_accounts: &[&AdjustedAccountBeforeFinalization], wallet: &Wallet, ) { @@ -125,7 +139,7 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn calculator_local_diagnostics( + pub fn calculator_local_diagnostics( wallet_ref: &Wallet, calculator: &N, criterion: u128, @@ -136,7 +150,7 @@ pub mod separately_defined_diagnostic_functions { wallet_ref, "PARTIAL CRITERION CALCULATED", "{: { - pub label: &'static str, - pub x_axis_progressive_supply: Vec, - pub x_axis_native_type_formatter: Box A + Send>, + pub horizontal_axis_progressive_supply: Vec, + pub horizontal_axis_native_type_formatter: Box A + Send>, } pub fn print_formulas_characteristics_for_diagnostics() { @@ -176,25 +189,30 @@ pub mod formulas_progressive_characteristics { } pub fn compute_progressive_characteristics( + main_param_name: &'static str, config_opt: Option>, formula: &dyn Fn(A) -> u128, ) where A: Debug, { config_opt.map(|config| { - let config_x_axis_type_formatter = config.x_axis_native_type_formatter; - let characteristics = config.x_axis_progressive_supply.into_iter().map(|input| { - let correctly_formatted_input = config_x_axis_type_formatter(input); - format!( - "x: {: SystemTime { - PaymentAdjusterInnerNull::panicking_operation("now()") - } - fn transaction_fee_count_limit_opt(&self) -> Option { - PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") - } - fn original_cw_masq_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance_minor()") - } - fn unallocated_cw_masq_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance_minor()") - } - - fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance_minor()") - } + fn now(&self) -> SystemTime; + fn transaction_fee_count_limit_opt(&self) -> Option; + fn original_cw_masq_balance_minor(&self) -> u128; + fn unallocated_cw_masq_balance_minor(&self) -> u128; + fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128); } pub struct PaymentAdjusterInnerReal { @@ -32,13 +21,13 @@ impl PaymentAdjusterInnerReal { pub fn new( now: SystemTime, transaction_fee_count_limit_opt: Option, - cw_masq_balance: u128, + cw_masq_balance_minor: u128, ) -> Self { Self { now, transaction_fee_count_limit_opt, - original_cw_masq_balance_minor: cw_masq_balance, - unallocated_cw_masq_balance_minor: cw_masq_balance, + original_cw_masq_balance_minor: cw_masq_balance_minor, + unallocated_cw_masq_balance_minor: cw_masq_balance_minor, } } } @@ -60,7 +49,7 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { let updated_thought_cw_balance = self .unallocated_cw_masq_balance_minor .checked_sub(subtrahend) - .expect("subtracting a small enough number"); + .expect("got into negative numbers"); self.unallocated_cw_masq_balance_minor = updated_thought_cw_balance } } @@ -70,13 +59,29 @@ pub struct PaymentAdjusterInnerNull {} impl PaymentAdjusterInnerNull { fn panicking_operation(operation: &str) -> ! { panic!( - "Called the null implementation of the {} method in PaymentAdjusterInner", + "Broken code: Broken code: Called the null implementation of the {} method in PaymentAdjusterInner", operation ) } } -impl PaymentAdjusterInner for PaymentAdjusterInnerNull {} +impl PaymentAdjusterInner for PaymentAdjusterInnerNull { + fn now(&self) -> SystemTime { + PaymentAdjusterInnerNull::panicking_operation("now()") + } + fn transaction_fee_count_limit_opt(&self) -> Option { + PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") + } + fn original_cw_masq_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance_minor()") + } + fn unallocated_cw_masq_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance_minor()") + } + fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance_minor()") + } +} #[cfg(test)] mod tests { @@ -104,7 +109,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the now() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the now() method in PaymentAdjusterInner" )] fn inner_null_calling_now() { let subject = PaymentAdjusterInnerNull {}; @@ -114,7 +119,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" )] fn inner_null_calling_transaction_fee_count_limit_opt() { let subject = PaymentAdjusterInnerNull {}; @@ -124,7 +129,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the original_cw_masq_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the original_cw_masq_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_original_cw_masq_balance_minor() { let subject = PaymentAdjusterInnerNull {}; @@ -134,7 +139,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_unallocated_cw_balance() { let subject = PaymentAdjusterInnerNull {}; @@ -144,7 +149,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the update_unallocated_cw_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the update_unallocated_cw_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_update_unallocated_cw_balance_minor() { let mut subject = PaymentAdjusterInnerNull {}; diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 2513a9bab..32210b2d7 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -15,7 +15,7 @@ use thousands::Separable; const REFILL_RECOMMENDATION: &str = "\ In order to continue consuming services from other Nodes and avoid delinquency bans it is necessary \ to allocate more funds in your consuming wallet."; -const LATER_DETECTED_MASQ_SCARCITY: &str = "\ +const LATER_DETECTED_MASQ_SEVERE_SCARCITY: &str = "\ Passing successful payment adjustment by the transaction fee, but facing critical scarcity of MASQ \ balance. Operation will abort."; @@ -117,7 +117,7 @@ pub fn before_and_after_debug_msg( ) } -pub fn log_info_for_disqualified_account( +pub fn info_log_for_disqualified_account( logger: &Logger, account: &AdjustedAccountBeforeFinalization, ) { @@ -161,14 +161,14 @@ pub fn log_insufficient_transaction_fee_balance( info!(logger, "{}", REFILL_RECOMMENDATION) } -pub fn log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient(logger: &Logger) { - error!(logger, "{}", LATER_DETECTED_MASQ_SCARCITY) +pub fn log_transaction_fee_adjustment_ok_but_masq_balance_undoable(logger: &Logger) { + error!(logger, "{}", LATER_DETECTED_MASQ_SEVERE_SCARCITY) } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::log_fns::{ - LATER_DETECTED_MASQ_SCARCITY, REFILL_RECOMMENDATION, + LATER_DETECTED_MASQ_SEVERE_SCARCITY, REFILL_RECOMMENDATION, }; #[test] @@ -179,7 +179,7 @@ mod tests { is necessary to allocate more funds in your consuming wallet." ); assert_eq!( - LATER_DETECTED_MASQ_SCARCITY, + LATER_DETECTED_MASQ_SEVERE_SCARCITY, "Passing successful payment adjustment by the \ transaction fee, but facing critical scarcity of MASQ balance. Operation will abort." ) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 1cab8e7e1..807656c1e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -6,13 +6,13 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; pub enum AdjustmentIterationResult { AllAccountsProcessedSmoothly(Vec), SpecialTreatmentNeeded { - special_case: SpecialTreatment, + case: AfterAdjustmentSpecialTreatment, remaining: Vec, }, } #[derive(Debug)] -pub enum SpecialTreatment { +pub enum AfterAdjustmentSpecialTreatment { TreatInsignificantAccount, TreatOutweighedAccounts(Vec), } @@ -30,16 +30,6 @@ impl AdjustedAccountBeforeFinalization { proposed_adjusted_balance, } } - - pub fn finalize_collection( - account_infos: Vec, - resolution: ProposedAdjustmentResolution, - ) -> Vec { - account_infos - .into_iter() - .map(|account_info| PayableAccount::from((account_info, resolution))) - .collect() - } } #[derive(Clone, Copy)] @@ -48,11 +38,11 @@ pub enum ProposedAdjustmentResolution { Revert, } -// sets the minimal percentage of the original balance that must be -// proposed after the adjustment or the account will be eliminated for insignificance +// Sets the minimal percentage of the original balance that must be proposed after the adjustment +// or the account will be eliminated for insignificance #[derive(Debug, PartialEq, Eq)] pub struct PercentageAccountInsignificance { - // using integers means we have to represent accurate percentage + // Using integers means we have to represent accurate percentage // as set of two constants pub multiplier: u128, pub divisor: u128, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 85e12951b..eff0c084a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -3,10 +3,11 @@ use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ - exhausting_cw_balance_diagnostics, maybe_find_an_account_to_disqualify_diagnostics, + account_nominated_for_disqualification_diagnostics, exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, possibly_outweighed_accounts_diagnostics, + try_finding_an_account_to_disqualify_diagnostics, }; -use crate::accountant::payment_adjuster::log_fns::log_info_for_disqualified_account; +use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, ProposedAdjustmentResolution, @@ -16,11 +17,10 @@ use itertools::Itertools; use masq_lib::logger::Logger; use std::iter::successors; use std::ops::Not; -use thousands::Separable; const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; -// represents 50% +// Represents 50% pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = PercentageAccountInsignificance { multiplier: 1, @@ -41,31 +41,34 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn reduce_collection_size_by_affordable_transaction_fee( +pub fn keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest( criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, - limit: u16, + affordable_transaction_count: u16, ) -> Vec<(u128, PayableAccount)> { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", - limit, + affordable_transaction_count, criteria_and_accounts_in_descending_order.len() ); criteria_and_accounts_in_descending_order .into_iter() - .take(limit as usize) + .take(affordable_transaction_count as usize) .collect() } -pub fn compute_fraction_preventing_mul_coeff(cw_masq_balance: u128, criteria_sum: u128) -> u128 { - let criteria_sum_digits_count = log_10(criteria_sum); - let cw_balance_digits_count = log_10(cw_masq_balance); +pub fn compute_fractional_numbers_preventing_mul_coefficient( + cw_masq_balance_minor: u128, + account_criteria_sum: u128, +) -> u128 { + let criteria_sum_digits_count = log_10(account_criteria_sum); + let cw_balance_digits_count = log_10(cw_masq_balance_minor); let positive_difference = criteria_sum_digits_count .checked_sub(cw_balance_digits_count) .unwrap_or(0); - let safe_mul_coeff = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; + let safe_mul_coefficient = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; 10_u128 - .checked_pow(safe_mul_coeff as u32) + .checked_pow(safe_mul_coefficient as u32) .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) } @@ -74,28 +77,30 @@ pub fn possibly_outweighed_accounts_fold_guts( Vec, Vec, ), - account_info: AdjustedAccountBeforeFinalization, + current_account_info: AdjustedAccountBeforeFinalization, ) -> ( Vec, Vec, ) { - if account_info.proposed_adjusted_balance > account_info.original_account.balance_wei { - possibly_outweighed_accounts_diagnostics(&account_info); + if current_account_info.proposed_adjusted_balance + > current_account_info.original_account.balance_wei + { + possibly_outweighed_accounts_diagnostics(¤t_account_info); let new_account_info = AdjustedAccountBeforeFinalization { - proposed_adjusted_balance: account_info.original_account.balance_wei, - ..account_info + proposed_adjusted_balance: current_account_info.original_account.balance_wei, + ..current_account_info }; outweighed.push(new_account_info); (outweighed, passing_through) } else { - passing_through.push(account_info); + passing_through.push(current_account_info); (outweighed, passing_through) } } -pub fn find_largest_disqualified_account<'a>( +pub fn find_largest_nominated_account<'a>( accounts: &'a [&'a AdjustedAccountBeforeFinalization], ) -> &'a AdjustedAccountBeforeFinalization { let first_account = &accounts.first().expect("collection was empty"); @@ -107,7 +112,7 @@ pub fn find_largest_disqualified_account<'a>( } else if current.original_account.balance_wei == largest_so_far.original_account.balance_wei { - // bigger value means younger + // Greater value means younger if current.original_account.last_paid_timestamp > largest_so_far.original_account.last_paid_timestamp { @@ -121,44 +126,11 @@ pub fn find_largest_disqualified_account<'a>( }) } -pub fn exhaust_cw_balance_totally( - verified_accounts: Vec, +pub fn exhaust_cw_till_the_last_drop( + approved_accounts: Vec, original_cw_masq_balance_minor: u128, ) -> Vec { - fn fold_guts( - status: ExhaustionStatus, - non_finalized_account_info: AdjustedAccountBeforeFinalization, - ) -> ExhaustionStatus { - if status.remainder != 0 { - let balance_gap = non_finalized_account_info - .original_account - .balance_wei - .checked_sub(non_finalized_account_info.proposed_adjusted_balance) - .unwrap_or_else(|| { - panic!( - "proposed balance should never bigger than the original but proposed: {} \ - and original: {}", - non_finalized_account_info.proposed_adjusted_balance, - non_finalized_account_info.original_account.balance_wei - ) - }); - let possible_extra_addition = if balance_gap < status.remainder { - balance_gap - } else { - status.remainder - }; - - exhausting_cw_balance_diagnostics(&non_finalized_account_info, possible_extra_addition); - - status.update_and_add(non_finalized_account_info, possible_extra_addition) - } else { - not_exhausting_cw_balance_diagnostics(&non_finalized_account_info); - - status.add(non_finalized_account_info) - } - } - - let adjusted_balances_total: u128 = sum_as(&verified_accounts, |account_info| { + let adjusted_balances_total: u128 = sum_as(&approved_accounts, |account_info| { account_info.proposed_adjusted_balance }); @@ -166,13 +138,13 @@ pub fn exhaust_cw_balance_totally( .checked_sub(adjusted_balances_total) .unwrap_or_else(|| { panic!( - "remainder should've been a positive number but was not after {} - {}", + "Remainder should've been a positive number but wasn't after {} - {}", original_cw_masq_balance_minor, adjusted_balances_total ) }); - let init = ExhaustionStatus::new(cw_reminder); - verified_accounts + let init = CwExhaustingStatus::new(cw_reminder); + approved_accounts .into_iter() .sorted_by(|info_a, info_b| { Ord::cmp( @@ -180,30 +152,66 @@ pub fn exhaust_cw_balance_totally( &info_b.proposed_adjusted_balance, ) }) - .fold(init, fold_guts) - .already_finalized_accounts + .fold( + init, + run_cw_exhausting_on_possibly_sub_optimal_account_balances, + ) + .accounts_finalized_so_far .into_iter() .sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)) .collect() } -pub fn maybe_find_an_account_to_disqualify_in_this_iteration( +fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( + status: CwExhaustingStatus, + non_finalized_account: AdjustedAccountBeforeFinalization, +) -> CwExhaustingStatus { + if status.remainder != 0 { + let balance_gap_minor = non_finalized_account + .original_account + .balance_wei + .checked_sub(non_finalized_account.proposed_adjusted_balance) + .unwrap_or_else(|| { + panic!( + "Proposed balance should never be bigger than the original one. Proposed: \ + {}, original: {}", + non_finalized_account.proposed_adjusted_balance, + non_finalized_account.original_account.balance_wei + ) + }); + let possible_extra_addition = if balance_gap_minor < status.remainder { + balance_gap_minor + } else { + status.remainder + }; + + exhausting_cw_balance_diagnostics(&non_finalized_account, possible_extra_addition); + + status.handle_balance_update_and_add(non_finalized_account, possible_extra_addition) + } else { + not_exhausting_cw_balance_diagnostics(&non_finalized_account); + + status.add(non_finalized_account) + } +} + +pub fn try_finding_an_account_to_disqualify_in_this_iteration( non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], logger: &Logger, ) -> Option { let disqualification_suspected_accounts = - list_accounts_under_the_disqualification_limit(non_finalized_adjusted_accounts); + list_accounts_nominated_for_disqualification(non_finalized_adjusted_accounts); disqualification_suspected_accounts .is_empty() .not() .then(|| { - let account_to_disqualify = find_largest_disqualified_account( + let account_to_disqualify = find_largest_nominated_account( &disqualification_suspected_accounts ); let wallet = account_to_disqualify.original_account.wallet.clone(); - maybe_find_an_account_to_disqualify_diagnostics( + try_finding_an_account_to_disqualify_diagnostics( &disqualification_suspected_accounts, &wallet, ); @@ -217,26 +225,36 @@ pub fn maybe_find_an_account_to_disqualify_in_this_iteration( wallet ); - log_info_for_disqualified_account(logger, account_to_disqualify); + info_log_for_disqualified_account(logger, account_to_disqualify); wallet }) } -struct ExhaustionStatus { +pub fn finalize_collection( + non_finalized_accounts: Vec, + resolution: ProposedAdjustmentResolution, +) -> Vec { + non_finalized_accounts + .into_iter() + .map(|account_info| PayableAccount::from((account_info, resolution))) + .collect() +} + +struct CwExhaustingStatus { remainder: u128, - already_finalized_accounts: Vec, + accounts_finalized_so_far: Vec, } -impl ExhaustionStatus { +impl CwExhaustingStatus { fn new(remainder: u128) -> Self { Self { remainder, - already_finalized_accounts: vec![], + accounts_finalized_so_far: vec![], } } - fn update_and_add( + fn handle_balance_update_and_add( mut self, mut non_finalized_account_info: AdjustedAccountBeforeFinalization, possible_extra_addition: u128, @@ -258,44 +276,45 @@ impl ExhaustionStatus { non_finalized_account_info, ProposedAdjustmentResolution::Finalize, )); - self.already_finalized_accounts.push(finalized_account); + self.accounts_finalized_so_far.push(finalized_account); self } } -pub fn sort_in_descendant_order_by_weights( +pub fn sort_in_descendant_order_by_criteria_sums( unsorted: impl Iterator, ) -> Vec<(u128, PayableAccount)> { unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .sorted_by(|(criteria_sum_a, _), (criteria_sum_b, _)| { + Ord::cmp(criteria_sum_b, criteria_sum_a) + }) .collect() } -pub fn rebuild_accounts(criteria_and_accounts: Vec<(u128, PayableAccount)>) -> Vec { +pub fn drop_criteria_sums_and_leave_accounts( + criteria_and_accounts: Vec<(u128, PayableAccount)>, +) -> Vec { criteria_and_accounts .into_iter() .map(|(_, account)| account) .collect() } -pub fn list_accounts_under_the_disqualification_limit( +pub fn list_accounts_nominated_for_disqualification( non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], ) -> Vec<&AdjustedAccountBeforeFinalization> { non_finalized_adjusted_accounts .iter() .flat_map(|account_info| { - let original_balance = account_info.original_account.balance_wei; - let balance_at_the_edge = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier - * original_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + let disqualification_edge = + calculate_disqualification_edge(account_info.original_account.balance_wei); let proposed_adjusted_balance = account_info.proposed_adjusted_balance; - if proposed_adjusted_balance <= balance_at_the_edge { - diagnostics!( - &account_info.original_account.wallet, - "ACCOUNT DISQUALIFIED FOR INSIGNIFICANCE AFTER ADJUSTMENT", - "Proposed: {}, qualification limit: {}", - proposed_adjusted_balance.separate_with_commas(), - balance_at_the_edge.separate_with_commas() + + if proposed_adjusted_balance <= disqualification_edge { + account_nominated_for_disqualification_diagnostics( + account_info, + proposed_adjusted_balance, + disqualification_edge, ); Some(&*account_info) @@ -306,14 +325,19 @@ pub fn list_accounts_under_the_disqualification_limit( .collect() } -// replace with `account_1.balance_wei.checked_ilog10().unwrap() + 1` -// which will be introduced by Rust 1.67.0; this was written with 1.63.0 +pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor +} + +// Replace with std lib method log10() for u128 which will be introduced by +// Rust 1.67.0; this was written using 1.63.0 pub fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } -const fn num_bits() -> usize { - std::mem::size_of::() * 8 +const fn num_bits() -> usize { + std::mem::size_of::() * 8 } pub fn log_2(x: u128) -> u32 { @@ -331,19 +355,16 @@ pub fn x_or_1(x: u128) -> u128 { } } -impl - From<( - AdjustedAccountBeforeFinalization, - ProposedAdjustmentResolution, - )> for PayableAccount -{ +type NonFinalizedAccountAndAdjustmentResolution = ( + AdjustedAccountBeforeFinalization, + ProposedAdjustmentResolution, +); + +impl From for PayableAccount { fn from( - (account_info, resolution): ( - AdjustedAccountBeforeFinalization, - ProposedAdjustmentResolution, - ), + (account_info, proposed_adjustment_resolution): NonFinalizedAccountAndAdjustmentResolution, ) -> Self { - match resolution { + match proposed_adjustment_resolution { ProposedAdjustmentResolution::Finalize => PayableAccount { balance_wei: account_info.proposed_adjusted_balance, ..account_info.original_account @@ -356,12 +377,15 @@ impl #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, + }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, - find_largest_disqualified_account, list_accounts_under_the_disqualification_limit, log_10, - log_2, maybe_find_an_account_to_disqualify_in_this_iteration, - possibly_outweighed_accounts_fold_guts, ExhaustionStatus, + calculate_disqualification_edge, compute_fractional_numbers_preventing_mul_coefficient, + criteria_total, exhaust_cw_till_the_last_drop, find_largest_nominated_account, + list_accounts_nominated_for_disqualification, log_10, log_2, + possibly_outweighed_accounts_fold_guts, + try_finding_an_account_to_disqualify_in_this_iteration, CwExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; @@ -379,8 +403,13 @@ mod tests { fn constants_are_correct() { assert_eq!(MAX_EXPONENT_FOR_10_IN_U128, 38); assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); - assert_eq!(ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier, 1); - assert_eq!(ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor, 2) + assert_eq!( + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + PercentageAccountInsignificance { + multiplier: 1, + divisor: 2 + } + ); } #[test] @@ -401,6 +430,7 @@ mod tests { [ (1, 0), (2, 1), + (3, 1), (4, 2), (8192, 13), (18446744073709551616, 64), @@ -419,9 +449,9 @@ mod tests { } #[test] - fn multiplication_coeff_for_integers_to_be_above_one_instead_of_fractional_numbers() { + fn multiplication_coefficient_can_give_numbers_preventing_fractional_numbers() { let final_criteria_sum = 5_000_000_000_000_u128; - let consuming_wallet_balances = vec![ + let cw_balances = vec![ 222_222_222_222_u128, 100_000, 123_456_789, @@ -430,10 +460,15 @@ mod tests { 1_000_000_000_000_000_000, //1 MASQ ]; - let result = consuming_wallet_balances + let result = cw_balances .clone() .into_iter() - .map(|cw_balance| compute_fraction_preventing_mul_coeff(cw_balance, final_criteria_sum)) + .map(|cw_balance| { + compute_fractional_numbers_preventing_mul_coefficient( + cw_balance, + final_criteria_sum, + ) + }) .collect::>(); assert_eq!( @@ -450,12 +485,11 @@ mod tests { } #[test] - fn multiplication_coeff_showing_extreme_feeding_and_safety_ceiling() { - // the coeff is multiples of 10 we need to multiply the cw balance with - // in order to get at a number bigger than the total criteria sum (and the more extra 10s we add, the more - // accurate numbers we can expect at the results of the entire payment adjuster machinery) + fn multiplication_coefficient_extreme_feeding_and_safety_ceiling() { + // We also add some extra multiples of 10, the more of them the more accurate + // numbers we can expect from the ultimate results of the payment adjuster machinery // - // we cannot say by heart which of the criteria sums of these parameters evaluates to is for sure bigger than another, + // We cannot say by heart which of the criteria sums of these parameters evaluates to is for sure bigger than another, // therefore we could hardly put them into an order let accounts_as_months_and_balances = vec![ (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), @@ -466,38 +500,43 @@ mod tests { (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), ]; - let (different_accounts_with_criteria, initial_accounts_order_from_the_seeds) = - get_extreme_criteria_and_initial_accounts_order(accounts_as_months_and_balances); + let ( + accounts_with_their_criteria_sums, + reserved_initial_accounts_order_according_to_wallets, + ) = get_extreme_criteria_and_initial_accounts_order(accounts_as_months_and_balances); let cw_balance_in_minor = 1; - let results = different_accounts_with_criteria + let results = accounts_with_their_criteria_sums .into_iter() .map(|(criteria_sum, account)| { - // scenario simplification: we asume there is always just one account in a time - let final_criteria_total = criteria_sum; - let resulted_coeff = compute_fraction_preventing_mul_coeff( + // Scenario simplification: we assume there is always just one account to process in a time + let computed_coefficient = compute_fractional_numbers_preventing_mul_coefficient( cw_balance_in_minor, - final_criteria_total, + criteria_sum, ); - (resulted_coeff, account.wallet, criteria_sum) + (computed_coefficient, account.wallet, criteria_sum) }) .collect::>(); - let initial_accounts_order_from_the_seeds_iter = - initial_accounts_order_from_the_seeds.iter().enumerate(); - let coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs = results + let reserved_initial_accounts_order_according_to_wallets_iter = + reserved_initial_accounts_order_according_to_wallets + .iter() + .enumerate(); + let mul_coefficients_and_criteria_sums_matching_the_order_of_the_original_inputs = results .into_iter() - .map(|(coeff, wallet, criteria_sum)| { - let (idx, _) = initial_accounts_order_from_the_seeds_iter - .clone() - .find(|(_, wallet_ordered)| wallet_ordered == &&wallet) - .unwrap(); - (idx, coeff, criteria_sum) - }) - .sorted_by(|(a_idx, _, _), (b_idx, _, _)| Ord::cmp(&b_idx, &a_idx)) - .map(|(_, coeff, criteria_sum)| (coeff, criteria_sum)) + .map( + |(computed_coefficient, account_wallet, account_criteria_sum)| { + let (idx, _) = reserved_initial_accounts_order_according_to_wallets_iter + .clone() + .find(|(_, wallet_ordered)| wallet_ordered == &&account_wallet) + .unwrap(); + (idx, computed_coefficient, account_criteria_sum) + }, + ) + .sorted_by(|(idx_a, _, _), (idx_b, _, _)| Ord::cmp(&idx_b, &idx_a)) + .map(|(_, coefficient, criteria_sum)| (coefficient, criteria_sum)) .collect::>(); - //to enable an easy visual check + // For ease with visual check #[rustfmt::skip] fn expected_result() -> Vec<(u128, u128)> { vec![ @@ -532,46 +571,57 @@ mod tests { ] } assert_eq!( - coeffs_and_criteria_sums_matching_the_order_of_the_original_inputs, + mul_coefficients_and_criteria_sums_matching_the_order_of_the_original_inputs, expected_result() ) } - fn envelope_payable_accounts_to_adjusted_accounts_before_finalization( + fn make_non_finalized_adjusted_accounts( payable_accounts: Vec<(PayableAccount, u128)>, ) -> Vec { payable_accounts .into_iter() .map(|(original_account, proposed_adjusted_balance)| { - AdjustedAccountBeforeFinalization { - original_account, - proposed_adjusted_balance, - } + AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance) }) .collect() } - fn give_by_reference( + fn by_reference( adjusted_accounts: &[AdjustedAccountBeforeFinalization], ) -> Vec<&AdjustedAccountBeforeFinalization> { adjusted_accounts.iter().collect() } #[test] - fn find_largest_disqualified_account_works_for_unequal_balances() { + fn calculate_disqualification_edge_works() { + let mut account = make_payable_account(111); + account.balance_wei = 300_000_000; + + let result = calculate_disqualification_edge(account.balance_wei); + + assert_eq!( + result, + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account.balance_wei) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + ) + } + + #[test] + fn find_largest_nominated_account_works_for_unequal_balances() { let account_1 = make_payable_account(111); let account_2 = make_payable_account(333); let account_3 = make_payable_account(222); let account_4 = make_payable_account(332); - let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + let accounts = make_non_finalized_adjusted_accounts(vec![ (account_1, 100_000_000), (account_2.clone(), 222_000_000), (account_3, 300_000_000), (account_4, 400_000_000), ]); - let referenced_accounts = give_by_reference(&accounts); + let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_disqualified_account(&referenced_accounts); + let result = find_largest_nominated_account(&referenced_non_finalized_accounts); assert_eq!( result, @@ -583,9 +633,8 @@ mod tests { } #[test] - fn find_largest_disqualified_account_for_equal_balances_chooses_younger_account() { - // because it will help do the impact on the cw balance that can be spread among less accounts, - // yet we prefer to keep the older and so more important account in the game + fn find_largest_nominated_account_for_equal_balances_chooses_younger_account() { + // We prefer to keep the older and so more important accounts in the game let now = SystemTime::now(); let account_1 = make_payable_account(111); let mut account_2 = make_payable_account(333); @@ -593,15 +642,15 @@ mod tests { let account_3 = make_payable_account(222); let mut account_4 = make_payable_account(333); account_4.last_paid_timestamp = now.checked_sub(Duration::from_secs(9999)).unwrap(); - let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + let accounts = make_non_finalized_adjusted_accounts(vec![ (account_1, 100_000_000), (account_2, 200_000_000), (account_3, 300_000_000), (account_4.clone(), 444_000_000), ]); - let referenced_accounts = give_by_reference(&accounts); + let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_disqualified_account(&referenced_accounts); + let result = find_largest_nominated_account(&referenced_non_finalized_accounts); assert_eq!( result, @@ -613,7 +662,7 @@ mod tests { } #[test] - fn find_largest_disqualified_account_for_accounts_with_equal_balances_as_well_as_age() { + fn find_largest_nominated_account_for_accounts_with_equal_balances_as_well_as_age() { let account = make_payable_account(111); let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); @@ -621,13 +670,13 @@ mod tests { account_1.wallet = wallet_1.clone(); let mut account_2 = account; account_2.wallet = wallet_2; - let accounts = envelope_payable_accounts_to_adjusted_accounts_before_finalization(vec![ + let accounts = make_non_finalized_adjusted_accounts(vec![ (account_1.clone(), 100_111_000), (account_2, 200_000_000), ]); - let referenced_accounts = give_by_reference(&accounts); + let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_disqualified_account(&referenced_accounts); + let result = find_largest_nominated_account(&referenced_non_finalized_accounts); assert_eq!( result, @@ -639,7 +688,7 @@ mod tests { } #[test] - fn ignores_accounts_with_original_balance_equal_to_the_proposed_ones() { + fn accounts_with_original_balances_equal_to_the_proposed_ones_are_not_outweighed() { let account_info = AdjustedAccountBeforeFinalization { original_account: PayableAccount { wallet: make_wallet("blah"), @@ -649,17 +698,17 @@ mod tests { }, proposed_adjusted_balance: 9_000_000_000, }; + let init = (vec![], vec![]); - let (outweighed, ok) = - possibly_outweighed_accounts_fold_guts((vec![], vec![]), account_info.clone()); + let (outweighed, ok) = possibly_outweighed_accounts_fold_guts(init, account_info.clone()); assert_eq!(outweighed, vec![]); assert_eq!(ok, vec![account_info]) } #[test] - fn pick_only_the_biggest_and_youngest_debt_from_all_disqualified_accounts_in_one_iteration() { - let test_name = "pick_only_the_biggest_and_youngest_debt_from_all_disqualified_accounts_in_one_iteration"; + fn picks_only_the_biggest_and_youngest_disqualified_account_in_one_iteration() { + let test_name = "picks_only_the_biggest_and_youngest_disqualified_account_in_one_iteration"; let now = SystemTime::now(); let cw_masq_balance = 2_000_000_000_000; let logger = Logger::new(test_name); @@ -700,7 +749,7 @@ mod tests { criteria_total, ); - let result = maybe_find_an_account_to_disqualify_in_this_iteration( + let result = try_finding_an_account_to_disqualify_in_this_iteration( &non_finalized_adjusted_accounts, &logger, ); @@ -724,23 +773,25 @@ mod tests { } } - fn assert_correct_payable_accounts_after_finalization( + fn assert_payable_accounts_after_adjustment_finalization( actual_accounts: Vec, - expected_parameters: Vec<(Wallet, u128)>, + expected_account_parts: Vec<(Wallet, u128)>, ) { let actual_accounts_simplified_and_sorted = actual_accounts .into_iter() .map(|account| (account.wallet.address(), account.balance_wei)) .sorted() .collect::>(); - let expected_parameters_sorted = expected_parameters + let expected_account_parts_sorted = expected_account_parts .into_iter() - .map(|(wallet, expected_balance)| (wallet.address(), expected_balance)) + .map(|(expected_wallet, expected_balance)| { + (expected_wallet.address(), expected_balance) + }) .sorted() .collect::>(); assert_eq!( actual_accounts_simplified_and_sorted, - expected_parameters_sorted + expected_account_parts_sorted ) } @@ -748,18 +799,23 @@ mod tests { fn exhaustive_status_is_constructed_properly() { let cw_balance_remainder = 45678; - let result = ExhaustionStatus::new(cw_balance_remainder); + let result = CwExhaustingStatus::new(cw_balance_remainder); assert_eq!(result.remainder, cw_balance_remainder); - assert_eq!(result.already_finalized_accounts, vec![]) + assert_eq!(result.accounts_finalized_so_far, vec![]) } #[test] - fn exhaust_cw_balance_totally_for_three_non_exhaustive_accounts_all_filled() { - // this can happen because some of the pre-qualified accounts could be - // eliminated for an insignificant pay and free the means for the other - // accounts and then we went through adjustment computation with some - // losses on precision, here we're gonna add in what was missing + fn three_non_exhaustive_accounts_all_refilled() { + // A seemingly irrational situation, this can happen when some of those + // originally qualified payables could get disqualified. Those would free some + // means that could be used for the other accounts. + // In the end, we have a final set with sub-optimal balances, despite + // the unallocated cw balance is larger than the entire sum of the original balances + // for this few resulting accounts. + // We can pay every account fully, so, why did we need to call the PaymentAdjuster + // in first place? + // The detail is in the loss of some accounts, allowing to pay more for the others. let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 45_000_000_000; let proposed_adjusted_balance_1 = 44_999_897_000; @@ -769,7 +825,7 @@ mod tests { let wallet_3 = make_wallet("ghi"); let original_requested_balance_3 = 41_000_000; let proposed_adjusted_balance_3 = 40_980_000; - let unallocated_cw_balance = original_requested_balance_1 + let original_cw_balance = original_requested_balance_1 + original_requested_balance_2 + original_requested_balance_3 + 5000; @@ -792,19 +848,19 @@ mod tests { ]; let result = - exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); + exhaust_cw_till_the_last_drop(non_finalized_adjusted_accounts, original_cw_balance); let expected_resulted_balances = vec![ (wallet_1, original_requested_balance_1), (wallet_2, original_requested_balance_2), (wallet_3, original_requested_balance_3), ]; - assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances) + assert_payable_accounts_after_adjustment_finalization(result, expected_resulted_balances) } #[test] - fn exhaust_cw_balance_totally_three_non_exhaustive_accounts_with_some_completely_filled_some_not( - ) { + fn three_non_exhaustive_accounts_with_one_completely_refilled_one_partly_one_not_at_all() { + // The smallest proposed adjusted balance gets refilled first, and then gradually on... let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 54_000_000_000; let proposed_adjusted_balance_1 = 53_898_000_000; @@ -814,7 +870,7 @@ mod tests { let wallet_3 = make_wallet("ghi"); let original_requested_balance_3 = 41_000_000; let proposed_adjusted_balance_3 = 40_980_000; - let unallocated_cw_balance = original_requested_balance_2 + let original_cw_balance = original_requested_balance_2 + original_requested_balance_3 + proposed_adjusted_balance_1 - 2_000_000; @@ -837,8 +893,7 @@ mod tests { ]; let result = - exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); - eprintln!("{:?}", result); + exhaust_cw_till_the_last_drop(non_finalized_adjusted_accounts, original_cw_balance); let expected_resulted_balances = vec![ (wallet_1, proposed_adjusted_balance_1), @@ -849,58 +904,12 @@ mod tests { .iter() .map(|(_, balance)| balance) .sum(); - let is_equal = check_sum == unallocated_cw_balance; - assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances); - assert!(is_equal) - } - - #[test] - fn exhaust_cw_balance_totally_three_non_exhaustive_accounts_with_two_of_them_completely_filled() - { - let wallet_1 = make_wallet("abc"); - let original_requested_balance_1 = 54_000_000_000; - let proposed_adjusted_balance_1 = 53_898_000_000; - let wallet_2 = make_wallet("def"); - let original_requested_balance_2 = 33_500_000_000; - let proposed_adjusted_balance_2 = 33_487_999_999; - let wallet_3 = make_wallet("ghi"); - let original_requested_balance_3 = 41_000_000; - let proposed_adjusted_balance_3 = 40_980_000; - let unallocated_cw_balance = original_requested_balance_2 - + original_requested_balance_3 - + proposed_adjusted_balance_1 - + 2_000_000; - let non_finalized_adjusted_accounts = vec![ - make_non_finalized_adjusted_account( - &wallet_1, - original_requested_balance_1, - proposed_adjusted_balance_1, - ), - make_non_finalized_adjusted_account( - &wallet_2, - original_requested_balance_2, - proposed_adjusted_balance_2, - ), - make_non_finalized_adjusted_account( - &wallet_3, - original_requested_balance_3, - proposed_adjusted_balance_3, - ), - ]; - - let result = - exhaust_cw_balance_totally(non_finalized_adjusted_accounts, unallocated_cw_balance); - - let expected_resulted_balances = vec![ - (wallet_1, 53_900_000_000), - (wallet_2, original_requested_balance_2), - (wallet_3, original_requested_balance_3), - ]; - assert_correct_payable_accounts_after_finalization(result, expected_resulted_balances) + assert_payable_accounts_after_adjustment_finalization(result, expected_resulted_balances); + assert_eq!(check_sum, original_cw_balance) } #[test] - fn list_accounts_under_the_disqualification_limit_employs_manifest_consts_of_insignificance() { + fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { let account_balance = 1_000_000; let prepare_account = |n: u64| { let mut account = make_payable_account(n); @@ -925,33 +934,31 @@ mod tests { payable_account_3, proposed_bad_balance_because_smaller, ); - let accounts_with_unchecked_adjustment = vec![ + let non_finalized_adjusted_accounts = vec![ account_info_1, account_info_2.clone(), account_info_3.clone(), ]; - let result = - list_accounts_under_the_disqualification_limit(&accounts_with_unchecked_adjustment); + let result = list_accounts_nominated_for_disqualification(&non_finalized_adjusted_accounts); let expected_disqualified_accounts = vec![&account_info_2, &account_info_3]; assert_eq!(result, expected_disqualified_accounts) } fn get_extreme_criteria_and_initial_accounts_order( - months_of_debt_and_balances_matrix: Vec<(usize, u128)>, + months_of_debt_and_balances: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { let now = SystemTime::now(); - let accounts = - make_extreme_accounts(Either::Right(months_of_debt_and_balances_matrix), now); + let accounts = make_extreme_accounts(Either::Right(months_of_debt_and_balances), now); let wallets_in_order = accounts .iter() .map(|account| account.wallet.clone()) .collect(); let subject = make_initialized_subject(now, None, None); - // when criteria are applied the collection will get sorted and will not necessarily have to match the initial order + // The initial order is remembered because when the criteria are applied the collection the collection + // also gets sorted and will not necessarily have to match the initial order let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(accounts); - (criteria_and_accounts, wallets_in_order) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 104823cfe..687cad575 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -26,21 +26,16 @@ use crate::accountant::payment_adjuster::inner::{ }; use crate::accountant::payment_adjuster::log_fns::{ before_and_after_debug_msg, log_adjustment_by_masq_required, - log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient, + log_transaction_fee_adjustment_ok_but_masq_balance_undoable, log_insufficient_transaction_fee_balance, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::{ +use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_fraction_preventing_mul_coeff, criteria_total, exhaust_cw_balance_totally, - maybe_find_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, - rebuild_accounts, reduce_collection_size_by_affordable_transaction_fee, - sort_in_descendant_order_by_weights, sum_as, -}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_sums_and_leave_accounts, keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::scanners::payable_scan_setup_msgs::{ FinancialAndTechDetails, PayablePaymentSetup, StageData, @@ -279,7 +274,7 @@ impl PaymentAdjusterReal { )?; match accounts { Either::Left(non_exhausted_accounts) => { - let accounts_by_fully_exhausted_cw = exhaust_cw_balance_totally( + let accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( non_exhausted_accounts, self.inner.original_cw_masq_balance_minor(), ); @@ -322,7 +317,7 @@ impl PaymentAdjusterReal { PaymentAdjusterError, > { let weighted_accounts_affordable_by_transaction_fee = - reduce_collection_size_by_affordable_transaction_fee( + keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest( criteria_and_accounts_in_descending_order, already_known_affordable_transaction_count, ); @@ -335,9 +330,7 @@ impl PaymentAdjusterReal { ) { Ok(answer) => answer, Err(e) => { - log_error_for_transaction_fee_adjustment_ok_but_masq_balance_insufficient( - &self.logger, - ); + log_transaction_fee_adjustment_ok_but_masq_balance_undoable(&self.logger); return Err(e); } }; @@ -351,8 +344,9 @@ impl PaymentAdjusterReal { Ok(Either::Left(adjustment_result_before_verification)) } false => { - let finalized_accounts = - rebuild_accounts(weighted_accounts_affordable_by_transaction_fee); + let finalized_accounts = drop_criteria_sums_and_leave_accounts( + weighted_accounts_affordable_by_transaction_fee, + ); Ok(Either::Right(finalized_accounts)) } } @@ -396,7 +390,7 @@ impl PaymentAdjusterReal { (decided_accounts, vec![]) } AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case, + case: special_case, remaining, } => { let here_decided_accounts = match special_case { @@ -449,7 +443,7 @@ impl PaymentAdjusterReal { .iterate_for_criteria(BalanceCriterionCalculator::new()); let collected_accounts_with_criteria = - sort_in_descendant_order_by_weights(criteria_and_accounts); + sort_in_descendant_order_by_criteria_sums(criteria_and_accounts); // effective only if the iterator is collected print_formulas_characteristics_for_diagnostics(); @@ -490,7 +484,8 @@ impl PaymentAdjusterReal { criteria_total: u128, ) -> Vec { let cw_masq_balance = self.inner.unallocated_cw_masq_balance_minor(); - let cpm_coeff = compute_fraction_preventing_mul_coeff(cw_masq_balance, criteria_total); + let cpm_coeff = + compute_fractional_numbers_preventing_mul_coefficient(cw_masq_balance, criteria_total); let multiplication_coeff_u256 = U256::from(cpm_coeff); let proportional_fragment_of_cw_balance = Self::compute_proportional_fragment( @@ -542,7 +537,7 @@ impl PaymentAdjusterReal { logger: &Logger, ) -> Either, AdjustmentIterationResult> { if let Some(disqualified_account_wallet) = - maybe_find_an_account_to_disqualify_in_this_iteration( + try_finding_an_account_to_disqualify_in_this_iteration( &non_finalized_adjusted_accounts, logger, ) @@ -562,7 +557,7 @@ impl PaymentAdjusterReal { .collect(); Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatInsignificantAccount, + case: TreatInsignificantAccount, remaining: remaining_reverted, }) } else { @@ -582,12 +577,10 @@ impl PaymentAdjusterReal { if outweighed.is_empty() { Either::Left(passing_through) } else { - let remaining = AdjustedAccountBeforeFinalization::finalize_collection( - passing_through, - ProposedAdjustmentResolution::Revert, - ); + let remaining = + finalize_collection(passing_through, ProposedAdjustmentResolution::Revert); Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatOutweighedAccounts(outweighed), + case: TreatOutweighedAccounts(outweighed), remaining, }) } @@ -694,7 +687,7 @@ mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialTreatment::TreatInsignificantAccount; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, @@ -722,7 +715,7 @@ mod tests { #[test] #[should_panic( - expected = "Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" )] fn payment_adjuster_new_is_created_with_inner_null() { let result = PaymentAdjusterReal::new(); @@ -1164,7 +1157,7 @@ mod tests { let remaining = match result { AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatInsignificantAccount, + case: TreatInsignificantAccount, remaining, } => remaining, x => panic!("we expected to see a disqualified account but got: {:?}", x), @@ -1180,7 +1173,7 @@ mod tests { let mut subject = make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { - special_case: TreatInsignificantAccount, + case: TreatInsignificantAccount, remaining: vec![], }; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index b8d66b9ad..c47b82788 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -23,33 +23,35 @@ pub fn make_initialized_subject( cw_masq_balance_minor_opt: Option, logger_opt: Option, ) -> PaymentAdjusterReal { + let cw_masq_balance_minor = cw_masq_balance_minor_opt.unwrap_or(0); + let logger = logger_opt.unwrap_or(Logger::new("test")); PaymentAdjusterReal { inner: Box::new(PaymentAdjusterInnerReal::new( now, None, - cw_masq_balance_minor_opt.unwrap_or(0), + cw_masq_balance_minor, )), - logger: logger_opt.unwrap_or(Logger::new("test")), + logger, } } pub fn make_extreme_accounts( - months_of_debt_vs_balance_setup: Either<(Vec, u128), Vec<(usize, u128)>>, + months_of_debt_and_balance_minor: Either<(Vec, u128), Vec<(usize, u128)>>, now: SystemTime, ) -> Vec { - let accounts_seed: Vec<(usize, u128)> = match months_of_debt_vs_balance_setup { + let accounts_seeds: Vec<(usize, u128)> = match months_of_debt_and_balance_minor { Either::Left((vec, constant_balance)) => vec .into_iter() .map(|months| (months, constant_balance)) .collect(), - Either::Right(vec_of_pairs) => vec_of_pairs, + Either::Right(specific_months_and_specific_balance) => specific_months_and_specific_balance, }; - accounts_seed + accounts_seeds .into_iter() .enumerate() - .map(|(idx, (months_count, balance_wei))| PayableAccount { + .map(|(idx, (months_count, balance_minor))| PayableAccount { wallet: make_wallet(&format!("blah{}", idx)), - balance_wei, + balance_wei: balance_minor, last_paid_timestamp: now .checked_sub(Duration::from_secs( months_count as u64 * (*ONE_MONTH_LONG_DEBT_SEC), diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index 98bb88b72..270aba73d 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -1,9 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; -use crate::masq_lib::utils::ExpectValue; use itertools::Itertools; pub struct MasqAdjustmentPossibilityVerifier {} @@ -16,61 +15,38 @@ impl MasqAdjustmentPossibilityVerifier { ) -> Result<(), PaymentAdjusterError> { // The reasoning is that the real adjustment algorithm will proceed by eliminating the biggest // account in each iteration, reaching out the smallest one eventually; if the smallest one - // reduced by the disqualification margin turned out possible to be paid by the available - // balance, we can state the Node is going to perform at least one blockchain transaction - + // reduced by the disqualification margin turned out possible to pay with the currently available + // balance, we can tell that this Node is going to initiate at least one blockchain transaction let sorted = accounts .iter() - .sorted_by(|account_b, account_a| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_a.balance_wei, &account_b.balance_wei) }) .collect::>(); - let smallest_account = sorted.first().expectv("qualified payable account"); + let smallest_account = sorted.first().expect("empty Vec of qualified payables "); - if (smallest_account.balance_wei * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - <= cw_masq_balance_minor - { + if calculate_disqualification_edge(smallest_account.balance_wei) <= cw_masq_balance_minor { Ok(()) } else { + let number_of_accounts = accounts.len(); Err(PaymentAdjusterError::AnalysisError( AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: accounts.len(), + number_of_accounts, cw_masq_balance_minor, }, )) } } - - fn calculate_breaking_line(account_balance: u128) -> u128 { - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - } } #[cfg(test)] mod tests { use crate::accountant::database_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use crate::accountant::test_utils::make_payable_account; - #[test] - fn calculate_breaking_line_works() { - let mut account = make_payable_account(111); - account.balance_wei = 300_000_000; - - let result = - MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account.balance_wei); - - assert_eq!( - result, - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account.balance_wei) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - ) - } - fn test_body_for_adjustment_possibility_nearly_rejected( original_accounts: Vec, cw_masq_balance: u128, @@ -91,8 +67,7 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = - MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_2.balance_wei) + 1; + let cw_masq_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; let original_accounts = vec![account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) @@ -104,8 +79,7 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = - MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_2.balance_wei); + let cw_masq_balance = calculate_disqualification_edge(account_2.balance_wei); let original_accounts = vec![account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) @@ -120,8 +94,7 @@ mod tests { account_2.balance_wei = 2_000_000_002; let mut account_3 = make_payable_account(333); account_3.balance_wei = 1_000_000_002; - let cw_masq_balance = - MasqAdjustmentPossibilityVerifier::calculate_breaking_line(account_3.balance_wei) - 1; + let cw_masq_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts.iter().collect::>(); From b33718d0548080a257d15c5430e39b1b1ebc586c Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 2 Sep 2023 23:00:25 +0200 Subject: [PATCH 078/250] GH-711: a few more little issues; all tests passing now --- node/src/accountant/mod.rs | 2 +- .../payment_adjuster/adjustment_runners.rs | 15 +++++---------- .../accountant/payment_adjuster/diagnostics.rs | 2 +- .../miscellaneous/helper_functions.rs | 9 ++------- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 22ef3b6e1..de286accf 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1660,7 +1660,7 @@ mod tests { log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ suffice for a payment for any of the recently qualified payables by the larger part of each. \ Please fund your consuming wallet in order to avoid being banned from your creditors. Failure \ - reason: Found less transaction fee balance than required by one payment. Number of canceled \ + reason: Found smaller transaction fee balance than does for a single payment. Number of canceled \ payments: 1. Transaction fee for a single account: 3,300,000 wei. Current consuming wallet \ balance: 123,000,000,000 wei.")); log_handler diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 067b78988..f3e53cdad 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -139,7 +139,7 @@ mod tests { MasqOnlyRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; use crate::accountant::test_utils::make_payable_account; @@ -210,9 +210,7 @@ mod tests { fn adjust_last_one_for_requested_balance_smaller_than_cw_but_not_needed_disqualified() { let now = SystemTime::now(); let account_balance = 4_500_600; - let cw_balance = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) - + 1; + let cw_balance = calculate_disqualification_edge(account_balance) + 1; let account = PayableAccount { wallet: make_wallet("abc"), balance_wei: account_balance, @@ -227,7 +225,7 @@ mod tests { result, Some(AdjustedAccountBeforeFinalization { original_account: account, - proposed_adjusted_balance: account_balance, + proposed_adjusted_balance: cw_balance, }) ) } @@ -250,8 +248,7 @@ mod tests { #[test] fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_on_edge() { let account_balance = 4_000_444; - let cw_balance = (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor; + let cw_balance = calculate_disqualification_edge(account_balance); test_adjust_last_one_when_disqualified(cw_balance, account_balance) } @@ -260,9 +257,7 @@ mod tests { fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() { let account_balance = 4_000_444; - let cw_balance = ((ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) - - 1; + let cw_balance = calculate_disqualification_edge(account_balance) - 1; test_adjust_last_one_when_disqualified(cw_balance, account_balance) } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 4be973351..120f92f24 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = true; +const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index eff0c084a..739c7eb7e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -600,11 +600,7 @@ mod tests { let result = calculate_disqualification_edge(account.balance_wei); - assert_eq!( - result, - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account.balance_wei) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - ) + assert_eq!(result, calculate_disqualification_edge(account.balance_wei)) } #[test] @@ -919,8 +915,7 @@ mod tests { let payable_account_1 = prepare_account(1); let payable_account_2 = prepare_account(2); let payable_account_3 = prepare_account(3); - let edge = account_balance / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor - * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier; + let edge = calculate_disqualification_edge(account_balance); let proposed_ok_balance = edge + 1; let account_info_1 = AdjustedAccountBeforeFinalization::new(payable_account_1, proposed_ok_balance); From 0176701720253b8536964db31d0e1030f6ee3014 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 4 Sep 2023 00:42:36 +0200 Subject: [PATCH 079/250] GH-711: errased some unrelated stuff in the big int processor away from this PR --- .../big_int_db_processor.rs | 39 +++++++---------- .../big_int_processing/big_int_divider.rs | 42 +++++++++---------- .../database_access_objects/receivable_dao.rs | 4 +- 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/node/src/accountant/big_int_processing/big_int_db_processor.rs b/node/src/accountant/big_int_processing/big_int_db_processor.rs index a3e3fa2e7..5c57bc1b5 100644 --- a/node/src/accountant/big_int_processing/big_int_db_processor.rs +++ b/node/src/accountant/big_int_processing/big_int_db_processor.rs @@ -28,7 +28,7 @@ impl<'a, T: TableNameDAO> BigIntDbProcessor { let mut stm = Self::prepare_statement(conn, main_sql); let params = config .params - .merge_with_main_clause_params((&config.params.wei_change_params).into()); + .merge_other_and_wei_params((&config.params.wei_change_params).into()); match stm.execute(params.as_slice()) { Ok(1) => Ok(()), Ok(x) => Err(BigIntDbError(format!("Expected 1 row to be changed for the unique key {} but got this count: {}", config.key_param_value(), x))), @@ -110,17 +110,17 @@ impl UpdateOverflowHandler for UpdateOverflowHandlerReal former_low_bytes, requested_wei_change, ); - - let wei_update_array = Self::wei_update_array( - requested_wei_change.high.name.as_str(), - &high_bytes_corrected, - requested_wei_change.low.name.as_str(), - &low_bytes_corrected, - ); + let wei_update_array: [(&str, &dyn ToSql); 2] = [ + ( + requested_wei_change.high.name.as_str(), + &high_bytes_corrected, + ), + (requested_wei_change.low.name.as_str(), &low_bytes_corrected), + ]; let execute_params = config .params - .merge_with_overflow_clause_params(wei_update_array); + .merge_other_and_wei_params_with_conditional_participants(wei_update_array); Self::execute_update(conn, &config, &execute_params); Ok(()) @@ -175,18 +175,6 @@ impl UpdateOverflowHandlerReal { (high_bytes_correction, low_bytes_correction) } - fn wei_update_array<'a>( - high_byte_param_name: &'a str, - high_byte_value: &'a i64, - low_byte_param_name: &'a str, - low_byte_value: &'a i64, - ) -> [(&'a str, &'a dyn ToSql); 2] { - [ - (high_byte_param_name, high_byte_value), - (low_byte_param_name, low_byte_value), - ] - } - fn return_first_error(two_results: [rusqlite::Result; 2]) -> rusqlite::Result<()> { let cached = format!("{:?}", two_results); match two_results.into_iter().find(|result| result.is_err()) { @@ -451,14 +439,14 @@ impl<'a> From<&'a WeiChangeAsHighAndLowBytes> for [(&'a str, &'a dyn ToSql); 2] } impl<'a> SQLParams<'a> { - fn merge_with_main_clause_params( + fn merge_other_and_wei_params( &'a self, wei_change_params: [(&'a str, &'a dyn ToSql); 2], ) -> Vec<(&'a str, &'a dyn ToSql)> { Self::merge_params(self.params_except_wei_change.iter(), wei_change_params) } - fn merge_with_overflow_clause_params( + fn merge_other_and_wei_params_with_conditional_participants( &'a self, wei_change_params: [(&'a str, &'a dyn ToSql); 2], ) -> Vec<(&'a str, &'a dyn ToSql)> { @@ -723,7 +711,8 @@ mod tests { } #[test] - fn merge_overflow_clause_params_can_filter_out_correct_params() { + fn merge_other_and_wei_params_with_conditional_participants_can_filter_out_just_update_params() + { let tuple_matrix = [ ("blah", &456_i64 as &dyn ExtendedParamsMarker), ("super key", &"abcxy"), @@ -754,7 +743,7 @@ mod tests { ], }; - let result = subject.merge_with_overflow_clause_params([ + let result = subject.merge_other_and_wei_params_with_conditional_participants([ ("always_present_1", &12), ("always_present_2", &77), ]); diff --git a/node/src/accountant/big_int_processing/big_int_divider.rs b/node/src/accountant/big_int_processing/big_int_divider.rs index 31ebacb8f..40d5a15f1 100644 --- a/node/src/accountant/big_int_processing/big_int_divider.rs +++ b/node/src/accountant/big_int_processing/big_int_divider.rs @@ -63,7 +63,7 @@ impl BigIntDivider { } } - pub fn register_big_int_deconstruction_fn_for_sqlite_connection( + pub fn register_big_int_deconstruction_for_sqlite_connection( conn: &Connection, ) -> rusqlite::Result<()> { Self::register_deconstruct_guts(conn, "slope_drop_high_bytes", "slope_drop_low_bytes") @@ -300,21 +300,20 @@ mod tests { ); } - fn create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + fn create_test_table_and_run_register_deconstruction_for_sqlite_connection( test_name: &str, ) -> Connection { let conn = create_new_empty_db("big_int_db_processor", test_name); - BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection(&conn).unwrap(); + BigIntDivider::register_big_int_deconstruction_for_sqlite_connection(&conn).unwrap(); conn.execute("create table test_table (computed_high_bytes int, computed_low_bytes int, database_parameter int not null)",[]).unwrap(); conn } #[test] fn register_deconstruct_for_sqlite_connection_works() { - let conn = - create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( - "register_deconstruct_for_sqlite_connection_works", - ); + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + "register_deconstruct_for_sqlite_connection_works", + ); let database_value_1: i64 = 12222; let database_value_2: i64 = 23333444; @@ -370,7 +369,7 @@ mod tests { #[test] fn register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_first_function() { - let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( "register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_first_function", ); @@ -398,7 +397,7 @@ mod tests { #[test] fn register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_second_function() { - let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( "register_deconstruct_for_sqlite_connection_returns_error_at_setting_the_second_function", ); @@ -426,7 +425,7 @@ mod tests { #[test] fn our_sqlite_functions_are_specialized_and_thus_should_not_take_positive_number_for_the_second_parameter( ) { - let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( "our_sqlite_functions_are_specialized_and_thus_should_not_take_positive_number_for_the_second_parameter" ); let error_invoker = |bytes_type: &str| { @@ -473,7 +472,7 @@ mod tests { #[test] fn our_sqlite_functions_are_specialized_thus_should_not_take_negative_number_for_the_third_parameter( ) { - let conn = create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( "our_sqlite_functions_are_specialized_thus_should_not_take_negative_number_for_the_third_parameter" ); let error_invoker = |bytes_type: &str| { @@ -517,10 +516,9 @@ mod tests { #[test] fn third_argument_error() { - let conn = - create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( - "third_argument_error", - ); + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + "third_argument_error", + ); let result = conn .execute( @@ -540,10 +538,9 @@ mod tests { #[test] fn first_fn_returns_internal_error_from_create_scalar_function() { - let conn = - create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( - "first_fn_returns_internal_error_from_create_scalar_function", - ); + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + "first_fn_returns_internal_error_from_create_scalar_function", + ); let result = BigIntDivider::register_deconstruct_guts( &conn, @@ -562,10 +559,9 @@ mod tests { #[test] fn second_fn_returns_internal_error_from_create_scalar_function() { - let conn = - create_test_table_and_run_register_big_int_deconstruction_fn_for_sqlite_connection( - "second_fn_returns_internal_error_from_create_scalar_function", - ); + let conn = create_test_table_and_run_register_deconstruction_for_sqlite_connection( + "second_fn_returns_internal_error_from_create_scalar_function", + ); let result = BigIntDivider::register_deconstruct_guts( &conn, diff --git a/node/src/accountant/database_access_objects/receivable_dao.rs b/node/src/accountant/database_access_objects/receivable_dao.rs index 1427f1db0..db91cbe93 100644 --- a/node/src/accountant/database_access_objects/receivable_dao.rs +++ b/node/src/accountant/database_access_objects/receivable_dao.rs @@ -96,7 +96,7 @@ pub trait ReceivableDaoFactory { impl ReceivableDaoFactory for DaoFactoryReal { fn make(&self) -> Box { let init_config = self.init_config.clone().add_special_conn_setup( - BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection, + BigIntDivider::register_big_int_deconstruction_for_sqlite_connection, ); let conn = connection_or_panic( &DbInitializerReal::default(), @@ -828,7 +828,7 @@ mod tests { home_dir: &Path, ) -> Box { let init_config = DbInitializationConfig::test_default().add_special_conn_setup( - BigIntDivider::register_big_int_deconstruction_fn_for_sqlite_connection, + BigIntDivider::register_big_int_deconstruction_for_sqlite_connection, ); DbInitializerReal::default() .initialize(home_dir, init_config) From b7e67cb76d9ed88752d9408ed1dc985c8e403d63 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 9 Oct 2023 21:22:44 +0200 Subject: [PATCH 080/250] GH-711: still merging but at least tests in Accountant alone are passing --- .github/workflows/ci-matrix.yml | 1 + .gitignore | 4 + automap/Cargo.lock | 43 +- automap/Cargo.toml | 1 - .../pcp_pmp_common/macos_specific.rs | 10 +- automap/src/comm_layer/pcp_pmp_common/mod.rs | 9 +- dns_utility/Cargo.lock | 88 +- masq/Cargo.toml | 1 - masq/src/command_context.rs | 25 +- masq/src/command_processor.rs | 8 +- masq/src/commands/change_password_command.rs | 6 +- masq/src/commands/check_password_command.rs | 9 +- masq/src/commands/configuration_command.rs | 21 +- .../src/commands/connection_status_command.rs | 6 +- .../financials_command/pretty_print_utils.rs | 3 +- masq/src/commands/generate_wallets_command.rs | 15 +- masq/src/commands/recover_wallets_command.rs | 13 +- .../src/commands/set_configuration_command.rs | 91 +- masq/src/commands/setup_command.rs | 195 +- masq/src/commands/wallet_addresses_command.rs | 6 +- masq/src/communications/broadcast_handler.rs | 25 +- masq/src/non_interactive_clap.rs | 5 +- masq/src/non_interactive_mode.rs | 9 +- masq/src/test_utils/mocks.rs | 6 +- ...ain_specific_directory_test_integration.rs | 46 + masq/tests/utils.rs | 8 +- masq_lib/Cargo.toml | 6 +- .../src/blockchains/blockchain_records.rs | 53 +- masq_lib/src/blockchains/chains.rs | 8 - masq_lib/src/constants.rs | 2 +- masq_lib/src/lib.rs | 1 + masq_lib/src/logger.rs | 5 +- masq_lib/src/messages.rs | 5 +- masq_lib/src/multi_config.rs | 10 +- masq_lib/src/shared_schema.rs | 79 +- masq_lib/src/type_obfuscation.rs | 83 + masq_lib/src/utils.rs | 133 +- multinode_integration_tests/Cargo.toml | 1 - .../docker/macos/Dockerfile | 10 +- multinode_integration_tests/src/masq_node.rs | 3 +- .../src/masq_real_node.rs | 59 +- multinode_integration_tests/src/utils.rs | 15 +- .../tests/blockchain_interaction_test.rs | 12 +- .../tests/bookkeeping_test.rs | 6 +- .../tests/data_routing_test.rs | 47 +- .../tests/min_hops_tests.rs | 123 + .../tests/verify_bill_payment.rs | 46 +- node/Cargo.lock | 198 +- node/Cargo.toml | 6 +- .../banned_dao.rs | 3 +- .../mod.rs | 0 .../payable_dao.rs | 37 +- .../pending_payable_dao.rs | 85 +- .../receivable_dao.rs | 37 +- .../utils.rs | 16 +- .../big_int_db_processor.rs | 14 +- .../big_int_divider.rs | 4 +- .../mod.rs | 0 .../test_utils.rs | 6 +- node/src/accountant/financials.rs | 4 +- node/src/accountant/mod.rs | 830 ++++--- .../payment_adjuster/adjustment_runners.rs | 34 +- .../age_criterion_calculator.rs | 2 +- .../balance_criterion_calculator.rs | 2 +- .../criteria_calculators/mod.rs | 2 +- .../payment_adjuster/diagnostics.rs | 4 +- .../accountant/payment_adjuster/log_fns.rs | 5 +- .../miscellaneous/data_structures.rs | 2 +- .../miscellaneous/helper_functions.rs | 4 +- node/src/accountant/payment_adjuster/mod.rs | 1598 +++++++------ .../accountant/payment_adjuster/test_utils.rs | 2 +- .../accountant/payment_adjuster/verifier.rs | 4 +- .../scanners/mid_scan_msg_handling/mod.rs | 3 + .../payable_scanner/agent_null.rs | 192 ++ .../payable_scanner/agent_web3.rs | 140 ++ .../payable_scanner/blockchain_agent.rs | 37 + .../payable_scanner/mod.rs | 79 + .../payable_scanner/msgs.rs | 72 + .../payable_scanner/test_utils.rs | 92 + node/src/accountant/scanners/mod.rs | 444 +++- .../scanners/payable_scan_setup_msgs.rs | 74 +- .../scanners/scan_mid_procedures.rs | 98 +- .../src/accountant/scanners/scanners_utils.rs | 75 +- node/src/accountant/scanners/test_utils.rs | 10 + node/src/accountant/test_utils.rs | 357 ++- node/src/actor_system_factory.rs | 507 ++--- node/src/apps.rs | 40 +- node/src/blockchain/bip32.rs | 65 +- node/src/blockchain/blockchain_bridge.rs | 1234 +++++----- .../lower_level_interface_null.rs | 108 + .../blockchain_interface_null/mod.rs | 281 +++ .../batch_payable_tools.rs | 4 +- .../lower_level_interface_web3.rs | 393 ++++ .../blockchain_interface_web3/mod.rs} | 2021 +++++++---------- .../blockchain_interface_web3/test_utils.rs | 172 ++ .../data_structures/errors.rs | 262 +++ .../data_structures/mod.rs | 29 + .../lower_level_interface.rs | 24 + .../blockchain/blockchain_interface/mod.rs | 52 + .../blockchain_interface/test_utils.rs | 131 ++ .../blockchain_interface_initializer.rs | 75 + node/src/blockchain/mod.rs | 3 +- node/src/blockchain/payer.rs | 2 +- node/src/blockchain/test_utils.rs | 367 +-- node/src/bootstrapper.rs | 39 +- node/src/daemon/daemon_initializer.rs | 4 +- node/src/daemon/setup_reporter.rs | 450 +++- node/src/database/config_dumper.rs | 168 +- node/src/database/db_initializer.rs | 14 +- .../src/database/db_migrations/db_migrator.rs | 4 + .../migrations/migration_3_to_4.rs | 8 +- .../migrations/migration_4_to_5.rs | 4 +- .../migrations/migration_6_to_7.rs | 4 +- .../migrations/migration_7_to_8.rs | 79 + .../migrations/migration_8_to_9.rs | 70 + .../database/db_migrations/migrations/mod.rs | 2 + .../database/db_migrations/migrator_utils.rs | 4 +- node/src/database/db_migrations/test_utils.rs | 3 +- node/src/db_config/config_dao.rs | 17 +- node/src/db_config/config_dao_null.rs | 34 +- node/src/db_config/mocks.rs | 3 +- .../src/db_config/persistent_configuration.rs | 151 +- node/src/dispatcher.rs | 7 +- node/src/hopper/routing_service.rs | 2 +- node/src/neighborhood/gossip_acceptor.rs | 40 +- node/src/neighborhood/mod.rs | 486 +++- .../src/neighborhood/neighborhood_database.rs | 10 +- .../neighborhood/overall_connection_status.rs | 29 +- node/src/node_configurator/configurator.rs | 411 +++- node/src/node_configurator/mod.rs | 136 +- .../node_configurator_standard.rs | 183 +- .../unprivileged_parse_args_configuration.rs | 129 +- node/src/node_test_utils.rs | 15 +- node/src/proxy_server/mod.rs | 127 +- node/src/run_modes_factories.rs | 12 +- node/src/server_initializer.rs | 12 +- node/src/stream_messages.rs | 27 +- node/src/stream_reader.rs | 11 +- node/src/sub_lib/accountant.rs | 32 +- node/src/sub_lib/blockchain_bridge.rs | 83 +- node/src/sub_lib/combined_parameters.rs | 6 +- node/src/sub_lib/configurator.rs | 1 + node/src/sub_lib/mod.rs | 1 - node/src/sub_lib/neighborhood.rs | 50 +- node/src/sub_lib/proxy_server.rs | 3 - .../sub_lib/set_consuming_wallet_message.rs | 9 - node/src/sub_lib/utils.rs | 10 +- node/src/sub_lib/wallet.rs | 69 +- node/src/test_utils/actor_system_factory.rs | 16 + node/src/test_utils/database_utils.rs | 8 +- node/src/test_utils/http_test_server.rs | 73 + node/src/test_utils/mod.rs | 83 +- .../src/test_utils/neighborhood_test_utils.rs | 9 +- .../persistent_configuration_mock.rs | 154 +- node/src/test_utils/recorder.rs | 257 ++- .../test_utils/recorder_stop_conditions.rs | 50 +- node/tests/dump_configuration_test.rs | 8 +- node/tests/financials_test.rs | 8 +- node/tests/initialization_test.rs | 36 +- .../node_exits_from_future_panic_test.rs | 27 +- node/tests/ui_gateway_test.rs | 27 +- node/tests/utils.rs | 66 +- 162 files changed, 10056 insertions(+), 5428 deletions(-) create mode 100644 masq/tests/chain_specific_directory_test_integration.rs create mode 100644 masq_lib/src/type_obfuscation.rs create mode 100644 multinode_integration_tests/tests/min_hops_tests.rs rename node/src/accountant/{database_access_objects => db_access_objects}/banned_dao.rs (99%) rename node/src/accountant/{database_access_objects => db_access_objects}/mod.rs (100%) rename node/src/accountant/{database_access_objects => db_access_objects}/payable_dao.rs (98%) rename node/src/accountant/{database_access_objects => db_access_objects}/pending_payable_dao.rs (93%) rename node/src/accountant/{database_access_objects => db_access_objects}/receivable_dao.rs (98%) rename node/src/accountant/{database_access_objects => db_access_objects}/utils.rs (98%) rename node/src/accountant/{big_int_processing => db_big_integer}/big_int_db_processor.rs (98%) rename node/src/accountant/{big_int_processing => db_big_integer}/big_int_divider.rs (99%) rename node/src/accountant/{big_int_processing => db_big_integer}/mod.rs (100%) rename node/src/accountant/{big_int_processing => db_big_integer}/test_utils.rs (75%) create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/mod.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/msgs.rs create mode 100644 node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs create mode 100644 node/src/accountant/scanners/test_utils.rs create mode 100644 node/src/blockchain/blockchain_interface/blockchain_interface_null/lower_level_interface_null.rs create mode 100644 node/src/blockchain/blockchain_interface/blockchain_interface_null/mod.rs rename node/src/blockchain/{ => blockchain_interface/blockchain_interface_web3}/batch_payable_tools.rs (96%) create mode 100644 node/src/blockchain/blockchain_interface/blockchain_interface_web3/lower_level_interface_web3.rs rename node/src/blockchain/{blockchain_interface.rs => blockchain_interface/blockchain_interface_web3/mod.rs} (55%) create mode 100644 node/src/blockchain/blockchain_interface/blockchain_interface_web3/test_utils.rs create mode 100644 node/src/blockchain/blockchain_interface/data_structures/errors.rs create mode 100644 node/src/blockchain/blockchain_interface/data_structures/mod.rs create mode 100644 node/src/blockchain/blockchain_interface/lower_level_interface.rs create mode 100644 node/src/blockchain/blockchain_interface/mod.rs create mode 100644 node/src/blockchain/blockchain_interface/test_utils.rs create mode 100644 node/src/blockchain/blockchain_interface_initializer.rs create mode 100644 node/src/database/db_migrations/migrations/migration_7_to_8.rs create mode 100644 node/src/database/db_migrations/migrations/migration_8_to_9.rs delete mode 100644 node/src/sub_lib/set_consuming_wallet_message.rs create mode 100644 node/src/test_utils/actor_system_factory.rs create mode 100644 node/src/test_utils/http_test_server.rs diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index 1c4739990..71a537d48 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -8,6 +8,7 @@ on: jobs: build: + if: github.event.pull_request.draft == false strategy: fail-fast: true matrix: diff --git a/.gitignore b/.gitignore index 91a07eaad..88a2ab5f9 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ Temporary Items **/.idea/**/dataSources.ids **/.idea/**/dataSources.xml **/.idea/**/dataSources.local.xml +**/.idea/**/dbnavigator.xml **/.idea/**/sqlDataSources.xml **/.idea/**/dynamic.xml **/.idea/**/uiDesigner.xml @@ -136,3 +137,6 @@ node.log lnk*.tmp /*/target/ node_ui/static/binaries/ + +# VSCode +.vscode \ No newline at end of file diff --git a/automap/Cargo.lock b/automap/Cargo.lock index ca3c01835..dfd2a58cb 100644 --- a/automap/Cargo.lock +++ b/automap/Cargo.lock @@ -517,6 +517,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "either" version = "1.8.1" @@ -1037,6 +1057,7 @@ dependencies = [ "clap", "const_format", "crossbeam-channel 0.5.8", + "dirs", "ethereum-types", "itertools", "lazy_static", @@ -1298,7 +1319,7 @@ dependencies = [ "cfg-if 0.1.10", "cloudabi", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "rustc_version", "smallvec", "winapi 0.3.9", @@ -1610,6 +1631,26 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.9", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.8.1" diff --git a/automap/Cargo.toml b/automap/Cargo.toml index 0af805918..25fd22993 100644 --- a/automap/Cargo.toml +++ b/automap/Cargo.toml @@ -3,7 +3,6 @@ name = "automap" version = "0.7.3" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" -copyright = "Copyright (c) 2019-2021, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." description = "Library full of code to make routers map ports through firewalls" edition = "2021" #workspace = "../node" diff --git a/automap/src/comm_layer/pcp_pmp_common/macos_specific.rs b/automap/src/comm_layer/pcp_pmp_common/macos_specific.rs index b3cc6f116..d82ad7a1a 100644 --- a/automap/src/comm_layer/pcp_pmp_common/macos_specific.rs +++ b/automap/src/comm_layer/pcp_pmp_common/macos_specific.rs @@ -1,8 +1,10 @@ // Copyright (c) 2019-2021, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + #![cfg(target_os = "macos")] use crate::comm_layer::pcp_pmp_common::FindRoutersCommand; use crate::comm_layer::AutomapError; +use masq_lib::utils::to_string; use std::net::IpAddr; use std::str::FromStr; @@ -13,13 +15,9 @@ pub fn macos_find_routers(command: &dyn FindRoutersCommand) -> Result>() - }) + .map(|line| line.split(": ").map(to_string).collect::>()) .filter(|pieces| pieces.len() > 1) .map(|pieces| IpAddr::from_str(&pieces[1]).expect("Bad syntax from route -n get default")) .collect::>(); diff --git a/automap/src/comm_layer/pcp_pmp_common/mod.rs b/automap/src/comm_layer/pcp_pmp_common/mod.rs index 00f7ccada..e0d62b346 100644 --- a/automap/src/comm_layer/pcp_pmp_common/mod.rs +++ b/automap/src/comm_layer/pcp_pmp_common/mod.rs @@ -245,7 +245,7 @@ pub mod tests { let result = subject.execute_command("ls booga"); match result { - Err(stderr) if stderr.contains("No such file or directory") => (), + Err(stderr) if stderr.contains("booga") => (), //directory booga does not exist Err(stderr) => panic!("Unexpected content in stderr: '{}'", stderr), x => panic!("Expected error message in stderr; got {:?}", x), } @@ -259,12 +259,7 @@ pub mod tests { let result = subject.execute_command("dir booga"); match result { - Err(stderr) - if stderr.contains("The system cannot find the file specified") - || stderr.contains("No such file or directory") => - { - () - } + Err(stderr) if stderr.contains("booga") => (), //directory booga does not exist Err(stderr) => panic!("Unexpected content in stderr: '{}'", stderr), x => panic!("Expected error message in stderr; got {:?}", x), } diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock index 9f372ce7b..04c8bf983 100644 --- a/dns_utility/Cargo.lock +++ b/dns_utility/Cargo.lock @@ -408,6 +408,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "dns_utility" version = "0.7.3" @@ -557,6 +577,17 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "gimli" version = "0.27.2" @@ -749,9 +780,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libsecp256k1" @@ -829,6 +860,7 @@ dependencies = [ "clap", "const_format", "crossbeam-channel 0.5.8", + "dirs", "ethereum-types", "itertools", "lazy_static", @@ -1065,7 +1097,7 @@ dependencies = [ "cfg-if 0.1.10", "cloudabi", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "rustc_version", "smallvec", "winapi 0.3.9", @@ -1182,7 +1214,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", @@ -1230,7 +1262,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", ] [[package]] @@ -1319,6 +1351,26 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.10", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.8.1" @@ -1610,6 +1662,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.15", +] + [[package]] name = "time" version = "0.1.45" @@ -2082,6 +2154,12 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "websocket" version = "0.26.5" diff --git a/masq/Cargo.toml b/masq/Cargo.toml index 5bc3641db..f2300bbd4 100644 --- a/masq/Cargo.toml +++ b/masq/Cargo.toml @@ -3,7 +3,6 @@ name = "masq" version = "0.7.3" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" -copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." description = "Reference implementation of user interface for MASQ Node" edition = "2021" workspace = "../node" diff --git a/masq/src/command_context.rs b/masq/src/command_context.rs index de30503bb..41308e5bb 100644 --- a/masq/src/command_context.rs +++ b/masq/src/command_context.rs @@ -11,6 +11,8 @@ use std::fmt::{Debug, Formatter}; use std::io; use std::io::{Read, Write}; +pub const DEFAULT_TRANSACT_TIMEOUT_MILLIS: u64 = 1000; + #[derive(Clone, Debug, PartialEq, Eq)] pub enum ContextError { ConnectionRefused(String), @@ -151,6 +153,7 @@ mod tests { ConnectionDropped, ConnectionRefused, PayloadError, }; use crate::communications::broadcast_handler::BroadcastHandleInactive; + use crate::test_utils::mocks::TRANSACT_TIMEOUT_MILLIS_FOR_TESTS; use masq_lib::messages::{FromMessageBody, UiCrashRequest, UiSetupRequest}; use masq_lib::messages::{ToMessageBody, UiShutdownRequest, UiShutdownResponse}; use masq_lib::test_utils::fake_stream_holder::{ByteArrayReader, ByteArrayWriter}; @@ -160,6 +163,11 @@ mod tests { use masq_lib::ui_traffic_converter::{TrafficConversionError, UnmarshalError}; use masq_lib::utils::{find_free_port, running_test}; + #[test] + fn constant_has_correct_values() { + assert_eq!(DEFAULT_TRANSACT_TIMEOUT_MILLIS, 1000); + } + #[test] fn error_conversion_happy_path() { running_test(); @@ -233,7 +241,12 @@ mod tests { subject.stdout = Box::new(stdout); subject.stderr = Box::new(stderr); - let response = subject.transact(UiShutdownRequest {}.tmb(1), 1000).unwrap(); + let response = subject + .transact( + UiShutdownRequest {}.tmb(1), + TRANSACT_TIMEOUT_MILLIS_FOR_TESTS, + ) + .unwrap(); let mut input = String::new(); subject.stdin().read_to_string(&mut input).unwrap(); write!(subject.stdout(), "This is stdout.").unwrap(); @@ -283,7 +296,10 @@ mod tests { let broadcast_handle = BroadcastHandleInactive; let mut subject = CommandContextReal::new(port, None, Box::new(broadcast_handle)).unwrap(); - let response = subject.transact(UiSetupRequest { values: vec![] }.tmb(1), 1000); + let response = subject.transact( + UiSetupRequest { values: vec![] }.tmb(1), + TRANSACT_TIMEOUT_MILLIS_FOR_TESTS, + ); assert_eq!(response, Err(PayloadError(101, "booga".to_string()))); stop_handle.stop(); @@ -298,7 +314,10 @@ mod tests { let broadcast_handle = BroadcastHandleInactive; let mut subject = CommandContextReal::new(port, None, Box::new(broadcast_handle)).unwrap(); - let response = subject.transact(UiSetupRequest { values: vec![] }.tmb(1), 1000); + let response = subject.transact( + UiSetupRequest { values: vec![] }.tmb(1), + TRANSACT_TIMEOUT_MILLIS_FOR_TESTS, + ); match response { Err(ConnectionDropped(_)) => (), diff --git a/masq/src/command_processor.rs b/masq/src/command_processor.rs index ade770c61..4438e2b0e 100644 --- a/masq/src/command_processor.rs +++ b/masq/src/command_processor.rs @@ -75,7 +75,7 @@ impl CommandProcessor for CommandProcessorReal { #[cfg(test)] mod tests { use super::*; - use crate::command_context::CommandContext; + use crate::command_context::{CommandContext, DEFAULT_TRANSACT_TIMEOUT_MILLIS}; use crate::commands::check_password_command::CheckPasswordCommand; use crate::communications::broadcast_handler::{ BroadcastHandleInactive, BroadcastHandler, BroadcastHandlerReal, @@ -85,7 +85,7 @@ mod tests { use masq_lib::messages::UiShutdownRequest; use masq_lib::messages::{ToMessageBody, UiCheckPasswordResponse, UiUndeliveredFireAndForget}; use masq_lib::test_utils::mock_websockets_server::MockWebSocketsServer; - use masq_lib::utils::{find_free_port, running_test}; + use masq_lib::utils::{find_free_port, running_test, to_string}; use std::thread; use std::time::Duration; @@ -94,7 +94,7 @@ mod tests { impl Command for TestCommand { fn execute<'a>(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> { - match context.transact(UiShutdownRequest {}.tmb(1), 1000) { + match context.transact(UiShutdownRequest {}.tmb(1), DEFAULT_TRANSACT_TIMEOUT_MILLIS) { Ok(_) => Ok(()), Err(e) => Err(CommandError::Other(format!("{:?}", e))), } @@ -210,7 +210,7 @@ mod tests { fn whole_message() -> String { TameCommand::MESSAGE_IN_PIECES .iter() - .map(|str| str.to_string()) + .map(to_string) .collect() } } diff --git a/masq/src/commands/change_password_command.rs b/masq/src/commands/change_password_command.rs index ef2fb020c..9dcc71075 100644 --- a/masq/src/commands/change_password_command.rs +++ b/masq/src/commands/change_password_command.rs @@ -9,9 +9,7 @@ use clap::{App, Arg, SubCommand}; use masq_lib::messages::{ UiChangePasswordRequest, UiChangePasswordResponse, UiNewPasswordBroadcast, }; -use masq_lib::{implement_as_any, short_writeln}; -#[cfg(test)] -use std::any::Any; +use masq_lib::{as_any_in_trait_impl, short_writeln}; use std::io::Write; #[derive(Debug, PartialEq, Eq)] @@ -81,7 +79,7 @@ impl Command for ChangePasswordCommand { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } pub fn change_password_subcommand() -> App<'static, 'static> { diff --git a/masq/src/commands/check_password_command.rs b/masq/src/commands/check_password_command.rs index 3dcc5abb2..ee7081e7d 100644 --- a/masq/src/commands/check_password_command.rs +++ b/masq/src/commands/check_password_command.rs @@ -5,11 +5,10 @@ use crate::commands::commands_common::{ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use clap::{App, Arg, SubCommand}; -use masq_lib::implement_as_any; +use masq_lib::as_any_in_trait_impl; use masq_lib::messages::{UiCheckPasswordRequest, UiCheckPasswordResponse}; use masq_lib::short_writeln; -#[cfg(test)] -use std::any::Any; +use masq_lib::utils::to_string; #[derive(Debug, PartialEq, Eq)] pub struct CheckPasswordCommand { @@ -52,7 +51,7 @@ impl Command for CheckPasswordCommand { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } impl CheckPasswordCommand { @@ -62,7 +61,7 @@ impl CheckPasswordCommand { Err(e) => return Err(format!("{}", e)), }; Ok(Self { - db_password_opt: matches.value_of("db-password").map(|r| r.to_string()), + db_password_opt: matches.value_of("db-password").map(to_string), }) } } diff --git a/masq/src/commands/configuration_command.rs b/masq/src/commands/configuration_command.rs index fea412a37..766eedc6d 100644 --- a/masq/src/commands/configuration_command.rs +++ b/masq/src/commands/configuration_command.rs @@ -6,12 +6,11 @@ use crate::commands::commands_common::{ dump_parameter_line, transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use clap::{App, Arg, SubCommand}; +use masq_lib::as_any_in_trait_impl; use masq_lib::constants::NODE_NOT_RUNNING_ERROR; -use masq_lib::implement_as_any; use masq_lib::messages::{UiConfigurationRequest, UiConfigurationResponse}; use masq_lib::short_writeln; -#[cfg(test)] -use std::any::Any; +use masq_lib::utils::to_string; use std::fmt::{Debug, Display}; use std::io::Write; use std::iter::once; @@ -65,7 +64,7 @@ impl Command for ConfigurationCommand { } } - implement_as_any!(); + as_any_in_trait_impl!(); } impl ConfigurationCommand { @@ -76,7 +75,7 @@ impl ConfigurationCommand { }; Ok(ConfigurationCommand { - db_password: matches.value_of("db-password").map(|s| s.to_string()), + db_password: matches.value_of("db-password").map(to_string), }) } @@ -111,6 +110,14 @@ impl ConfigurationCommand { &Self::interpret_option(&configuration.earning_wallet_address_opt), ); dump_parameter_line(stream, "Gas price:", &configuration.gas_price.to_string()); + dump_parameter_line( + stream, + "Max block count:", + &configuration + .max_block_count_opt + .map(|m| m.separate_with_commas()) + .unwrap_or_else(|| "[Unlimited]".to_string()), + ); dump_parameter_line( stream, "Neighborhood mode:", @@ -306,6 +313,7 @@ mod tests { chain_name: "ropsten".to_string(), gas_price: 2345, neighborhood_mode: "standard".to_string(), + max_block_count_opt: None, consuming_wallet_private_key_opt: Some("consuming wallet private key".to_string()), consuming_wallet_address_opt: Some("consuming wallet address".to_string()), earning_wallet_address_opt: Some("earning address".to_string()), @@ -367,6 +375,7 @@ mod tests { |Current schema version: schema version\n\ |Earning wallet address: earning address\n\ |Gas price: 2345\n\ +|Max block count: [Unlimited]\n\ |Neighborhood mode: standard\n\ |Port mapping protocol: PCP\n\ |Start block: 3456\n\ @@ -403,6 +412,7 @@ mod tests { clandestine_port: 1234, chain_name: "mumbai".to_string(), gas_price: 2345, + max_block_count_opt: Some(100_000), neighborhood_mode: "zero-hop".to_string(), consuming_wallet_address_opt: None, consuming_wallet_private_key_opt: None, @@ -463,6 +473,7 @@ mod tests { |Current schema version: schema version\n\ |Earning wallet address: earning wallet\n\ |Gas price: 2345\n\ +|Max block count: 100,000\n\ |Neighborhood mode: zero-hop\n\ |Port mapping protocol: PCP\n\ |Start block: 3456\n\ diff --git a/masq/src/commands/connection_status_command.rs b/masq/src/commands/connection_status_command.rs index bc3ece105..fc96c6716 100644 --- a/masq/src/commands/connection_status_command.rs +++ b/masq/src/commands/connection_status_command.rs @@ -6,14 +6,12 @@ use crate::commands::commands_common::{ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use clap::{App, SubCommand}; +use masq_lib::as_any_in_trait_impl; use masq_lib::constants::NODE_NOT_RUNNING_ERROR; -use masq_lib::implement_as_any; use masq_lib::messages::{ UiConnectionStage, UiConnectionStatusRequest, UiConnectionStatusResponse, }; use masq_lib::short_writeln; -#[cfg(test)] -use std::any::Any; use std::fmt::Debug; #[derive(Debug, PartialEq, Eq)] @@ -64,7 +62,7 @@ impl Command for ConnectionStatusCommand { } } - implement_as_any!(); + as_any_in_trait_impl!(); } impl ConnectionStatusCommand { diff --git a/masq/src/commands/financials_command/pretty_print_utils.rs b/masq/src/commands/financials_command/pretty_print_utils.rs index 16088b7d5..2adbfbad4 100644 --- a/masq/src/commands/financials_command/pretty_print_utils.rs +++ b/masq/src/commands/financials_command/pretty_print_utils.rs @@ -11,6 +11,7 @@ pub(in crate::commands::financials_command) mod restricted { use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::messages::{UiPayableAccount, UiReceivableAccount}; use masq_lib::short_writeln; + use masq_lib::utils::to_string; use std::fmt::{Debug, Display}; use std::io::Write; use thousands::Separable; @@ -163,7 +164,7 @@ pub(in crate::commands::financials_command) mod restricted { fn prepare_headings_of_records(is_gwei: bool) -> (HeadingsHolder, HeadingsHolder) { fn to_owned_strings(words: Vec<&str>) -> Vec { - words.iter().map(|str| str.to_string()).collect() + words.iter().map(to_string).collect() } let balance = gwei_or_masq_balance(is_gwei); ( diff --git a/masq/src/commands/generate_wallets_command.rs b/masq/src/commands/generate_wallets_command.rs index 7cc388393..6bfd8b359 100644 --- a/masq/src/commands/generate_wallets_command.rs +++ b/masq/src/commands/generate_wallets_command.rs @@ -1,19 +1,16 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -#[cfg(test)] -use std::any::Any; - use crate::command_context::CommandContext; use crate::commands::commands_common::{ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use clap::{App, Arg, SubCommand}; use lazy_static::lazy_static; -use masq_lib::implement_as_any; +use masq_lib::as_any_in_trait_impl; use masq_lib::messages::{UiGenerateSeedSpec, UiGenerateWalletsRequest, UiGenerateWalletsResponse}; use masq_lib::short_writeln; -use masq_lib::utils::DEFAULT_CONSUMING_DERIVATION_PATH; use masq_lib::utils::DEFAULT_EARNING_DERIVATION_PATH; +use masq_lib::utils::{to_string, DEFAULT_CONSUMING_DERIVATION_PATH}; lazy_static! { static ref CONSUMING_PATH_HELP: String = format!( @@ -85,8 +82,8 @@ impl GenerateWalletsCommand { Err(e) => return Err(format!("{}", e)), }; - let consuming_path_opt = matches.value_of("consuming-path").map(|p| p.to_string()); - let earning_path_opt = matches.value_of("earning-path").map(|p| p.to_string()); + let consuming_path_opt = matches.value_of("consuming-path").map(to_string); + let earning_path_opt = matches.value_of("earning-path").map(to_string); let seed_spec_opt = if consuming_path_opt.is_some() || earning_path_opt.is_some() { Some(SeedSpec { word_count: matches @@ -99,7 +96,7 @@ impl GenerateWalletsCommand { .value_of("language") .expect("language not properly defaulted") .to_string(), - passphrase_opt: matches.value_of("passphrase").map(|s| s.to_string()), + passphrase_opt: matches.value_of("passphrase").map(to_string), }) } else { None @@ -168,7 +165,7 @@ impl Command for GenerateWalletsCommand { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } pub fn generate_wallets_subcommand() -> App<'static, 'static> { diff --git a/masq/src/commands/recover_wallets_command.rs b/masq/src/commands/recover_wallets_command.rs index 34ea03179..b7f3e6bc1 100644 --- a/masq/src/commands/recover_wallets_command.rs +++ b/masq/src/commands/recover_wallets_command.rs @@ -6,11 +6,10 @@ use crate::commands::commands_common::{ }; use clap::{App, Arg, ArgGroup, SubCommand}; use itertools::{Either, Itertools}; -use masq_lib::implement_as_any; +use masq_lib::as_any_in_trait_impl; use masq_lib::messages::{UiRecoverSeedSpec, UiRecoverWalletsRequest, UiRecoverWalletsResponse}; use masq_lib::short_writeln; -#[cfg(test)] -use std::any::Any; +use masq_lib::utils::to_string; #[derive(Debug, PartialEq, Eq)] pub struct SeedSpec { @@ -36,12 +35,12 @@ impl RecoverWalletsCommand { let mnemonic_phrase_opt = matches .value_of("mnemonic-phrase") - .map(|mpv| mpv.split(' ').map(|x| x.to_string()).collect_vec()); + .map(|mpv| mpv.split(' ').map(to_string).collect_vec()); let language = matches .value_of("language") .expect("language is not properly defaulted by clap") .to_string(); - let passphrase_opt = matches.value_of("passphrase").map(|mp| mp.to_string()); + let passphrase_opt = matches.value_of("passphrase").map(to_string); let seed_spec_opt = mnemonic_phrase_opt.map(|mnemonic_phrase| SeedSpec { mnemonic_phrase, language, @@ -121,7 +120,7 @@ impl Command for RecoverWalletsCommand { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } const RECOVER_WALLETS_ABOUT: &str = @@ -340,7 +339,7 @@ mod tests { db_password: "password".to_string(), seed_spec_opt: Some (SeedSpec { mnemonic_phrase: "river message view churn potato cabbage craft luggage tape month observe obvious" - .split(" ").into_iter().map(|x| x.to_string()).collect(), + .split(" ").into_iter().map(to_string).collect(), passphrase_opt: Some("booga".to_string()), language: "English".to_string(), }), diff --git a/masq/src/commands/set_configuration_command.rs b/masq/src/commands/set_configuration_command.rs index df4dbfbc4..72fabc498 100644 --- a/masq/src/commands/set_configuration_command.rs +++ b/masq/src/commands/set_configuration_command.rs @@ -1,14 +1,12 @@ use crate::command_context::CommandContext; use crate::commands::commands_common::{transaction, Command, CommandError}; use clap::{App, Arg, ArgGroup, SubCommand}; -use masq_lib::implement_as_any; +use masq_lib::as_any_in_trait_impl; use masq_lib::messages::{UiSetConfigurationRequest, UiSetConfigurationResponse}; -use masq_lib::shared_schema::common_validators; -use masq_lib::shared_schema::GAS_PRICE_HELP; +use masq_lib::shared_schema::gas_price_arg; +use masq_lib::shared_schema::min_hops_arg; use masq_lib::short_writeln; use masq_lib::utils::ExpectValue; -#[cfg(test)] -use std::any::Any; #[derive(Debug, PartialEq, Eq)] pub struct SetConfigurationCommand { @@ -55,7 +53,7 @@ impl Command for SetConfigurationCommand { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } const SET_CONFIGURATION_ABOUT: &str = @@ -63,18 +61,15 @@ const SET_CONFIGURATION_ABOUT: &str = const START_BLOCK_HELP: &str = "Ordinal number of the Ethereum block where scanning for transactions will start."; +pub fn set_configurationify<'a>(shared_schema_arg: Arg<'a, 'a>) -> Arg<'a, 'a> { + shared_schema_arg.takes_value(true).min_values(1) +} + pub fn set_configuration_subcommand() -> App<'static, 'static> { SubCommand::with_name("set-configuration") .about(SET_CONFIGURATION_ABOUT) - .arg( - Arg::with_name("gas-price") - .help(&GAS_PRICE_HELP) - .long("gas-price") - .value_name("GAS-PRICE") - .takes_value(true) - .required(false) - .validator(common_validators::validate_gas_price), - ) + .arg(set_configurationify(gas_price_arg())) + .arg(set_configurationify(min_hops_arg())) .arg( Arg::with_name("start-block") .help(START_BLOCK_HELP) @@ -86,7 +81,7 @@ pub fn set_configuration_subcommand() -> App<'static, 'static> { ) .group( ArgGroup::with_name("parameter") - .args(&["gas-price", "start-block"]) + .args(&["gas-price", "min-hops", "start-block"]) .required(true), ) } @@ -135,16 +130,51 @@ mod tests { #[test] fn command_execution_works_all_fine() { + test_command_execution("--start-block", "123456"); + test_command_execution("--gas-price", "123456"); + test_command_execution("--min-hops", "6"); + } + + #[test] + fn set_configuration_command_throws_err_for_missing_values() { + set_configuration_command_throws_err_for_missing_value("--start-block"); + set_configuration_command_throws_err_for_missing_value("--gas-price"); + set_configuration_command_throws_err_for_missing_value("--min-hops"); + } + + #[test] + fn set_configuration_command_throws_err_for_invalid_arg() { + let (invalid_arg, some_value) = ("--invalid-arg", "123"); + + let result = SetConfigurationCommand::new(&[ + "set-configuration".to_string(), + invalid_arg.to_string(), + some_value.to_string(), + ]); + + let err_msg = result.unwrap_err(); + assert!(err_msg.contains("Found argument"), "{}", err_msg); + assert!(err_msg.contains("--invalid-arg"), "{}", err_msg); + assert!( + err_msg.contains("which wasn't expected, or isn't valid in this context"), + "{}", + err_msg + ); + } + + fn test_command_execution(name: &str, value: &str) { let transact_params_arc = Arc::new(Mutex::new(vec![])); let mut context = CommandContextMock::new() .transact_params(&transact_params_arc) .transact_result(Ok(UiSetConfigurationResponse {}.tmb(4321))); let stdout_arc = context.stdout_arc(); let stderr_arc = context.stderr_arc(); - let subject = SetConfigurationCommand { - name: "start-block".to_string(), - value: "123456".to_string(), - }; + let subject = SetConfigurationCommand::new(&[ + "set-configuration".to_string(), + name.to_string(), + value.to_string(), + ]) + .unwrap(); let result = subject.execute(&mut context); @@ -154,16 +184,31 @@ mod tests { *transact_params, vec![( UiSetConfigurationRequest { - name: "start-block".to_string(), - value: "123456".to_string() + name: name[2..].to_string(), + value: value.to_string(), } .tmb(0), 1000 )] ); let stderr = stderr_arc.lock().unwrap(); - assert_eq!(*stderr.get_string(), String::new()); + assert_eq!(&stderr.get_string(), ""); let stdout = stdout_arc.lock().unwrap(); assert_eq!(&stdout.get_string(), "Parameter was successfully set\n"); } + + fn set_configuration_command_throws_err_for_missing_value(name: &str) { + let result = + SetConfigurationCommand::new(&["set-configuration".to_string(), name.to_string()]); + + let err_msg_fragment = "requires a value but none was supplied"; + let actual_err_msg = result.err().unwrap(); + assert_eq!( + actual_err_msg.contains(err_msg_fragment), + true, + "'{}' did not contain '{}'", + actual_err_msg, + err_msg_fragment + ); + } } diff --git a/masq/src/commands/setup_command.rs b/masq/src/commands/setup_command.rs index 022a7af5e..baec35e1b 100644 --- a/masq/src/commands/setup_command.rs +++ b/masq/src/commands/setup_command.rs @@ -4,18 +4,18 @@ use crate::command_context::CommandContext; use crate::commands::commands_common::{transaction, Command, CommandError}; use crate::terminal::terminal_interface::TerminalWrapper; use clap::{value_t, App, SubCommand}; +use masq_lib::as_any_in_trait_impl; use masq_lib::constants::SETUP_ERROR; -use masq_lib::implement_as_any; use masq_lib::messages::{ UiSetupBroadcast, UiSetupInner, UiSetupRequest, UiSetupRequestValue, UiSetupResponse, + UiSetupResponseValue, UiSetupResponseValueStatus, }; -use masq_lib::shared_schema::shared_app; +use masq_lib::shared_schema::{data_directory_arg, shared_app}; use masq_lib::short_writeln; -use masq_lib::utils::index_of_from; -#[cfg(test)] -use std::any::Any; +use masq_lib::utils::{index_of_from, DATA_DIRECTORY_DAEMON_HELP}; use std::fmt::Debug; use std::io::Write; +use std::iter::Iterator; pub const SETUP_COMMAND_TIMEOUT_MILLIS: u64 = 30000; @@ -24,6 +24,7 @@ const SETUP_COMMAND_ABOUT: &str = pub fn setup_subcommand() -> App<'static, 'static> { shared_app(SubCommand::with_name("setup").about(SETUP_COMMAND_ABOUT)) + .arg(data_directory_arg(DATA_DIRECTORY_DAEMON_HELP.as_str())) } #[derive(Debug, PartialEq, Eq)] @@ -50,7 +51,7 @@ impl Command for SetupCommand { Err(e) => Err(e), } } - implement_as_any!(); + as_any_in_trait_impl!(); } impl SetupCommand { @@ -72,6 +73,7 @@ impl SetupCommand { } }) .collect::>(); + values.sort_by(|a, b| { a.name .partial_cmp(&b.name) @@ -107,6 +109,21 @@ impl SetupCommand { .expect("String comparison failed") }); short_writeln!(stdout, "{:29} {:64} {}", "NAME", "VALUE", "STATUS"); + let chain_and_data_dir = + |p: &UiSetupResponseValue| (p.name.to_owned(), (p.value.clone(), p.status)); + let chain = inner + .values + .iter() + .find(|&p| p.name.as_str() == "chain") + .map(chain_and_data_dir) + .expect("Chain name is missing in setup cluster!"); + let data_directory = inner + .values + .iter() + .find(|&p| p.name.as_str() == "data-directory") + .map(chain_and_data_dir) + .expect("data-directory is missing in setup cluster!"); + inner.values.into_iter().for_each(|value| { short_writeln!( stdout, @@ -130,6 +147,14 @@ impl SetupCommand { "NOTE: no changes were made to the setup because the Node is currently running.\n" ); } + if chain.1 .1 != UiSetupResponseValueStatus::Default + || data_directory.1 .1 != UiSetupResponseValueStatus::Default + { + short_writeln!( + stdout, + "NOTE: your data directory was modified to match the chain parameter.\n" + ); + } } } @@ -139,11 +164,10 @@ mod tests { use crate::command_factory::{CommandFactory, CommandFactoryReal}; use crate::communications::broadcast_handler::StreamFactory; use crate::test_utils::mocks::{CommandContextMock, TerminalPassiveMock, TestStreamFactory}; - use masq_lib::constants::ETH_ROPSTEN_FULL_IDENTIFIER; + use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::messages::ToMessageBody; use masq_lib::messages::UiSetupResponseValueStatus::{Configured, Default, Set}; use masq_lib::messages::{UiSetupRequest, UiSetupResponse, UiSetupResponseValue}; - use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use std::sync::{Arc, Mutex}; #[test] @@ -179,7 +203,8 @@ mod tests { .transact_result(Ok(UiSetupResponse { running: false, values: vec![ - UiSetupResponseValue::new("chain", "eth-ropsten", Configured), + UiSetupResponseValue::new("chain", "eth-mainnet", Configured), + UiSetupResponseValue::new("data-directory", "/home/booga/eth-mainnet", Default), UiSetupResponseValue::new("neighborhood-mode", "zero-hop", Set), UiSetupResponseValue::new( "neighbors", @@ -202,7 +227,7 @@ mod tests { "zero-hop".to_string(), "--log-level".to_string(), "--chain".to_string(), - "eth-ropsten".to_string(), + "polygon-mainnet".to_string(), "--scan-intervals".to_string(), "123|111|228".to_string(), "--scans".to_string(), @@ -219,10 +244,7 @@ mod tests { vec![( UiSetupRequest { values: vec![ - UiSetupRequestValue::new( - "chain", - TEST_DEFAULT_CHAIN.rec().literal_identifier - ), + UiSetupRequestValue::new("chain", DEFAULT_CHAIN.rec().literal_identifier), UiSetupRequestValue::clear("log-level"), UiSetupRequestValue::new("neighborhood-mode", "zero-hop"), UiSetupRequestValue::new("scan-intervals", "123|111|228"), @@ -235,12 +257,13 @@ mod tests { ); assert_eq! (stdout_arc.lock().unwrap().get_string(), "NAME VALUE STATUS\n\ -chain eth-ropsten Configured\n\ +chain eth-mainnet Configured\n\ +data-directory /home/booga/eth-mainnet Default\n\ neighborhood-mode zero-hop Set\n\ neighbors masq://eth-mainnet:95VjByq5tEUUpDcczA__zXWGE6-7YFEvzN4CDVoPbWw@13.23.13.23:4545 Set\n\ scan-intervals 123|111|228 Set\n\ scans off Set\n\ -\n"); +\nNOTE: your data directory was modified to match the chain parameter.\n\n"); assert_eq!(stderr_arc.lock().unwrap().get_string(), String::new()); } @@ -252,7 +275,8 @@ scans off .transact_result(Ok(UiSetupResponse { running: true, values: vec![ - UiSetupResponseValue::new("chain", ETH_ROPSTEN_FULL_IDENTIFIER, Set), + UiSetupResponseValue::new("chain", "eth-mainnet", Set), + UiSetupResponseValue::new("data-directory", "/home/booga/eth-mainnet", Set), UiSetupResponseValue::new("neighborhood-mode", "zero-hop", Configured), UiSetupResponseValue::new("clandestine-port", "8534", Default), ], @@ -268,7 +292,7 @@ scans off "--neighborhood-mode".to_string(), "zero-hop".to_string(), "--chain".to_string(), - "eth-ropsten".to_string(), + "eth-mainnet".to_string(), "--clandestine-port".to_string(), "8534".to_string(), "--log-level".to_string(), @@ -284,7 +308,7 @@ scans off vec![( UiSetupRequest { values: vec![ - UiSetupRequestValue::new("chain", "eth-ropsten"), + UiSetupRequestValue::new("chain", "eth-mainnet"), UiSetupRequestValue::new("clandestine-port", "8534"), UiSetupRequestValue::clear("log-level"), UiSetupRequestValue::new("neighborhood-mode", "zero-hop"), @@ -296,15 +320,16 @@ scans off ); assert_eq! (stdout_arc.lock().unwrap().get_string(), "NAME VALUE STATUS\n\ -chain eth-ropsten Set\n\ +chain eth-mainnet Set\n\ clandestine-port 8534 Default\n\ +data-directory /home/booga/eth-mainnet Set\n\ neighborhood-mode zero-hop Configured\n\ \n\ ERRORS: ip Nosir, I don't like it.\n\ \n\ NOTE: no changes were made to the setup because the Node is currently running.\n\ -\n"); +\nNOTE: your data directory was modified to match the chain parameter.\n\n"); assert_eq!(stderr_arc.lock().unwrap().get_string(), String::new()); } @@ -313,9 +338,10 @@ NOTE: no changes were made to the setup because the Node is currently running.\n let message = UiSetupBroadcast { running: false, values: vec![ - UiSetupResponseValue::new("chain", "eth-ropsten", Set), + UiSetupResponseValue::new("chain", "eth-mainnet", Set), UiSetupResponseValue::new("neighborhood-mode", "zero-hop", Configured), UiSetupResponseValue::new("clandestine-port", "8534", Default), + UiSetupResponseValue::new("data-directory", "/home/booga/eth-mainnet", Set), ], errors: vec![("ip".to_string(), "No sir, I don't like it.".to_string())], }; @@ -330,12 +356,133 @@ NOTE: no changes were made to the setup because the Node is currently running.\n Daemon setup has changed:\n\ \n\ NAME VALUE STATUS\n\ -chain eth-ropsten Set\n\ +chain eth-mainnet Set\n\ clandestine-port 8534 Default\n\ +data-directory /home/booga/eth-mainnet Set\n\ neighborhood-mode zero-hop Configured\n\ \n\ ERRORS: ip No sir, I don't like it.\n\ -\n"); +\nNOTE: your data directory was modified to match the chain parameter.\n\n"); + } + + #[derive(Debug, PartialEq, Eq)] + pub struct SetupCommandData { + chain_str: Option, + data_directory: Option, + chain_name_expected: Option<&'static str>, + data_directory_expected: Option<&'static str>, + note_expected: bool, + status_chain: UiSetupResponseValueStatus, + status_data_dir: UiSetupResponseValueStatus, + } + + #[test] + fn setup_command_with_data_directory_shows_right_path() { + #[rustfmt::skip] + vec![ + SetupCommandData { + chain_str: None, + data_directory: None, + chain_name_expected: Some("polygon-mainnet"), + data_directory_expected: Some("/home/cooga/.local/MASQ/polygon-mainnet"), + note_expected: false, + status_chain: UiSetupResponseValueStatus::Default, + status_data_dir: UiSetupResponseValueStatus::Default, + }, + SetupCommandData { + chain_str: Some("polygon-mumbai".to_owned()), + data_directory: None, + chain_name_expected: Some("polygon-mumbai"), + data_directory_expected: Some("/home/cooga/.local/MASQ/polygon-mumbai"), + note_expected: true, + status_chain: UiSetupResponseValueStatus::Set, + status_data_dir: UiSetupResponseValueStatus::Default, + }, + SetupCommandData { + chain_str: None, + data_directory: Some("booga".to_owned()), + chain_name_expected: Some("polygon-mainnet"), + data_directory_expected: Some("booga/polygon-mainnet"), + note_expected: true, + status_chain: UiSetupResponseValueStatus::Default, + status_data_dir: UiSetupResponseValueStatus::Set, + }, + SetupCommandData { + chain_str: Some("polygon-mumbai".to_owned()), + data_directory: Some("booga/polygon-mumbai".to_owned()), + chain_name_expected: Some("polygon-mumbai"), + data_directory_expected: Some("booga/polygon-mumbai/polygon-mumbai"), + note_expected: true, + status_chain: UiSetupResponseValueStatus::Set, + status_data_dir: UiSetupResponseValueStatus::Set, + }, + ].iter().for_each(| + data| { + let note_expected_real = match data.note_expected { + true => "\nNOTE: your data directory was modified to match the chain parameter.\n", + _ => "" + }; + let status_data_dir_str = match data.status_data_dir { + Default => "Default", + Set => "Set", + Configured => "Configured", + UiSetupResponseValueStatus::Blank => "Blank", + UiSetupResponseValueStatus::Required => "Required" + }; + let status_chain_str = match data.status_chain { + Default => "Default", + Set => "Set", + Configured => "Configured", + UiSetupResponseValueStatus::Blank => "Blank", + UiSetupResponseValueStatus::Required => "Required" + }; + let expected = format!("\ +NAME VALUE STATUS\n\ +{:29} {:64} {}\n{:29} {:64} {}\n{}\n", + "chain", + data.chain_name_expected.unwrap(), + status_chain_str, + "data-directory", + data.data_directory_expected.unwrap(), + status_data_dir_str, + note_expected_real + ); + let chain_real = match &data.chain_str { Some(chain) => chain, _ => "polygon-mainnet" }; + let data_directory_real = match &data.data_directory { + Some(dir) => format!("{}/{}", dir, chain_real), + _ => format!("/home/cooga/.local/MASQ/{}", chain_real) + }; + process_setup_command_for_given_attributes( + chain_real, + &data_directory_real, + &expected, + data.status_chain, + data.status_data_dir + ); + }); + } + + fn process_setup_command_for_given_attributes( + chain: &str, + data_directory: &str, + expected: &str, + status_chain: UiSetupResponseValueStatus, + status_data_dir: UiSetupResponseValueStatus, + ) { + let message = UiSetupResponse { + running: false, + values: vec![ + UiSetupResponseValue::new("chain", chain, status_chain), + UiSetupResponseValue::new("data-directory", data_directory, status_data_dir), + ], + errors: vec![], + }; + let (stream_factory, handle) = TestStreamFactory::new(); + let (mut stdout, _) = stream_factory.make(); + + SetupCommand::dump_setup(UiSetupInner::from(message), &mut stdout); + + assert_eq!(handle.stdout_so_far(), expected); } } diff --git a/masq/src/commands/wallet_addresses_command.rs b/masq/src/commands/wallet_addresses_command.rs index e1f90e4fc..edc2e2a0f 100644 --- a/masq/src/commands/wallet_addresses_command.rs +++ b/masq/src/commands/wallet_addresses_command.rs @@ -6,9 +6,7 @@ use crate::commands::commands_common::{ }; use clap::{App, Arg, SubCommand}; use masq_lib::messages::{UiWalletAddressesRequest, UiWalletAddressesResponse}; -use masq_lib::{implement_as_any, short_writeln}; -#[cfg(test)] -use std::any::Any; +use masq_lib::{as_any_in_trait_impl, short_writeln}; #[derive(Debug, PartialEq, Eq)] pub struct WalletAddressesCommand { @@ -68,7 +66,7 @@ impl Command for WalletAddressesCommand { ); Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } #[cfg(test)] diff --git a/masq/src/communications/broadcast_handler.rs b/masq/src/communications/broadcast_handler.rs index c798c1981..e4903ef17 100644 --- a/masq/src/communications/broadcast_handler.rs +++ b/masq/src/communications/broadcast_handler.rs @@ -11,18 +11,16 @@ use masq_lib::messages::{ }; use masq_lib::ui_gateway::MessageBody; use masq_lib::utils::ExpectValue; -use masq_lib::{declare_as_any, implement_as_any, short_writeln}; +use masq_lib::{as_any_in_trait, as_any_in_trait_impl, short_writeln}; use std::fmt::Debug; use std::io::Write; use std::thread; use crate::notifications::connection_change_notification::ConnectionChangeNotification; -#[cfg(test)] -use std::any::Any; pub trait BroadcastHandle: Send { fn send(&self, message_body: MessageBody); - declare_as_any!(); + as_any_in_trait!(); } pub struct BroadcastHandleInactive; @@ -30,7 +28,7 @@ pub struct BroadcastHandleInactive; impl BroadcastHandle for BroadcastHandleInactive { //simply dropped (unless we find a better use for such a message) fn send(&self, _message_body: MessageBody) {} - implement_as_any!(); + as_any_in_trait_impl!(); } pub struct BroadcastHandleGeneric { @@ -193,6 +191,7 @@ mod tests { TerminalPassiveMock, TestStreamFactory, }; use crossbeam_channel::{bounded, unbounded, Receiver}; + use masq_lib::messages::UiSetupResponseValueStatus::{Configured, Default}; use masq_lib::messages::{ CrashReason, SerializableLogLevel, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, UiLogBroadcast, UiNodeCrashedBroadcast, @@ -211,7 +210,10 @@ mod tests { .start(Box::new(factory)); let message = UiSetupBroadcast { running: true, - values: vec![], + values: vec![ + UiSetupResponseValue::new("chain", "eth-ropsten", Configured), + UiSetupResponseValue::new("data-directory", "/home/booga", Default), + ], errors: vec![], } .tmb(0); @@ -390,7 +392,10 @@ mod tests { }; let good_message = UiSetupBroadcast { running: true, - values: vec![], + values: vec![ + UiSetupResponseValue::new("chain", "eth-ropsten", Configured), + UiSetupResponseValue::new("data-directory", "/home/booga", Default), + ], errors: vec![], } .tmb(0); @@ -468,6 +473,11 @@ mod tests { value: "error".to_string(), status: UiSetupResponseValueStatus::Set, }, + UiSetupResponseValue { + name: "data-directory".to_string(), + value: "/home/booga".to_string(), + status: UiSetupResponseValueStatus::Default, + }, ], errors: vec![], }; @@ -478,6 +488,7 @@ mod tests { NAME VALUE STATUS chain ropsten Configured +data-directory /home/booga Default ip 4.4.4.4 Set log-level error Set neighborhood-mode standard Default diff --git a/masq/src/non_interactive_clap.rs b/masq/src/non_interactive_clap.rs index 14b5fa7db..46f60957a 100644 --- a/masq/src/non_interactive_clap.rs +++ b/masq/src/non_interactive_clap.rs @@ -40,13 +40,14 @@ fn handle_help_or_version_if_required<'a>(args: &[String]) -> ArgMatches<'a> { mod tests { use super::*; use masq_lib::constants::DEFAULT_UI_PORT; + use masq_lib::utils::to_string; #[test] fn non_interactive_clap_real_produces_default_value_for_ui_port() { let result = NonInteractiveClapReal.non_interactive_initial_clap_operations( &vec!["masq", "setup", "--chain"] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(), ); @@ -58,7 +59,7 @@ mod tests { let result = NonInteractiveClapReal.non_interactive_initial_clap_operations( &vec!["masq", "--ui-port", "10000", "setup", "--log-level", "off"] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(), ); diff --git a/masq/src/non_interactive_mode.rs b/masq/src/non_interactive_mode.rs index 290e57cc1..164b9c293 100644 --- a/masq/src/non_interactive_mode.rs +++ b/masq/src/non_interactive_mode.rs @@ -176,6 +176,7 @@ mod tests { use masq_lib::intentionally_blank; use masq_lib::messages::{ToMessageBody, UiNewPasswordBroadcast, UiShutdownRequest}; use masq_lib::test_utils::fake_stream_holder::FakeStreamHolder; + use masq_lib::utils::to_string; use std::any::Any; use std::sync::{Arc, Mutex}; @@ -217,7 +218,7 @@ mod tests { "--param3", ] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(), ); @@ -228,7 +229,7 @@ mod tests { vec![ vec!["subcommand", "--param1", "value1", "--param2", "--param3"] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(), ] ); @@ -478,7 +479,7 @@ mod tests { fn extract_subcommands_can_process_normal_non_interactive_request() { let args = vec!["masq", "setup", "--log-level", "off"] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(); let result = Main::extract_subcommand(&args); @@ -497,7 +498,7 @@ mod tests { fn extract_subcommands_can_process_non_interactive_request_including_special_port() { let args = vec!["masq", "--ui-port", "10000", "setup", "--log-level", "off"] .iter() - .map(|str| str.to_string()) + .map(to_string) .collect::>(); let result = Main::extract_subcommand(&args); diff --git a/masq/src/test_utils/mocks.rs b/masq/src/test_utils/mocks.rs index 2ce41c058..955fa578a 100644 --- a/masq/src/test_utils/mocks.rs +++ b/masq/src/test_utils/mocks.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::command_context::{CommandContext, ContextError}; +use crate::command_context::{CommandContext, ContextError, DEFAULT_TRANSACT_TIMEOUT_MILLIS}; use crate::command_factory::{CommandFactory, CommandFactoryError}; use crate::command_processor::{CommandProcessor, CommandProcessorFactory}; use crate::commands::commands_common::CommandError::Transmission; @@ -24,6 +24,8 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use std::{io, thread}; +pub const TRANSACT_TIMEOUT_MILLIS_FOR_TESTS: u64 = DEFAULT_TRANSACT_TIMEOUT_MILLIS; + #[derive(Default)] pub struct CommandFactoryMock { make_params: Arc>>>, @@ -290,7 +292,7 @@ impl Command for MockCommand { fn execute(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> { write!(context.stdout(), "MockCommand output").unwrap(); write!(context.stderr(), "MockCommand error").unwrap(); - match context.transact(self.message.clone(), 1000) { + match context.transact(self.message.clone(), TRANSACT_TIMEOUT_MILLIS_FOR_TESTS) { Ok(_) => self.execute_results.borrow_mut().remove(0), Err(e) => Err(Transmission(format!("{:?}", e))), } diff --git a/masq/tests/chain_specific_directory_test_integration.rs b/masq/tests/chain_specific_directory_test_integration.rs new file mode 100644 index 000000000..39d782cfc --- /dev/null +++ b/masq/tests/chain_specific_directory_test_integration.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::utils::{DaemonProcess, MasqProcess}; +use masq_lib::utils::find_free_port; +use std::thread; +use std::time::Duration; + +mod utils; + +#[test] +fn ensure_data_directory_has_specific_chain_directory_within_integration() { + let port = find_free_port(); + let daemon_handle = DaemonProcess::new().start(port.clone()); + let masq_handle = MasqProcess::new().start_noninteractive(vec![ + "--ui-port", + *&port.to_string().to_owned().as_str(), + "setup", + ]); + let (stdout, _stderr, _exit_code) = masq_handle.stop(); + let mut masq_handle2 = MasqProcess::new().start_interactive(port, true); + let mut stdin_handle = masq_handle2.create_stdin_handle(); + + stdin_handle.type_command("setup --data-directory /home/booga/masqhome/polygon-mainnet"); + + thread::sleep(Duration::from_millis(1000)); + + stdin_handle.type_command("exit"); + + let (stdout2, _stderr2, _exit_code2) = masq_handle2.stop(); + let expected = format!( + "{:29} {:64} {}", + "data-directory", "/home/booga/masqhome/polygon-mainnet", "Set" + ); + + assert!( + !stdout.contains("MASQ/polygon-mainnet/MASQ/polygon-mainnet Default"), + "Wrong directory: duplication of /MASQ/polygon-mainnet when Default" + ); + assert!( + stdout2.contains(&expected), + "Wrong directory: missing chain specific directory when Set:\nstdout: {}\n", + stdout2 + ); + + daemon_handle.kill(); +} diff --git a/masq/tests/utils.rs b/masq/tests/utils.rs index 7c7044df3..4ded72f11 100644 --- a/masq/tests/utils.rs +++ b/masq/tests/utils.rs @@ -107,10 +107,12 @@ impl StdinHandle { } pub fn type_command(&mut self, command: &str) { match self.stdin.write_fmt(format_args!("{}\n", command)) { - Ok(_) => (), + Ok(_) => match self.stdin.flush() { + Ok(_) => (), + Err(e) => panic!("flush failed in type_command: {}", e), + }, Err(e) => { - eprintln!("{}", e); - panic!("type_command failed: {}", e) + panic!("type_command write failed: {}", e) } } } diff --git a/masq_lib/Cargo.toml b/masq_lib/Cargo.toml index 836a622b8..60fa87c79 100644 --- a/masq_lib/Cargo.toml +++ b/masq_lib/Cargo.toml @@ -3,17 +3,17 @@ name = "masq_lib" version = "0.7.3" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" -copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." description = "Code common to Node and masq; also, temporarily, to dns_utility" edition = "2021" workspace = "../node" [dependencies] actix = "0.7.9" -time = {version = "0.3.11", features = [ "formatting" ]} clap = "2.33.3" const_format = "0.2.22" crossbeam-channel = "0.5.1" +dirs = "4.0.0" +ethereum-types = "0.9.0" itertools = "0.10.1" lazy_static = "1.4.0" log = "0.4.8" @@ -21,9 +21,9 @@ regex = "1.5.4" serde = "1.0.133" serde_derive = "1.0.133" serde_json = "1.0.74" +time = {version = "0.3.11", features = [ "formatting" ]} tiny-hderive = "0.3.0" toml = "0.5.8" -ethereum-types = "0.9.0" websocket = {version = "0.26.2", default-features = false, features = ["sync"]} [features] diff --git a/masq_lib/src/blockchains/blockchain_records.rs b/masq_lib/src/blockchains/blockchain_records.rs index 1bd13e622..f9b4bcab0 100644 --- a/masq_lib/src/blockchains/blockchain_records.rs +++ b/masq_lib/src/blockchains/blockchain_records.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::blockchains::chains::{Chain, ChainFamily}; +use crate::blockchains::chains::Chain; use crate::constants::{ DEV_CHAIN_FULL_IDENTIFIER, ETH_MAINNET_CONTRACT_CREATION_BLOCK, ETH_MAINNET_FULL_IDENTIFIER, ETH_ROPSTEN_FULL_IDENTIFIER, MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK, @@ -15,7 +15,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ BlockchainRecord { self_id: Chain::PolyMainnet, num_chain_id: 137, - chain_family: ChainFamily::Polygon, literal_identifier: POLYGON_MAINNET_FULL_IDENTIFIER, contract: POLYGON_MAINNET_CONTRACT_ADDRESS, contract_creation_block: POLYGON_MAINNET_CONTRACT_CREATION_BLOCK, @@ -23,7 +22,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ BlockchainRecord { self_id: Chain::EthMainnet, num_chain_id: 1, - chain_family: ChainFamily::Eth, literal_identifier: ETH_MAINNET_FULL_IDENTIFIER, contract: ETH_MAINNET_CONTRACT_ADDRESS, contract_creation_block: ETH_MAINNET_CONTRACT_CREATION_BLOCK, @@ -31,7 +29,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ BlockchainRecord { self_id: Chain::PolyMumbai, num_chain_id: 80001, - chain_family: ChainFamily::Polygon, literal_identifier: POLYGON_MUMBAI_FULL_IDENTIFIER, contract: MUMBAI_TESTNET_CONTRACT_ADDRESS, contract_creation_block: MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK, @@ -39,7 +36,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ BlockchainRecord { self_id: Chain::EthRopsten, num_chain_id: 3, - chain_family: ChainFamily::Eth, literal_identifier: ETH_ROPSTEN_FULL_IDENTIFIER, contract: ROPSTEN_TESTNET_CONTRACT_ADDRESS, contract_creation_block: ROPSTEN_TESTNET_CONTRACT_CREATION_BLOCK, @@ -47,7 +43,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ BlockchainRecord { self_id: Chain::Dev, num_chain_id: 2, - chain_family: ChainFamily::Dev, literal_identifier: DEV_CHAIN_FULL_IDENTIFIER, contract: MULTINODE_TESTNET_CONTRACT_ADDRESS, contract_creation_block: MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK, @@ -58,7 +53,6 @@ pub const CHAINS: [BlockchainRecord; 5] = [ pub struct BlockchainRecord { pub self_id: Chain, pub num_chain_id: u64, - pub chain_family: ChainFamily, pub literal_identifier: &'static str, pub contract: Address, pub contract_creation_block: u64, @@ -172,26 +166,8 @@ mod tests { literal_identifier: "eth-mainnet", contract: ETH_MAINNET_CONTRACT_ADDRESS, contract_creation_block: ETH_MAINNET_CONTRACT_CREATION_BLOCK, - chain_family: ChainFamily::Eth } - ) - } - - #[test] - fn multinode_testnet_chain_record_is_properly_declared() { - let examined_chain = Chain::Dev; - let chain_record = return_examined(examined_chain); - assert_eq!( - chain_record, - &BlockchainRecord { - num_chain_id: 2, - self_id: examined_chain, - literal_identifier: "dev", - contract: MULTINODE_TESTNET_CONTRACT_ADDRESS, - contract_creation_block: 0, - chain_family: ChainFamily::Dev - } - ) + ); } #[test] @@ -206,9 +182,8 @@ mod tests { literal_identifier: "eth-ropsten", contract: ROPSTEN_TESTNET_CONTRACT_ADDRESS, contract_creation_block: ROPSTEN_TESTNET_CONTRACT_CREATION_BLOCK, - chain_family: ChainFamily::Eth } - ) + ); } #[test] @@ -223,9 +198,8 @@ mod tests { literal_identifier: "polygon-mainnet", contract: POLYGON_MAINNET_CONTRACT_ADDRESS, contract_creation_block: POLYGON_MAINNET_CONTRACT_CREATION_BLOCK, - chain_family: ChainFamily::Polygon } - ) + ); } #[test] @@ -240,9 +214,24 @@ mod tests { literal_identifier: "polygon-mumbai", contract: MUMBAI_TESTNET_CONTRACT_ADDRESS, contract_creation_block: MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK, - chain_family: ChainFamily::Polygon } - ) + ); + } + + #[test] + fn multinode_testnet_chain_record_is_properly_declared() { + let examined_chain = Chain::Dev; + let chain_record = return_examined(examined_chain); + assert_eq!( + chain_record, + &BlockchainRecord { + num_chain_id: 2, + self_id: examined_chain, + literal_identifier: "dev", + contract: MULTINODE_TESTNET_CONTRACT_ADDRESS, + contract_creation_block: 0, + } + ); } fn return_examined<'a>(chain: Chain) -> &'a BlockchainRecord { diff --git a/masq_lib/src/blockchains/chains.rs b/masq_lib/src/blockchains/chains.rs index be0d9f79f..ac3fbbac0 100644 --- a/masq_lib/src/blockchains/chains.rs +++ b/masq_lib/src/blockchains/chains.rs @@ -16,13 +16,6 @@ pub enum Chain { Dev, } -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] -pub enum ChainFamily { - Eth, - Polygon, - Dev, -} - impl Default for Chain { fn default() -> Self { DEFAULT_CHAIN @@ -143,7 +136,6 @@ mod tests { literal_identifier: "", contract: Default::default(), contract_creation_block: 0, - chain_family: ChainFamily::Polygon, } } diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index 4b7720c70..7bdcbe1e1 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -5,7 +5,7 @@ use crate::data_version::DataVersion; use const_format::concatcp; pub const DEFAULT_CHAIN: Chain = Chain::PolyMainnet; -pub const CURRENT_SCHEMA_VERSION: usize = 7; +pub const CURRENT_SCHEMA_VERSION: usize = 9; pub const HIGHEST_RANDOM_CLANDESTINE_PORT: u16 = 9999; pub const HTTP_PORT: u16 = 80; diff --git a/masq_lib/src/lib.rs b/masq_lib/src/lib.rs index db70ff4cf..e5232b221 100644 --- a/masq_lib/src/lib.rs +++ b/masq_lib/src/lib.rs @@ -22,5 +22,6 @@ pub mod crash_point; pub mod data_version; pub mod shared_schema; pub mod test_utils; +pub mod type_obfuscation; pub mod ui_gateway; pub mod ui_traffic_converter; diff --git a/masq_lib/src/logger.rs b/masq_lib/src/logger.rs index 455d0f574..b5fe282c8 100644 --- a/masq_lib/src/logger.rs +++ b/masq_lib/src/logger.rs @@ -1,10 +1,9 @@ use std::fmt::{Debug, Formatter}; // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::constants::CURRENT_SCHEMA_VERSION; use crate::constants::{ CLIENT_REQUEST_PAYLOAD_CURRENT_VERSION, CLIENT_RESPONSE_PAYLOAD_CURRENT_VERSION, - DNS_RESOLVER_FAILURE_CURRENT_VERSION, GOSSIP_CURRENT_VERSION, GOSSIP_FAILURE_CURRENT_VERSION, - NODE_RECORD_INNER_CURRENT_VERSION, + CURRENT_SCHEMA_VERSION, DNS_RESOLVER_FAILURE_CURRENT_VERSION, GOSSIP_CURRENT_VERSION, + GOSSIP_FAILURE_CURRENT_VERSION, NODE_RECORD_INNER_CURRENT_VERSION, }; use crate::data_version::DataVersion; use crate::messages::SerializableLogLevel; diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index c8a2f7079..59522171e 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -4,6 +4,7 @@ use crate::messages::UiMessageError::{DeserializationError, PayloadError, Unexpe use crate::shared_schema::ConfiguratorError; use crate::ui_gateway::MessageBody; use crate::ui_gateway::MessagePath::{Conversation, FireAndForget}; +use crate::utils::to_string; use itertools::Itertools; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; @@ -243,7 +244,7 @@ impl UiSetupRequest { .into_iter() .map(|(name, value)| UiSetupRequestValue { name: name.to_string(), - value: value.map(|v| v.to_string()), + value: value.map(to_string), }) .collect(), } @@ -484,6 +485,8 @@ pub struct UiConfigurationResponse { pub earning_wallet_address_opt: Option, #[serde(rename = "gasPrice")] pub gas_price: u64, + #[serde(rename = "maxBlockCount")] + pub max_block_count_opt: Option, #[serde(rename = "neighborhoodMode")] pub neighborhood_mode: String, #[serde(rename = "portMappingProtocol")] diff --git a/masq_lib/src/multi_config.rs b/masq_lib/src/multi_config.rs index 544a247a2..ffc9cc434 100644 --- a/masq_lib/src/multi_config.rs +++ b/masq_lib/src/multi_config.rs @@ -47,6 +47,7 @@ macro_rules! values_m { }}; } +#[derive(Debug)] pub struct MultiConfig<'a> { arg_matches: ArgMatches<'a>, } @@ -500,6 +501,7 @@ pub mod tests { use super::*; use crate::test_utils::environment_guard::EnvironmentGuard; use crate::test_utils::utils::ensure_node_home_directory_exists; + use crate::utils::to_string; use clap::Arg; use std::fs::File; use std::io::Write; @@ -928,7 +930,7 @@ pub mod tests { "--other_takes_no_value", ] .into_iter() - .map(|s| s.to_string()) + .map(to_string) .collect(); let subject = CommandLineVcl::new(command_line.clone()); @@ -951,10 +953,7 @@ pub mod tests { #[test] #[should_panic(expected = "Expected option beginning with '--', not value")] fn command_line_vcl_panics_when_given_value_without_name() { - let command_line: Vec = vec!["", "value"] - .into_iter() - .map(|s| s.to_string()) - .collect(); + let command_line: Vec = vec!["", "value"].into_iter().map(to_string).collect(); CommandLineVcl::new(command_line.clone()); } @@ -1126,6 +1125,7 @@ pub mod tests { } let result = ConfigFileVcl::new(&file_path, true).err().unwrap(); + assert_contains( &result.to_string(), "doesn't make sense: parameter 'array' must have a scalar value, not an array value.", diff --git a/masq_lib/src/shared_schema.rs b/masq_lib/src/shared_schema.rs index 97734cd91..00cee6b7c 100644 --- a/masq_lib/src/shared_schema.rs +++ b/masq_lib/src/shared_schema.rs @@ -1,3 +1,5 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + use crate::constants::{ DEFAULT_GAS_PRICE, DEFAULT_UI_PORT, DEV_CHAIN_FULL_IDENTIFIER, ETH_MAINNET_FULL_IDENTIFIER, ETH_ROPSTEN_FULL_IDENTIFIER, HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT, @@ -30,8 +32,9 @@ pub const CONSUMING_PRIVATE_KEY_HELP: &str = "The private key for the Ethereum w supply exactly the same private key every time you run the Node. A consuming private key is 64 case-insensitive \ hexadecimal digits."; pub const DATA_DIRECTORY_HELP: &str = - "Directory in which the Node will store its persistent state, including at \ - least its database and by default its configuration file as well."; + "Directory in which the Node will store its persistent state, including at least its database \ + and by default its configuration file as well.\nNote: any existing database in the data directory \ + must have been created from the same chain this run is using, or the Node will be terminated."; pub const DB_PASSWORD_HELP: &str = "A password or phrase to decrypt the encrypted material in the database, to include your \ mnemonic seed (if applicable) and your list of previous neighbors. If you don't provide this \ @@ -218,6 +221,16 @@ lazy_static! { // These Args are needed in more than one clap schema. To avoid code duplication, they're defined here and referred // to from multiple places. +pub fn chain_arg<'a>() -> Arg<'a, 'a> { + Arg::with_name("chain") + .long("chain") + .value_name("CHAIN") + .min_values(0) + .max_values(1) + .possible_values(official_chain_names()) + .help(CHAIN_HELP) +} + pub fn config_file_arg<'a>() -> Arg<'a, 'a> { Arg::with_name("config-file") .long("config-file") @@ -229,7 +242,7 @@ pub fn config_file_arg<'a>() -> Arg<'a, 'a> { .help(CONFIG_FILE_HELP) } -pub fn data_directory_arg<'a>() -> Arg<'a, 'a> { +pub fn data_directory_arg(help: &str) -> Arg { Arg::with_name("data-directory") .long("data-directory") .value_name("DATA-DIRECTORY") @@ -237,17 +250,7 @@ pub fn data_directory_arg<'a>() -> Arg<'a, 'a> { .min_values(0) .max_values(1) .empty_values(false) - .help(DATA_DIRECTORY_HELP) -} - -pub fn chain_arg<'a>() -> Arg<'a, 'a> { - Arg::with_name("chain") - .long("chain") - .value_name("CHAIN") - .min_values(0) - .max_values(1) - .possible_values(official_chain_names()) - .help(CHAIN_HELP) + .help(help) } pub fn official_chain_names() -> &'static [&'static str] { @@ -285,6 +288,26 @@ where .help(help) } +pub fn gas_price_arg<'a>() -> Arg<'a, 'a> { + Arg::with_name("gas-price") + .long("gas-price") + .value_name("GAS-PRICE") + .min_values(0) + .max_values(1) + .validator(common_validators::validate_gas_price) + .help(&GAS_PRICE_HELP) +} + +pub fn min_hops_arg<'a>() -> Arg<'a, 'a> { + Arg::with_name("min-hops") + .long("min-hops") + .value_name("MIN-HOPS") + .min_values(0) + .max_values(1) + .possible_values(&["1", "2", "3", "4", "5", "6"]) + .help(MIN_HOPS_HELP) +} + #[cfg(not(target_os = "windows"))] pub fn real_user_arg<'a>() -> Arg<'a, 'a> { Arg::with_name("real-user") @@ -366,7 +389,7 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> { .case_insensitive(true) .hidden(true), ) - .arg(data_directory_arg()) + .arg(data_directory_arg(DATA_DIRECTORY_HELP)) .arg(db_password_arg(DB_PASSWORD_HELP)) .arg( Arg::with_name("dns-servers") @@ -389,15 +412,7 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> { .max_values(1) .hidden(true), ) - .arg( - Arg::with_name("gas-price") - .long("gas-price") - .value_name("GAS-PRICE") - .min_values(0) - .max_values(1) - .validator(common_validators::validate_gas_price) - .help(&GAS_PRICE_HELP), - ) + .arg(gas_price_arg()) .arg( Arg::with_name("ip") .long("ip") @@ -427,17 +442,7 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> { .case_insensitive(true) .help(MAPPING_PROTOCOL_HELP), ) - .arg( - Arg::with_name("min-hops") - .long("min-hops") - .value_name("MIN_HOPS") - .default_value("3") - .required(false) - .min_values(0) - .max_values(1) - .possible_values(&["1", "2", "3", "4", "5", "6"]) - .help(MIN_HOPS_HELP), - ) + .arg(min_hops_arg()) .arg( Arg::with_name("neighborhood-mode") .long("neighborhood-mode") @@ -708,7 +713,9 @@ mod tests { assert_eq!( DATA_DIRECTORY_HELP, "Directory in which the Node will store its persistent state, including at \ - least its database and by default its configuration file as well." + least its database and by default its configuration file as well.\nNote: any existing \ + database in the data directory must have been created from the same chain this run is using, \ + or the Node will be terminated." ); assert_eq!( DB_PASSWORD_HELP, diff --git a/masq_lib/src/type_obfuscation.rs b/masq_lib/src/type_obfuscation.rs new file mode 100644 index 000000000..1f3c79258 --- /dev/null +++ b/masq_lib/src/type_obfuscation.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use std::any::TypeId; +use std::mem::transmute; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Obfuscated { + type_id: TypeId, + bytes: Vec, +} + +impl Obfuscated { + // Although we're asking the compiler for a cast between two types + // where one is generic and both could possibly be of a different + // size, which almost applies to an unsupported kind of operation, + // the compiler stays calm here. The use of vectors at the input as + // well as output lets us avoid the above depicted situation. + // + // If you wish to write an implementation allowing more arbitrary + // types on your own, instead of helping yourself by a library like + // 'bytemuck', consider these functions from the std library, + // 'mem::transmute_copy' or 'mem::forget()', which will renew + // the compiler's trust for you. However, the true adventure will + // begin when you are supposed to write code to realign the plain + // bytes backwards to your desired type... + + pub fn obfuscate_vector(data: Vec) -> Obfuscated { + let bytes = unsafe { transmute::, Vec>(data) }; + + Obfuscated { + type_id: TypeId::of::(), + bytes, + } + } + + pub fn expose_vector(self) -> Vec { + if self.type_id != TypeId::of::() { + panic!("Forbidden! You're trying to interpret obfuscated data as the wrong type.") + } + + unsafe { transmute::, Vec>(self.bytes) } + } + + // Proper casting from a non vec structure into a vector of bytes + // is difficult and ideally requires an involvement of a library + // like bytemuck. + // If you think we do need such cast, place other methods in here + // and don't remove the ones above because: + // a) bytemuck will force you to implement its 'Pod' trait which + // might imply an (at minimum) ugly implementation for a std + // type like a Vec because both the trait and the type have + // their definitions situated externally to our project, + // therefore you might need to solve it by introducing + // a super-trait from our code + // b) using our simple 'obfuscate_vector' function will always + // be fairly more efficient than if done with help of + // the other library +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn obfuscation_works() { + let data = vec!["I'm fearing of losing my entire identity".to_string()]; + + let obfuscated_data = Obfuscated::obfuscate_vector(data.clone()); + let fenix_like_data: Vec = obfuscated_data.expose_vector(); + + assert_eq!(data, fenix_like_data) + } + + #[test] + #[should_panic( + expected = "Forbidden! You're trying to interpret obfuscated data as the wrong type." + )] + fn obfuscation_attempt_to_reinterpret_to_wrong_type() { + let data = vec![0_u64]; + let obfuscated_data = Obfuscated::obfuscate_vector(data.clone()); + let _: Vec = obfuscated_data.expose_vector(); + } +} diff --git a/masq_lib/src/utils.rs b/masq_lib/src/utils.rs index cd036d637..24dc6d320 100644 --- a/masq_lib/src/utils.rs +++ b/masq_lib/src/utils.rs @@ -1,11 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::blockchains::chains::Chain; +use dirs::{data_local_dir, home_dir}; use lazy_static::lazy_static; use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::io; use std::io::ErrorKind; use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket}; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::{Arc, Mutex}; @@ -35,6 +38,40 @@ lazy_static! { static ref FIND_FREE_PORT_NEXT: Arc> = Arc::new(Mutex::new(FIND_FREE_PORT_LOWEST)); } +//data-directory help +lazy_static! { + pub static ref DATA_DIRECTORY_DAEMON_HELP: String = compute_data_directory_help(); +} + +fn compute_data_directory_help() -> String { + let data_dir = data_local_dir().unwrap(); + let home_dir = home_dir().unwrap(); + let polygon_mainnet_dir = Path::new(&data_dir.to_str().unwrap()) + .join("MASQ") + .join("polygon-mainnet"); + let polygon_mumbai_dir = Path::new(&data_dir.to_str().unwrap()) + .join("MASQ") + .join("polygon-mumbai"); + format!("Directory in which the Node will store its persistent state, including at least its database \ + and by default its configuration file as well. By default, your data-directory is located in \ + your application directory, under your home directory e.g.: '{}'.\n\n\ + In case you change your chain to a different one, the data-directory path is automatically changed \ + to end with the name of your chain: e.g.: if you choose polygon-mumbai, then data-directory is \ + automatically changed to: '{}'.\n\n\ + You can specify your own data-directory to the Daemon in two different ways: \n\n\ + 1. If you provide a path without the chain name on the end, the Daemon will automatically change \ + your data-directory to correspond with the chain. For example: {}/masq_home will be automatically \ + changed to: '{}/masq_home/polygon-mainnet'.\n\n\ + 2. If you provide your data directory with the corresponding chain name on the end, eg: {}/masq_home/polygon-mainnet, \ + there will be no change until you set the chain parameter to a different value.", + polygon_mainnet_dir.to_string_lossy().to_string().as_str(), + polygon_mumbai_dir.to_string_lossy().to_string().as_str(), + &home_dir.to_string_lossy().to_string().as_str(), + &home_dir.to_string_lossy().to_string().as_str(), + home_dir.to_string_lossy().to_string().as_str() + ) +} + #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum AutomapProtocol { Pmp, @@ -129,6 +166,18 @@ fn port_is_free_for_ip_addr(ip_addr: IpAddr, port: u16) -> bool { true } +pub fn add_masq_and_chain_directories(chain: Chain, local_data_dir: &Path) -> PathBuf { + let masq_dir = PathBuf::from(local_data_dir).join("MASQ"); + add_chain_specific_directory(chain, masq_dir.as_path()) +} + +pub fn add_chain_specific_directory(chain: Chain, local_data_dir: &Path) -> PathBuf { + match local_data_dir.ends_with(chain.rec().literal_identifier) { + true => PathBuf::from(local_data_dir), + false => PathBuf::from(local_data_dir).join(chain.rec().literal_identifier), + } +} + pub fn localhost() -> IpAddr { IpAddr::V4(Ipv4Addr::LOCALHOST) } @@ -272,10 +321,7 @@ pub fn exit_process_with_sigterm(message: &str) { } pub fn slice_of_strs_to_vec_of_strings(slice: &[&str]) -> Vec { - slice - .iter() - .map(|item| item.to_string()) - .collect::>() + slice.iter().map(to_string).collect::>() } pub trait ExpectValue { @@ -340,6 +386,14 @@ where fn helper_access(&mut self) -> &mut Option; } +// A handy function for closures +pub fn to_string(displayable: D) -> String +where + D: Display, +{ + displayable.to_string() +} + #[macro_export] macro_rules! short_writeln { ($dst:expr) => ( @@ -358,10 +412,10 @@ macro_rules! intentionally_blank { } #[macro_export] -macro_rules! declare_as_any { +macro_rules! as_any_in_trait { () => { #[cfg(test)] - fn as_any(&self) -> &dyn Any { + fn as_any(&self) -> &dyn std::any::Any { use masq_lib::intentionally_blank; intentionally_blank!() } @@ -369,18 +423,48 @@ macro_rules! declare_as_any { } #[macro_export] -macro_rules! implement_as_any { +macro_rules! as_any_in_trait_impl { () => { #[cfg(test)] - fn as_any(&self) -> &dyn Any { + fn as_any(&self) -> &dyn std::any::Any { self } }; } +#[macro_export] +macro_rules! test_only_use { + ($($use_clause: item),+) => { + $( + #[cfg(test)] + $use_clause + )+ + } +} + +#[macro_export(local_inner_macros)] +macro_rules! hashmap { + () => { + ::std::collections::HashMap::new() + }; + ($($key:expr => $val:expr,)+) => { + hashmap!($($key => $val),+) + }; + ($($key:expr => $value:expr),+) => { + { + let mut _hm = ::std::collections::HashMap::new(); + $( + let _ = _hm.insert($key, $value); + )* + _hm + } + }; +} + #[cfg(test)] mod tests { use super::*; + use std::collections::HashMap; use std::env::current_dir; use std::fmt::Write; use std::fs::{create_dir_all, File, OpenOptions}; @@ -702,4 +786,37 @@ mod tests { let result = type_name_of(running_test); assert_eq!(result, "masq_lib::utils::running_test") } + + #[test] + fn hashmap_macro_works() { + let empty_hashmap: HashMap = hashmap!(); + let hashmap_with_one_element = hashmap!(1 => 2); + let hashmap_with_multiple_elements = hashmap!(1 => 2, 10 => 20, 12 => 42); + let hashmap_with_trailing_comma = hashmap!(1 => 2, 10 => 20,); + let hashmap_of_string = hashmap!("key" => "val"); + + let expected_empty_hashmap: HashMap = HashMap::new(); + let mut expected_hashmap_with_one_element = HashMap::new(); + expected_hashmap_with_one_element.insert(1, 2); + let mut expected_hashmap_with_multiple_elements = HashMap::new(); + expected_hashmap_with_multiple_elements.insert(1, 2); + expected_hashmap_with_multiple_elements.insert(10, 20); + expected_hashmap_with_multiple_elements.insert(12, 42); + let mut expected_hashmap_with_trailing_comma = HashMap::new(); + expected_hashmap_with_trailing_comma.insert(1, 2); + expected_hashmap_with_trailing_comma.insert(10, 20); + let mut expected_hashmap_of_string = HashMap::new(); + expected_hashmap_of_string.insert("key", "val"); + assert_eq!(empty_hashmap, expected_empty_hashmap); + assert_eq!(hashmap_with_one_element, expected_hashmap_with_one_element); + assert_eq!( + hashmap_with_multiple_elements, + expected_hashmap_with_multiple_elements + ); + assert_eq!( + hashmap_with_trailing_comma, + expected_hashmap_with_trailing_comma + ); + assert_eq!(hashmap_of_string, expected_hashmap_of_string); + } } diff --git a/multinode_integration_tests/Cargo.toml b/multinode_integration_tests/Cargo.toml index c8d65b59f..cc6b9424a 100644 --- a/multinode_integration_tests/Cargo.toml +++ b/multinode_integration_tests/Cargo.toml @@ -3,7 +3,6 @@ name = "multinode_integration_tests" version = "0.7.3" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" -copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." description = "" edition = "2021" workspace = "../node" diff --git a/multinode_integration_tests/docker/macos/Dockerfile b/multinode_integration_tests/docker/macos/Dockerfile index dd9b3074c..a4a86f156 100644 --- a/multinode_integration_tests/docker/macos/Dockerfile +++ b/multinode_integration_tests/docker/macos/Dockerfile @@ -1,13 +1,13 @@ -FROM rust:stretch +FROM rust:bullseye ARG uid ARG gid -RUN (addgroup substratum --gid $gid || continue) \ - && adduser --disabled-password --uid $uid --gid $gid --home /home/substratum substratum \ - && chown -R $uid:$gid /home/substratum +RUN (addgroup masq --gid $gid || continue) \ + && adduser --disabled-password --uid $uid --gid $gid --home /home/masq masq \ + && chown -R $uid:$gid /home/masq RUN apt-get update && apt-get install -y sudo curl && rustup component add rustfmt clippy \ && cargo install sccache && chown -R $uid:$gid /usr/local/cargo /usr/local/rustup -USER substratum +USER masq diff --git a/multinode_integration_tests/src/masq_node.rs b/multinode_integration_tests/src/masq_node.rs index 02e6d7cea..5f293828c 100644 --- a/multinode_integration_tests/src/masq_node.rs +++ b/multinode_integration_tests/src/masq_node.rs @@ -6,6 +6,7 @@ use masq_lib::constants::{ CENTRAL_DELIMITER, CHAIN_IDENTIFIER_DELIMITER, CURRENT_LOGFILE_NAME, HIGHEST_USABLE_PORT, MASQ_URL_PREFIX, }; +use masq_lib::utils::to_string; use node_lib::sub_lib::cryptde::{CryptDE, PublicKey}; use node_lib::sub_lib::cryptde_null::CryptDENull; use node_lib::sub_lib::neighborhood::{NodeDescriptor, RatePack}; @@ -81,7 +82,7 @@ impl fmt::Display for NodeReference { Some(node_addr) => node_addr .ports() .iter() - .map(|port| port.to_string()) + .map(to_string) .collect::>() .join(NodeAddr::PORTS_SEPARATOR), None => String::new(), diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index dbc547e93..77b82054c 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -12,10 +12,10 @@ use log::Level; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::{CURRENT_LOGFILE_NAME, DEFAULT_UI_PORT}; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; -use masq_lib::utils::localhost; +use masq_lib::utils::{localhost, to_string}; use masq_lib::utils::{DEFAULT_CONSUMING_DERIVATION_PATH, DEFAULT_EARNING_DERIVATION_PATH}; -use node_lib::blockchain::bip32::Bip32ECKeyProvider; -use node_lib::neighborhood::DEFAULT_MIN_HOPS_COUNT; +use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; +use node_lib::neighborhood::DEFAULT_MIN_HOPS; use node_lib::sub_lib::accountant::{ PaymentThresholds, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, }; @@ -30,6 +30,7 @@ use std::fmt::Display; use std::net::IpAddr; use std::net::Ipv4Addr; use std::net::SocketAddr; +use std::path::Path; use std::rc::Rc; use std::str::FromStr; use std::string::ToString; @@ -114,7 +115,7 @@ pub fn make_consuming_wallet_info(token: &str) -> ConsumingWalletInfo { #[derive(PartialEq, Eq, Clone, Debug)] pub struct NodeStartupConfig { pub neighborhood_mode: String, - pub min_hops_count: Hops, + pub min_hops: Hops, pub ip_info: LocalIpInfo, pub dns_servers_opt: Option>, pub neighbors: Vec, @@ -146,7 +147,7 @@ impl NodeStartupConfig { pub fn new() -> NodeStartupConfig { NodeStartupConfig { neighborhood_mode: "standard".to_string(), - min_hops_count: DEFAULT_MIN_HOPS_COUNT, + min_hops: DEFAULT_MIN_HOPS, ip_info: LocalIpInfo::ZeroHop, dns_servers_opt: None, neighbors: Vec::new(), @@ -179,7 +180,7 @@ impl NodeStartupConfig { args.push("--neighborhood-mode".to_string()); args.push(self.neighborhood_mode.clone()); args.push("--min-hops".to_string()); - args.push(format!("{}", self.min_hops_count as usize)); + args.push(format!("{}", self.min_hops as usize)); if let LocalIpInfo::DistributedKnown(ip_addr) = self.ip_info { args.push("--ip".to_string()); args.push(ip_addr.to_string()); @@ -259,7 +260,7 @@ impl NodeStartupConfig { } fn slices_to_strings(strs: Vec<&str>) -> Vec { - strs.into_iter().map(|x| x.to_string()).collect() + strs.into_iter().map(to_string).collect() } fn make_establish_wallet_args(&self) -> Option> { @@ -376,7 +377,7 @@ impl NodeStartupConfig { EarningWalletInfo::Address(address) => Wallet::from_str(address).unwrap(), EarningWalletInfo::DerivationPath(phrase, derivation_path) => { let mnemonic = Mnemonic::from_phrase(phrase.as_str(), Language::English).unwrap(); - let keypair = Bip32ECKeyProvider::try_from(( + let keypair = Bip32EncryptionKeyProvider::try_from(( Seed::new(&mnemonic, "passphrase").as_ref(), derivation_path.as_str(), )) @@ -391,12 +392,12 @@ impl NodeStartupConfig { ConsumingWalletInfo::None => None, ConsumingWalletInfo::PrivateKey(key) => { let key_bytes = key.from_hex::>().unwrap(); - let keypair = Bip32ECKeyProvider::from_raw_secret(&key_bytes).unwrap(); + let keypair = Bip32EncryptionKeyProvider::from_raw_secret(&key_bytes).unwrap(); Some(Wallet::from(keypair)) } ConsumingWalletInfo::DerivationPath(phrase, derivation_path) => { let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); - let keypair = Bip32ECKeyProvider::try_from(( + let keypair = Bip32EncryptionKeyProvider::try_from(( Seed::new(&mnemonic, "passphrase").as_ref(), derivation_path.as_str(), )) @@ -409,7 +410,7 @@ impl NodeStartupConfig { pub struct NodeStartupConfigBuilder { neighborhood_mode: String, - min_hops_count: Hops, + min_hops: Hops, ip_info: LocalIpInfo, dns_servers_opt: Option>, neighbors: Vec, @@ -465,7 +466,7 @@ impl NodeStartupConfigBuilder { pub fn standard() -> Self { Self { neighborhood_mode: "standard".to_string(), - min_hops_count: DEFAULT_MIN_HOPS_COUNT, + min_hops: DEFAULT_MIN_HOPS, ip_info: LocalIpInfo::DistributedUnknown, dns_servers_opt: None, neighbors: vec![], @@ -491,7 +492,7 @@ impl NodeStartupConfigBuilder { pub fn copy(config: &NodeStartupConfig) -> Self { Self { neighborhood_mode: config.neighborhood_mode.clone(), - min_hops_count: config.min_hops_count, + min_hops: config.min_hops, ip_info: config.ip_info, dns_servers_opt: config.dns_servers_opt.clone(), neighbors: config.neighbors.clone(), @@ -530,8 +531,8 @@ impl NodeStartupConfigBuilder { } } - pub fn min_hops_count(mut self, value: Hops) -> Self { - self.min_hops_count = value; + pub fn min_hops(mut self, value: Hops) -> Self { + self.min_hops = value; self } @@ -641,14 +642,14 @@ impl NodeStartupConfigBuilder { } pub fn db_password(mut self, value: Option<&str>) -> Self { - self.db_password = value.map(|str| str.to_string()); + self.db_password = value.map(to_string); self } pub fn build(self) -> NodeStartupConfig { NodeStartupConfig { neighborhood_mode: self.neighborhood_mode, - min_hops_count: self.min_hops_count, + min_hops: self.min_hops, ip_info: self.ip_info, dns_servers_opt: self.dns_servers_opt, neighbors: self.neighbors, @@ -1166,6 +1167,7 @@ impl MASQRealNode { fn extract_node_reference(name: &str) -> Result { let descriptor_regex = Self::descriptor_regex(); + let log_file_path = Path::new(DATA_DIRECTORY).join(CURRENT_LOGFILE_NAME); let mut retries_left = 25; loop { if retries_left <= 0 { @@ -1176,10 +1178,7 @@ impl MASQRealNode { thread::sleep(Duration::from_millis(250)); match Self::exec_command_on_container_and_wait( name, - vec![ - "cat", - &format!("{}/{}", DATA_DIRECTORY, CURRENT_LOGFILE_NAME), - ], + vec!["cat", &log_file_path.to_string_lossy()], ) { Ok(output) => { if let Some(captures) = descriptor_regex.captures(output.as_str()) { @@ -1196,8 +1195,10 @@ impl MASQRealNode { } Err(e) => { println!( - "Failed to cat logfile for {} at {}/{}: {}", - name, DATA_DIRECTORY, CURRENT_LOGFILE_NAME, e + "Failed to cat logfile for {} at {}: {}", + name, + &log_file_path.to_string_lossy(), + e ); } }; @@ -1304,7 +1305,7 @@ mod tests { #[test] fn node_startup_config_builder_settings() { - let min_hops_count = Hops::SixHops; + let min_hops = Hops::SixHops; let ip_addr = IpAddr::from_str("1.2.3.4").unwrap(); let one_neighbor_key = PublicKey::new(&[1, 2, 3, 4]); let one_neighbor_ip_addr = IpAddr::from_str("4.5.6.7").unwrap(); @@ -1333,7 +1334,7 @@ mod tests { let dns_target = IpAddr::from_str("8.9.10.11").unwrap(); let result = NodeStartupConfigBuilder::standard() - .min_hops_count(min_hops_count) + .min_hops(min_hops) .ip(ip_addr) .dns_servers(dns_servers.clone()) .neighbor(neighbors[0].clone()) @@ -1342,7 +1343,7 @@ mod tests { .dns_port(35) .build(); - assert_eq!(result.min_hops_count, min_hops_count); + assert_eq!(result.min_hops, min_hops); assert_eq!(result.ip_info, LocalIpInfo::DistributedKnown(ip_addr)); assert_eq!(result.dns_servers_opt, Some(dns_servers)); assert_eq!(result.neighbors, neighbors); @@ -1355,7 +1356,7 @@ mod tests { fn node_startup_config_builder_copy() { let original = NodeStartupConfig { neighborhood_mode: "consume-only".to_string(), - min_hops_count: Hops::TwoHops, + min_hops: Hops::TwoHops, ip_info: LocalIpInfo::DistributedUnknown, dns_servers_opt: Some(vec![IpAddr::from_str("255.255.255.255").unwrap()]), neighbors: vec![NodeReference::new( @@ -1434,7 +1435,7 @@ mod tests { .build(); assert_eq!(result.neighborhood_mode, neighborhood_mode); - assert_eq!(result.min_hops_count, Hops::TwoHops); + assert_eq!(result.min_hops, Hops::TwoHops); assert_eq!(result.ip_info, LocalIpInfo::DistributedKnown(ip_addr)); assert_eq!(result.dns_servers_opt, Some(dns_servers)); assert_eq!(result.neighbors, neighbors); @@ -1501,7 +1502,7 @@ mod tests { let subject = NodeStartupConfigBuilder::standard() .neighborhood_mode("consume-only") - .min_hops_count(Hops::SixHops) + .min_hops(Hops::SixHops) .ip(IpAddr::from_str("1.3.5.7").unwrap()) .neighbor(one_neighbor.clone()) .neighbor(another_neighbor.clone()) diff --git a/multinode_integration_tests/src/utils.rs b/multinode_integration_tests/src/utils.rs index 5a42c3ebf..47f452881 100644 --- a/multinode_integration_tests/src/utils.rs +++ b/multinode_integration_tests/src/utils.rs @@ -5,10 +5,8 @@ use crate::masq_node::{MASQNode, MASQNodeUtils}; use crate::masq_real_node::MASQRealNode; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::NeighborhoodModeLight; -use node_lib::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoReal}; -use node_lib::accountant::database_access_objects::receivable_dao::{ - ReceivableDao, ReceivableDaoReal, -}; +use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; +use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; use node_lib::database::connection_wrapper::ConnectionWrapper; use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, @@ -72,10 +70,7 @@ pub fn wait_for_chunk(stream: &mut TcpStream, timeout: &Duration) -> Result Box { let db_initializer = DbInitializerReal::default(); - let path = std::path::PathBuf::from(MASQRealNode::node_home_dir( - &MASQNodeUtils::find_project_root(), - node_name, - )); + let path = std::path::PathBuf::from(node_chain_specific_data_directory(node_name)); db_initializer .initialize( &path, @@ -88,6 +83,10 @@ pub fn database_conn(node_name: &str) -> Box { .unwrap() } +pub fn node_chain_specific_data_directory(node_name: &str) -> String { + MASQRealNode::node_home_dir(&MASQNodeUtils::find_project_root(), node_name) +} + pub fn config_dao(node_name: &str) -> Box { Box::new(ConfigDaoReal::new(database_conn(node_name))) } diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 346c6a684..1eb6a3ca7 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -15,13 +15,14 @@ use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node::MASQNodeUtils; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, MASQRealNode, NodeStartupConfigBuilder, + ConsumingWalletInfo, NodeStartupConfigBuilder, }; use multinode_integration_tests_lib::mock_blockchain_client_server::MBCSBuilder; use multinode_integration_tests_lib::utils::{ - config_dao, open_all_file_permissions, receivable_dao, UrlHolder, + config_dao, node_chain_specific_data_directory, open_all_file_permissions, receivable_dao, + UrlHolder, }; -use node_lib::accountant::database_access_objects::utils::CustomQuery; +use node_lib::accountant::db_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; #[test] @@ -71,9 +72,8 @@ fn debtors_are_credited_once_but_not_twice() { .ui_port(ui_port) .build(); let (node_name, node_index) = cluster.prepare_real_node(&node_config); - let node_home_dir = - MASQRealNode::node_home_dir(&MASQNodeUtils::find_project_root(), &node_name); - open_all_file_permissions(PathBuf::from(node_home_dir)); + let chain_specific_dir = node_chain_specific_data_directory(&node_name); + open_all_file_permissions(PathBuf::from(chain_specific_dir)); { let config_dao = config_dao(&node_name); config_dao diff --git a/multinode_integration_tests/tests/bookkeeping_test.rs b/multinode_integration_tests/tests/bookkeeping_test.rs index cb570a8ae..41642a724 100644 --- a/multinode_integration_tests/tests/bookkeeping_test.rs +++ b/multinode_integration_tests/tests/bookkeeping_test.rs @@ -6,9 +6,9 @@ use multinode_integration_tests_lib::masq_real_node::{ STANDARD_CLIENT_TIMEOUT_MILLIS, }; use multinode_integration_tests_lib::utils::{payable_dao, receivable_dao}; -use node_lib::accountant::database_access_objects::payable_dao::PayableAccount; -use node_lib::accountant::database_access_objects::receivable_dao::ReceivableAccount; -use node_lib::accountant::database_access_objects::utils::CustomQuery; +use node_lib::accountant::db_access_objects::payable_dao::PayableAccount; +use node_lib::accountant::db_access_objects::receivable_dao::ReceivableAccount; +use node_lib::accountant::db_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; use std::collections::HashMap; use std::thread; diff --git a/multinode_integration_tests/tests/data_routing_test.rs b/multinode_integration_tests/tests/data_routing_test.rs index 9b0e5a979..0b3fa9d21 100644 --- a/multinode_integration_tests/tests/data_routing_test.rs +++ b/multinode_integration_tests/tests/data_routing_test.rs @@ -13,7 +13,6 @@ use native_tls::TlsConnector; use native_tls::TlsStream; use node_lib::proxy_server::protocol_pack::ServerImpersonator; use node_lib::proxy_server::server_impersonator_http::ServerImpersonatorHttp; -use node_lib::sub_lib::neighborhood::Hops; use node_lib::test_utils::{handle_connection_error, read_until_timeout}; use std::io::Write; use std::net::{IpAddr, SocketAddr, TcpStream}; @@ -58,7 +57,7 @@ fn http_end_to_end_routing_test() { thread::sleep(Duration::from_millis(500)); - let mut client = last_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); + let mut client = last_node.make_client(8080, 5000); client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); let response = client.wait_for_chunk(); @@ -70,50 +69,6 @@ fn http_end_to_end_routing_test() { ); } -fn assert_http_end_to_end_routing_test(min_hops_count: Hops) { - let mut cluster = MASQNodeCluster::start().unwrap(); - let config = NodeStartupConfigBuilder::standard() - .min_hops_count(min_hops_count) - .chain(cluster.chain) - .consuming_wallet_info(make_consuming_wallet_info("first_node")) - .build(); - let first_node = cluster.start_real_node(config); - - let nodes_count = 2 * (min_hops_count as usize) + 1; - let nodes = (0..nodes_count) - .map(|_| { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .neighbor(first_node.node_reference()) - .chain(cluster.chain) - .build(), - ) - }) - .collect::>(); - - thread::sleep(Duration::from_millis(500 * (nodes.len() as u64))); - - let mut client = first_node.make_client(8080, 5000); - client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); - let response = client.wait_for_chunk(); - - assert_eq!( - index_of(&response, &b"

Example Domain

"[..]).is_some(), - true, - "Actual response:\n{}", - String::from_utf8(response).unwrap() - ); -} - -#[test] -fn http_end_to_end_routing_test_with_different_min_hops_count() { - // This test fails sometimes due to a timeout: "Couldn't read chunk: Kind(TimedOut)" - // You may fix it by increasing the timeout for the client. - assert_http_end_to_end_routing_test(Hops::OneHop); - assert_http_end_to_end_routing_test(Hops::TwoHops); - assert_http_end_to_end_routing_test(Hops::SixHops); -} - #[test] fn http_end_to_end_routing_test_with_consume_and_originate_only_nodes() { let mut cluster = MASQNodeCluster::start().unwrap(); diff --git a/multinode_integration_tests/tests/min_hops_tests.rs b/multinode_integration_tests/tests/min_hops_tests.rs new file mode 100644 index 000000000..ed0ef34f4 --- /dev/null +++ b/multinode_integration_tests/tests/min_hops_tests.rs @@ -0,0 +1,123 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use masq_lib::messages::{ToMessageBody, UiSetConfigurationRequest}; +use masq_lib::utils::{find_free_port, index_of}; +use multinode_integration_tests_lib::masq_node::MASQNode; +use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; +use multinode_integration_tests_lib::masq_real_node::{ + make_consuming_wallet_info, MASQRealNode, NodeStartupConfigBuilder, +}; +use node_lib::sub_lib::neighborhood::Hops; +use std::thread; +use std::time::Duration; + +#[test] +fn data_can_be_routed_using_different_min_hops() { + // This test fails sometimes due to a timeout: "Couldn't read chunk: Kind(TimedOut)" + // You may fix it by increasing the timeout for the client. + assert_http_end_to_end_routing(Hops::OneHop); + assert_http_end_to_end_routing(Hops::TwoHops); + assert_http_end_to_end_routing(Hops::SixHops); +} + +fn assert_http_end_to_end_routing(min_hops: Hops) { + let mut cluster = MASQNodeCluster::start().unwrap(); + let config = NodeStartupConfigBuilder::standard() + .min_hops(min_hops) + .chain(cluster.chain) + .consuming_wallet_info(make_consuming_wallet_info("first_node")) + .build(); + let first_node = cluster.start_real_node(config); + + // For 1-hop route, 3 nodes are necessary if we use last node as the originating node + let nodes_count = (min_hops as usize) + 2; + let nodes = (0..nodes_count) + .map(|i| { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .neighbor(first_node.node_reference()) + .consuming_wallet_info(make_consuming_wallet_info(&format!("node_{i}"))) + .chain(cluster.chain) + .build(), + ) + }) + .collect::>(); + + thread::sleep(Duration::from_millis(500 * (nodes.len() as u64))); + + let last_node = nodes.last().unwrap(); + let mut client = last_node.make_client(8080, 5000); + client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); + let response = client.wait_for_chunk(); + + assert_eq!( + index_of(&response, &b"

Example Domain

"[..]).is_some(), + true, + "Actual response:\n{}", + String::from_utf8(response).unwrap() + ); +} + +#[test] +fn min_hops_can_be_changed_during_runtime() { + let initial_min_hops = Hops::OneHop; + let new_min_hops = Hops::TwoHops; + let mut cluster = MASQNodeCluster::start().unwrap(); + let ui_port = find_free_port(); + let first_node_config = NodeStartupConfigBuilder::standard() + .min_hops(initial_min_hops) + .chain(cluster.chain) + .consuming_wallet_info(make_consuming_wallet_info("first_node")) + .ui_port(ui_port) + .build(); + let first_node = cluster.start_real_node(first_node_config); + let ui_client = first_node.make_ui(ui_port); + + for _ in 0..initial_min_hops as u8 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .neighbor(first_node.node_reference()) + .chain(cluster.chain) + .build(), + ); + } + thread::sleep(Duration::from_millis(1000)); + + let mut client = first_node.make_client(8080, 5000); + client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); + let response = client.wait_for_chunk(); + + // Client shutdown is necessary to re-initialize stream keys for old requests + client.shutdown(); + + assert_eq!( + index_of(&response, &b"

Example Domain

"[..]).is_some(), + true, + "Actual response:\n{}", + String::from_utf8(response).unwrap() + ); + + ui_client.send_request( + UiSetConfigurationRequest { + name: "min-hops".to_string(), + value: new_min_hops.to_string(), + } + .tmb(1), + ); + let response = ui_client.wait_for_response(1, Duration::from_secs(2)); + assert!(response.payload.is_ok()); + + let mut client = first_node.make_client(8080, 5000); + client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); + let response = client.wait_for_chunk(); + assert_eq!( + index_of( + &response, + &b"

Subtitle: Can't find a route to www.example.com

"[..] + ) + .is_some(), + true, + "Actual response:\n{}", + String::from_utf8(response).unwrap() + ); +} diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 24a9c6197..5e9b50347 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -5,21 +5,21 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::constants::WEIS_IN_GWEI; use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; -use multinode_integration_tests_lib::masq_node::{MASQNode, MASQNodeUtils}; +use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeStartupConfig, - NodeStartupConfigBuilder, + ConsumingWalletInfo, EarningWalletInfo, NodeStartupConfig, NodeStartupConfigBuilder, }; -use multinode_integration_tests_lib::utils::{open_all_file_permissions, UrlHolder}; -use node_lib::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoReal}; -use node_lib::accountant::database_access_objects::receivable_dao::{ - ReceivableDao, ReceivableDaoReal, +use multinode_integration_tests_lib::utils::{ + node_chain_specific_data_directory, open_all_file_permissions, UrlHolder, }; -use node_lib::blockchain::bip32::Bip32ECKeyProvider; -use node_lib::blockchain::blockchain_interface::{ - BlockchainInterface, BlockchainInterfaceNonClandestine, REQUESTS_IN_PARALLEL, +use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; +use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; +use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; +use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ + BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, }; +use node_lib::blockchain::blockchain_interface::BlockchainInterface; use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, }; @@ -48,7 +48,7 @@ fn verify_bill_payment() { blockchain_server.start(); blockchain_server.wait_until_ready(); let url = blockchain_server.url().to_string(); - let (_event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); + let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); let web3 = Web3::new(http.clone()); let deriv_path = derivation_path(0, 0); let seed = make_seed(); @@ -60,8 +60,7 @@ fn verify_bill_payment() { "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", contract_addr ); - let blockchain_interface = - BlockchainInterfaceNonClandestine::new(http, _event_loop_handle, cluster.chain); + let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); assert_balances( &contract_owner_wallet, &blockchain_interface, @@ -100,9 +99,8 @@ fn verify_bill_payment() { let amount = 10 * payment_thresholds.permanent_debt_allowed_gwei as u128 * WEIS_IN_GWEI as u128; - let project_root = MASQNodeUtils::find_project_root(); let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); - let consuming_node_path = MASQRealNode::node_home_dir(&project_root, &consuming_node_name); + let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); let consuming_node_connection = DbInitializerReal::default() .initialize( Path::new(&consuming_node_path), @@ -140,7 +138,7 @@ fn verify_bill_payment() { let (serving_node_1_name, serving_node_1_index) = cluster.prepare_real_node(&serving_node_1_config); - let serving_node_1_path = MASQRealNode::node_home_dir(&project_root, &serving_node_1_name); + let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); let serving_node_1_connection = DbInitializerReal::default() .initialize( Path::new(&serving_node_1_path), @@ -155,7 +153,7 @@ fn verify_bill_payment() { let (serving_node_2_name, serving_node_2_index) = cluster.prepare_real_node(&serving_node_2_config); - let serving_node_2_path = MASQRealNode::node_home_dir(&project_root, &serving_node_2_name); + let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); let serving_node_2_connection = DbInitializerReal::default() .initialize( Path::new(&serving_node_2_path), @@ -170,7 +168,7 @@ fn verify_bill_payment() { let (serving_node_3_name, serving_node_3_index) = cluster.prepare_real_node(&serving_node_3_config); - let serving_node_3_path = MASQRealNode::node_home_dir(&project_root, &serving_node_3_name); + let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); let serving_node_3_connection = DbInitializerReal::default() .initialize( Path::new(&serving_node_3_path), @@ -321,12 +319,13 @@ fn make_init_config(chain: Chain) -> DbInitializationConfig { fn assert_balances( wallet: &Wallet, - blockchain_interface: &BlockchainInterfaceNonClandestine, + blockchain_interface: &BlockchainInterfaceWeb3, expected_eth_balance: &str, expected_token_balance: &str, ) { let eth_balance = blockchain_interface - .get_gas_balance(&wallet) + .lower_interface() + .get_transaction_fee_balance(&wallet) .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); assert_eq!( format!("{}", eth_balance), @@ -336,8 +335,9 @@ fn assert_balances( expected_eth_balance ); let token_balance = blockchain_interface - .get_token_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve token balance for {}", wallet)); + .lower_interface() + .get_service_fee_balance(&wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); assert_eq!( token_balance, web3::types::U256::from_dec_str(expected_token_balance).unwrap(), @@ -393,7 +393,7 @@ fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { let secret = extended_priv_key.secret().to_hex::(); ( - Wallet::from(Bip32ECKeyProvider::from_key(extended_priv_key)), + Wallet::from(Bip32EncryptionKeyProvider::from_key(extended_priv_key)), secret, ) } diff --git a/node/Cargo.lock b/node/Cargo.lock index 483020ce0..acfbd14ba 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -16,7 +16,7 @@ dependencies = [ "fnv", "futures", "libc", - "log 0.4.14", + "log 0.4.18", "parking_lot 0.7.1", "smallvec 0.6.13", "tokio 0.1.22", @@ -152,7 +152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" dependencies = [ "http 0.2.5", - "log 0.4.14", + "log 0.4.18", "url 2.2.2", "wildmatch", ] @@ -189,7 +189,7 @@ dependencies = [ "igd", "lazy_static", "local_ipaddress", - "log 0.4.14", + "log 0.4.18", "masq_lib", "port_scanner", "pretty-hex 0.1.1", @@ -319,12 +319,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "boxfnonce" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5988cb1d626264ac94100be357308f29ff7cbdd3b36bda27f450a4ee3f713426" - [[package]] name = "bumpalo" version = "3.8.0" @@ -436,8 +430,8 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "unicode-xid 0.2.1", ] @@ -660,16 +654,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "daemonize" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70c24513e34f53b640819f0ac9f705b673fcf4006d7aab8778bee72ebfc89815" -dependencies = [ - "boxfnonce", - "libc", -] - [[package]] name = "data-encoding" version = "2.3.0" @@ -705,8 +689,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" dependencies = [ "convert_case", - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "rustc_version 0.3.3", "syn 1.0.85", ] @@ -896,8 +880,8 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", "synstructure", ] @@ -976,7 +960,7 @@ dependencies = [ "flate2", "glob", "lazy_static", - "log 0.4.14", + "log 0.4.18", "regex", "thiserror", "yansi", @@ -992,7 +976,7 @@ dependencies = [ "chrono", "glob", "lazy_static", - "log 0.4.14", + "log 0.4.18", "regex", "thiserror", "yansi", @@ -1172,7 +1156,7 @@ dependencies = [ "futures", "http 0.1.21", "indexmap", - "log 0.4.14", + "log 0.4.18", "slab 0.4.2", "string", "tokio-io", @@ -1203,12 +1187,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - [[package]] name = "hashbrown" version = "0.12.3" @@ -1224,7 +1202,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown 0.12.3", + "hashbrown", ] [[package]] @@ -1395,7 +1373,7 @@ dependencies = [ "httparse", "iovec", "itoa 0.4.6", - "log 0.4.14", + "log 0.4.18", "net2", "rustc_version 0.2.3", "time 0.1.44", @@ -1489,7 +1467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4e7ee8b51e541486d7040883fe1f00e2a9954bcc24fd155b7e4f03ed4b93dd" dependencies = [ "attohttpc", - "log 0.4.14", + "log 0.4.18", "rand 0.8.4", "url 2.2.2", "xmltree", @@ -1533,12 +1511,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.0.1", - "hashbrown 0.9.1", + "hashbrown", ] [[package]] @@ -1636,7 +1614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" dependencies = [ "futures", - "log 0.4.14", + "log 0.4.18", "serde", "serde_derive", "serde_json", @@ -1666,9 +1644,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" [[package]] name = "libsecp256k1" @@ -1804,17 +1782,14 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.14", + "log 0.4.18", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "lru-cache" @@ -1853,10 +1828,11 @@ dependencies = [ "clap", "const_format", "crossbeam-channel 0.5.1", + "dirs 4.0.0", "ethereum-types", "itertools 0.10.3", "lazy_static", - "log 0.4.14", + "log 0.4.18", "nix 0.23.1", "regex", "serde", @@ -1953,7 +1929,7 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log 0.4.14", + "log 0.4.18", "miow 0.2.1", "net2", "slab 0.4.2", @@ -1967,7 +1943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", - "log 0.4.14", + "log 0.4.18", "miow 0.3.7", "ntapi", "winapi 0.3.9", @@ -2032,7 +2008,7 @@ dependencies = [ "futures", "itertools 0.10.3", "lazy_static", - "log 0.4.14", + "log 0.4.18", "masq_lib", "native-tls", "node", @@ -2062,7 +2038,7 @@ checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" dependencies = [ "lazy_static", "libc", - "log 0.4.14", + "log 0.4.18", "openssl", "openssl-probe", "openssl-sys", @@ -2127,7 +2103,6 @@ dependencies = [ "clap", "core-foundation 0.7.0", "crossbeam-channel 0.5.1", - "daemonize", "dirs 4.0.0", "ethabi", "ethereum-types", @@ -2145,7 +2120,7 @@ dependencies = [ "lazy_static", "libc", "libsecp256k1 0.7.0", - "log 0.4.14", + "log 0.4.18", "masq_lib", "native-tls", "nix 0.23.1", @@ -2182,6 +2157,7 @@ dependencies = [ "trust-dns-proto 0.8.0", "trust-dns-resolver 0.12.0", "unindent", + "variant_count", "web3", "websocket", "winreg 0.10.1", @@ -2309,9 +2285,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.9.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -2562,8 +2538,8 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95af56fee93df76d721d356ac1ca41fccf168bc448eb14049234df764ba3e76" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", ] @@ -2652,11 +2628,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ - "unicode-xid 0.2.1", + "unicode-ident", ] [[package]] @@ -2676,11 +2652,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.14" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.59", ] [[package]] @@ -3023,7 +2999,7 @@ dependencies = [ "ipnet", "js-sys", "lazy_static", - "log 0.4.14", + "log 0.4.18", "mime 0.3.16", "native-tls", "percent-encoding 2.1.0", @@ -3097,9 +3073,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.17" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -3292,8 +3268,8 @@ version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", ] @@ -3337,8 +3313,8 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", ] @@ -3555,8 +3531,8 @@ version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "unicode-xid 0.2.1", ] @@ -3566,8 +3542,8 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", "unicode-xid 0.2.1", ] @@ -3659,8 +3635,8 @@ version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", ] @@ -3824,7 +3800,7 @@ dependencies = [ "bytes 0.4.12", "futures", "iovec", - "log 0.4.14", + "log 0.4.18", "mio 0.6.22", "scoped-tls", "tokio 0.1.22", @@ -3873,7 +3849,7 @@ checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", "futures", - "log 0.4.14", + "log 0.4.18", ] [[package]] @@ -3895,7 +3871,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures", "lazy_static", - "log 0.4.14", + "log 0.4.18", "mio 0.6.22", "num_cpus", "parking_lot 0.9.0", @@ -3957,7 +3933,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures", "lazy_static", - "log 0.4.14", + "log 0.4.18", "num_cpus", "slab 0.4.2", "tokio-executor", @@ -3993,7 +3969,7 @@ checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes 0.4.12", "futures", - "log 0.4.14", + "log 0.4.18", "mio 0.6.22", "tokio-codec", "tokio-io", @@ -4010,7 +3986,7 @@ dependencies = [ "futures", "iovec", "libc", - "log 0.4.14", + "log 0.4.18", "mio 0.6.22", "mio-uds", "tokio-codec", @@ -4027,7 +4003,7 @@ dependencies = [ "bytes 1.1.0", "futures-core", "futures-sink", - "log 0.4.14", + "log 0.4.18", "pin-project-lite", "tokio 1.15.0", ] @@ -4085,7 +4061,7 @@ dependencies = [ "failure", "futures", "lazy_static", - "log 0.4.14", + "log 0.4.18", "radix_trie", "rand 0.7.3", "tokio 0.1.22", @@ -4105,7 +4081,7 @@ dependencies = [ "futures", "idna 0.1.5", "lazy_static", - "log 0.4.14", + "log 0.4.18", "rand 0.5.6", "smallvec 0.6.13", "socket2 0.3.15", @@ -4129,7 +4105,7 @@ dependencies = [ "futures", "idna 0.1.5", "lazy_static", - "log 0.4.14", + "log 0.4.18", "rand 0.5.6", "smallvec 0.6.13", "socket2 0.3.15", @@ -4154,7 +4130,7 @@ dependencies = [ "futures", "idna 0.2.0", "lazy_static", - "log 0.4.14", + "log 0.4.18", "rand 0.7.3", "smallvec 0.6.13", "socket2 0.3.15", @@ -4178,7 +4154,7 @@ dependencies = [ "futures", "ipconfig 0.1.9", "lazy_static", - "log 0.4.14", + "log 0.4.18", "lru-cache", "resolv-conf", "smallvec 0.6.13", @@ -4197,7 +4173,7 @@ dependencies = [ "futures", "ipconfig 0.2.2", "lazy_static", - "log 0.4.14", + "log 0.4.18", "lru-cache", "resolv-conf", "smallvec 0.6.13", @@ -4274,6 +4250,12 @@ dependencies = [ "matches", ] +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + [[package]] name = "unicode-normalization" version = "0.1.13" @@ -4345,6 +4327,16 @@ dependencies = [ "rand 0.6.5", ] +[[package]] +name = "variant_count" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" +dependencies = [ + "quote 1.0.28", + "syn 1.0.85", +] + [[package]] name = "vcpkg" version = "0.2.10" @@ -4376,7 +4368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ "futures", - "log 0.4.14", + "log 0.4.18", "try-lock", ] @@ -4386,7 +4378,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.14", + "log 0.4.18", "try-lock", ] @@ -4420,9 +4412,9 @@ checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.14", - "proc-macro2 1.0.36", - "quote 1.0.14", + "log 0.4.18", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", "wasm-bindgen-shared", ] @@ -4445,7 +4437,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ - "quote 1.0.14", + "quote 1.0.28", "wasm-bindgen-macro-support", ] @@ -4455,8 +4447,8 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -4493,7 +4485,7 @@ dependencies = [ "hyper 0.12.35", "hyper-tls 0.3.2", "jsonrpc-core", - "log 0.4.14", + "log 0.4.18", "native-tls", "parking_lot 0.10.2", "rlp", @@ -4736,8 +4728,8 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.14", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.85", "synstructure", ] diff --git a/node/Cargo.toml b/node/Cargo.toml index 39fe9e52f..aac0d3b57 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -3,12 +3,10 @@ name = "node" version = "0.7.3" license = "GPL-3.0-only" authors = ["Dan Wiebe ", "MASQ"] -copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." description = "MASQ Node is the foundation of MASQ Network, an open-source network that allows anyone to allocate spare computing resources to make the internet a free and fair place for the entire world." edition = "2021" [workspace] -#members = ["../masq_lib"] members = ["../multinode_integration_tests", "../masq_lib", "../masq"] [dependencies] @@ -62,6 +60,7 @@ toml = "0.5.8" trust-dns = "0.17.0" trust-dns-resolver = "0.12.0" unindent = "0.1.7" +variant_count = "1.1.0" web3 = {version = "0.11.0", default-features = false, features = ["http", "tls"]} websocket = {version = "0.26.2", default-features = false, features = ["async", "sync"]} secp256k1secrets = {package = "secp256k1", version = "0.17.2"} @@ -71,7 +70,6 @@ system-configuration = "0.4.0" core-foundation = "0.7.0" [target.'cfg(not(target_os = "windows"))'.dependencies] -daemonize = "0.4.1" nix = "0.23.0" openssl = {version = "0.10.38", features = ["vendored"]} @@ -100,8 +98,6 @@ path = "src/main_win.rs" name = "node_lib" path = "src/lib.rs" -cargo-bundle = "0.4.0" - [features] expose_test_privates = [] diff --git a/node/src/accountant/database_access_objects/banned_dao.rs b/node/src/accountant/db_access_objects/banned_dao.rs similarity index 99% rename from node/src/accountant/database_access_objects/banned_dao.rs rename to node/src/accountant/db_access_objects/banned_dao.rs index 0644699c6..24b3ced81 100644 --- a/node/src/accountant/database_access_objects/banned_dao.rs +++ b/node/src/accountant/db_access_objects/banned_dao.rs @@ -1,5 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::utils::{DaoFactoryReal, VigilantRusqliteFlatten}; + +use crate::accountant::db_access_objects::utils::{DaoFactoryReal, VigilantRusqliteFlatten}; use crate::database::connection_wrapper::ConnectionWrapper; use crate::sub_lib::wallet::Wallet; use lazy_static::lazy_static; diff --git a/node/src/accountant/database_access_objects/mod.rs b/node/src/accountant/db_access_objects/mod.rs similarity index 100% rename from node/src/accountant/database_access_objects/mod.rs rename to node/src/accountant/db_access_objects/mod.rs diff --git a/node/src/accountant/database_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs similarity index 98% rename from node/src/accountant/database_access_objects/payable_dao.rs rename to node/src/accountant/db_access_objects/payable_dao.rs index 0036134e6..16f72d5d4 100644 --- a/node/src/accountant/database_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -1,21 +1,21 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::big_int_processing::big_int_db_processor::KnownKeyVariants::{ +use crate::accountant::db_big_integer::big_int_db_processor::KnownKeyVariants::{ PendingPayableRowid, WalletAddress, }; -use crate::accountant::big_int_processing::big_int_db_processor::WeiChange::{ +use crate::accountant::db_big_integer::big_int_db_processor::WeiChange::{ Addition, Subtraction, }; -use crate::accountant::big_int_processing::big_int_db_processor::{ +use crate::accountant::db_big_integer::big_int_db_processor::{ BigIntDbProcessor, BigIntSqlConfig, Param, SQLParamsBuilder, TableNameDAO, }; -use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::utils; -use crate::accountant::database_access_objects::utils::{ +use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; +use crate::accountant::db_access_objects::utils; +use crate::accountant::db_access_objects::utils::{ sum_i128_values_from_table, to_time_t, AssemblerFeeder, CustomQuery, DaoFactoryReal, RangeStmConfig, TopStmConfig, VigilantRusqliteFlatten, }; -use crate::accountant::database_access_objects::payable_dao::mark_pending_payable_associated_functions::{ +use crate::accountant::db_access_objects::payable_dao::mark_pending_payable_associated_functions::{ compose_case_expression, execute_command, serialize_wallets, }; use crate::accountant::{checked_conversion, sign_conversion, PendingPayableId}; @@ -48,21 +48,6 @@ pub struct PayableAccount { pub pending_payable_opt: Option, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingPayable { - pub recipient_wallet: Wallet, - pub hash: H256, -} - -impl PendingPayable { - pub fn new(recipient_wallet: Wallet, hash: H256) -> Self { - Self { - recipient_wallet, - hash, - } - } -} - pub trait PayableDao: Debug + Send { fn more_money_payable( &self, @@ -402,8 +387,8 @@ impl TableNameDAO for PayableDaoReal { mod mark_pending_payable_associated_functions { use crate::accountant::comma_joined_stringifiable; - use crate::accountant::database_access_objects::payable_dao::PayableDaoError; - use crate::accountant::database_access_objects::utils::{ + use crate::accountant::db_access_objects::payable_dao::PayableDaoError; + use crate::accountant::db_access_objects::utils::{ update_rows_and_return_valid_count, VigilantRusqliteFlatten, }; use crate::database::connection_wrapper::ConnectionWrapper; @@ -552,9 +537,9 @@ mod mark_pending_payable_associated_functions { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::{from_time_t, now_time_t, to_time_t}; + use crate::accountant::db_access_objects::utils::{from_time_t, now_time_t, to_time_t}; use crate::accountant::gwei_to_wei; - use crate::accountant::database_access_objects::payable_dao::mark_pending_payable_associated_functions::explanatory_extension; + use crate::accountant::db_access_objects::payable_dao::mark_pending_payable_associated_functions::explanatory_extension; use crate::accountant::test_utils::{ assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_pending_payable_fingerprint, diff --git a/node/src/accountant/database_access_objects/pending_payable_dao.rs b/node/src/accountant/db_access_objects/pending_payable_dao.rs similarity index 93% rename from node/src/accountant/database_access_objects/pending_payable_dao.rs rename to node/src/accountant/db_access_objects/pending_payable_dao.rs index 462030704..d18bb1ceb 100644 --- a/node/src/accountant/database_access_objects/pending_payable_dao.rs +++ b/node/src/accountant/db_access_objects/pending_payable_dao.rs @@ -1,12 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::utils::{ +use crate::accountant::db_access_objects::utils::{ from_time_t, to_time_t, DaoFactoryReal, VigilantRusqliteFlatten, }; +use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; use crate::accountant::{checked_conversion, comma_joined_stringifiable}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::database::connection_wrapper::ConnectionWrapper; +use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use masq_lib::utils::ExpectValue; use rusqlite::Row; @@ -26,6 +27,7 @@ pub enum PendingPayableDaoError { } pub trait PendingPayableDao { + // Note that the order of the returned results is not guaranteed fn fingerprints_rowids(&self, hashes: &[H256]) -> Vec<(Option, H256)>; fn return_all_errorless_fingerprints(&self) -> Vec; fn insert_new_fingerprints( @@ -65,9 +67,7 @@ impl PendingPayableDao for PendingPayableDaoReal<'_> { .filter(|hash| !all_found_records.keys().contains(hash)); all_found_records .iter() - .sorted() .map(|(hash, rowid)| (Some(*rowid), *hash)) - .rev() .chain(hashes_of_missing_rowids.map(|hash| (None, *hash))) .collect() } @@ -210,13 +210,18 @@ impl PendingPayableDao for PendingPayableDaoReal<'_> { } } -pub trait PendingPayableDaoFactory { - fn make(&self) -> Box; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingPayable { + pub recipient_wallet: Wallet, + pub hash: H256, } -impl PendingPayableDaoFactory for DaoFactoryReal { - fn make(&self) -> Box { - Box::new(PendingPayableDaoReal::new(self.make_connection())) +impl PendingPayable { + pub fn new(recipient_wallet: Wallet, hash: H256) -> Self { + Self { + recipient_wallet, + hash, + } } } @@ -239,14 +244,24 @@ impl<'a> PendingPayableDaoReal<'a> { } } +pub trait PendingPayableDaoFactory { + fn make(&self) -> Box; +} + +impl PendingPayableDaoFactory for DaoFactoryReal { + fn make(&self) -> Box { + Box::new(PendingPayableDaoReal::new(self.make_connection())) + } +} + #[cfg(test)] mod tests { - use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; - use crate::accountant::database_access_objects::pending_payable_dao::{ + use crate::accountant::db_access_objects::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoReal, }; - use crate::accountant::database_access_objects::utils::from_time_t; + use crate::accountant::db_access_objects::utils::from_time_t; + use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::test_utils::make_tx_hash; use crate::database::connection_wrapper::ConnectionWrapperReal; @@ -388,14 +403,28 @@ mod tests { let result = subject.fingerprints_rowids(&[hash_1, hash_2]); - assert_eq!(result, vec![(Some(1), hash_1), (Some(2), hash_2)]) + let first_expected_pair = (Some(1), hash_1); + assert!( + result.contains(&first_expected_pair), + "Returned rowid pairs should have contained {:?} but all it did is {:?}", + first_expected_pair, + result + ); + let second_expected_pair = (Some(2), hash_2); + assert!( + result.contains(&second_expected_pair), + "Returned rowid pairs should have contained {:?} but all it did is {:?}", + second_expected_pair, + result + ); + assert_eq!(result.len(), 2); } #[test] - fn fingerprints_rowids_when_nonexistent_record() { + fn fingerprints_rowids_when_nonexistent_records() { let home_dir = ensure_node_home_directory_exists( "pending_payable_dao", - "fingerprints_rowids_when_nonexistent_record", + "fingerprints_rowids_when_nonexistent_records", ); let wrapped_conn = DbInitializerReal::default() .initialize(&home_dir, DbInitializationConfig::test_default()) @@ -403,10 +432,32 @@ mod tests { let subject = PendingPayableDaoReal::new(wrapped_conn); let hash_1 = make_tx_hash(11119); let hash_2 = make_tx_hash(22229); + let hash_3 = make_tx_hash(33339); + let hash_4 = make_tx_hash(44449); + { + // For more illustrative results with the hash_3 linking to rowid 2 instead of the ambiguous 1 + subject + .insert_new_fingerprints(&[(hash_2, 8901234)], SystemTime::now()) + .unwrap(); + { + subject + .insert_new_fingerprints(&[(hash_3, 1234567)], SystemTime::now()) + .unwrap() + } + subject.delete_fingerprints(&[1]).unwrap() + } - let result = subject.fingerprints_rowids(&[hash_1, hash_2]); + let result = subject.fingerprints_rowids(&[hash_1, hash_2, hash_3, hash_4]); - assert_eq!(result, vec![(None, hash_1), (None, hash_2)]) + assert_eq!( + result, + vec![ + (Some(2), hash_3), + (None, hash_1), + (None, hash_2), + (None, hash_4) + ] + ) } #[test] diff --git a/node/src/accountant/database_access_objects/receivable_dao.rs b/node/src/accountant/db_access_objects/receivable_dao.rs similarity index 98% rename from node/src/accountant/database_access_objects/receivable_dao.rs rename to node/src/accountant/db_access_objects/receivable_dao.rs index db91cbe93..14fe4b4e9 100644 --- a/node/src/accountant/database_access_objects/receivable_dao.rs +++ b/node/src/accountant/db_access_objects/receivable_dao.rs @@ -1,22 +1,20 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::big_int_processing::big_int_db_processor::KnownKeyVariants::WalletAddress; -use crate::accountant::big_int_processing::big_int_db_processor::WeiChange::{ - Addition, Subtraction, -}; -use crate::accountant::big_int_processing::big_int_db_processor::{ - BigIntDbProcessor, BigIntSqlConfig, Param, SQLParamsBuilder, TableNameDAO, -}; -use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; use crate::accountant::checked_conversion; -use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoError::RusqliteError; -use crate::accountant::database_access_objects::utils; -use crate::accountant::database_access_objects::utils::{ +use crate::accountant::db_access_objects::receivable_dao::ReceivableDaoError::RusqliteError; +use crate::accountant::db_access_objects::utils; +use crate::accountant::db_access_objects::utils::{ sum_i128_values_from_table, to_time_t, AssemblerFeeder, CustomQuery, DaoFactoryReal, RangeStmConfig, ThresholdUtils, TopStmConfig, VigilantRusqliteFlatten, }; +use crate::accountant::db_big_integer::big_int_db_processor::KnownKeyVariants::WalletAddress; +use crate::accountant::db_big_integer::big_int_db_processor::WeiChange::{Addition, Subtraction}; +use crate::accountant::db_big_integer::big_int_db_processor::{ + BigIntDbProcessor, BigIntSqlConfig, Param, SQLParamsBuilder, TableNameDAO, +}; +use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; use crate::accountant::gwei_to_wei; -use crate::blockchain::blockchain_interface::BlockchainTransaction; +use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; use crate::database::connection_wrapper::ConnectionWrapper; use crate::database::db_initializer::{connection_or_panic, DbInitializerReal}; use crate::db_config::persistent_configuration::PersistentConfigError; @@ -31,8 +29,6 @@ use masq_lib::utils::{plus, ExpectValue}; use rusqlite::OptionalExtension; use rusqlite::Row; use rusqlite::{named_params, Error}; -#[cfg(test)] -use std::any::Any; use std::time::SystemTime; #[derive(Debug, PartialEq, Eq)] @@ -86,7 +82,7 @@ pub trait ReceivableDao: Send { //test-only-like method but because of share with multi-node tests #[cfg(test)] is disallowed fn account_status(&self, wallet: &Wallet) -> Option; - declare_as_any!(); + as_any_in_trait!(); } pub trait ReceivableDaoFactory { @@ -269,7 +265,7 @@ impl ReceivableDao for ReceivableDaoReal { } } - implement_as_any!(); + as_any_in_trait_impl!(); } impl ReceivableDaoReal { @@ -413,14 +409,17 @@ impl TableNameDAO for ReceivableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::{from_time_t, now_time_t, to_time_t}; + use crate::accountant::db_access_objects::utils::{ + from_time_t, now_time_t, to_time_t, CustomQuery, + }; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_receivable_account, }; - use crate::database::db_initializer::{DbInitializationConfig, DbInitializer}; - use crate::database::db_initializer::{DbInitializerReal, ExternalData}; + use crate::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, + }; use crate::db_config::persistent_configuration::PersistentConfigError; use crate::test_utils::assert_contains; use crate::test_utils::make_wallet; diff --git a/node/src/accountant/database_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs similarity index 98% rename from node/src/accountant/database_access_objects/utils.rs rename to node/src/accountant/db_access_objects/utils.rs index 39671afe9..85e5adade 100644 --- a/node/src/accountant/database_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -1,8 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; +use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; use crate::accountant::{checked_conversion, gwei_to_wei, sign_conversion}; use crate::database::connection_wrapper::ConnectionWrapper; use crate::database::db_initializer::{ @@ -288,11 +288,11 @@ pub fn remap_receivable_accounts(accounts: Vec) -> Vec>, - outcoming_payments_instructions_sub_opt: Option>, - pps_for_blockchain_bridge_sub_opt: Option>, + outbound_payments_instructions_sub_opt: Option>, + qualified_payables_sub_opt: Option>, retrieve_transactions_sub_opt: Option>, request_transaction_receipts_subs_opt: Option>, report_inbound_payments_sub_opt: Option>, @@ -203,11 +204,15 @@ impl Handler for Accountant { } } -impl Handler for Accountant { +impl Handler for Accountant { type Result = (); - fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) -> Self::Result { - self.handle_payable_payment_setup_msg(msg) + fn handle( + &mut self, + msg: BlockchainAgentWithContextMessage, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_payable_payment_setup(msg) } } @@ -426,8 +431,8 @@ impl Accountant { crashable: config.crash_point == CrashPoint::Message, scan_schedulers: ScanSchedulers::new(scan_intervals), financial_statistics: Rc::clone(&financial_statistics), - outcoming_payments_instructions_sub_opt: None, - pps_for_blockchain_bridge_sub_opt: None, + outbound_payments_instructions_sub_opt: None, + qualified_payables_sub_opt: None, report_sent_payables_sub_opt: None, retrieve_transactions_sub_opt: None, report_inbound_payments_sub_opt: None, @@ -445,10 +450,7 @@ impl Accountant { report_routing_service_provided: recipient!(addr, ReportRoutingServiceProvidedMessage), report_exit_service_provided: recipient!(addr, ReportExitServiceProvidedMessage), report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), - report_consuming_wallet_balances_and_qualified_payables: recipient!( - addr, - PayablePaymentSetup - ), + report_payable_payments_setup: recipient!(addr, BlockchainAgentWithContextMessage), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), report_transaction_receipts: recipient!(addr, ReportTransactionReceipts), @@ -539,14 +541,17 @@ impl Accountant { } fn handle_bind_message(&mut self, msg: BindMessage) { - self.outcoming_payments_instructions_sub_opt = - Some(msg.peer_actors.blockchain_bridge.report_accounts_payable); + self.outbound_payments_instructions_sub_opt = Some( + msg.peer_actors + .blockchain_bridge + .outbound_payments_instructions, + ); self.retrieve_transactions_sub_opt = Some(msg.peer_actors.blockchain_bridge.retrieve_transactions); self.report_inbound_payments_sub_opt = Some(msg.peer_actors.accountant.report_inbound_payments); - self.pps_for_blockchain_bridge_sub_opt = - Some(msg.peer_actors.blockchain_bridge.pps_for_blockchain_bridge); + self.qualified_payables_sub_opt = + Some(msg.peer_actors.blockchain_bridge.qualified_payables); self.report_sent_payables_sub_opt = Some(msg.peer_actors.accountant.report_sent_payments); self.ui_message_sub_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); self.request_transaction_receipts_subs_opt = Some( @@ -645,7 +650,7 @@ impl Accountant { }) } - fn handle_payable_payment_setup_msg(&mut self, msg: PayablePaymentSetup) { + fn handle_payable_payment_setup(&mut self, msg: BlockchainAgentWithContextMessage) { let response_skeleton_opt = msg.response_skeleton_opt; let blockchain_bridge_instructions_opt = match self .scanners @@ -653,13 +658,13 @@ impl Accountant { .try_skipping_payment_adjustment(msg, &self.logger) { Some(result) => match result { - Either::Left(transformed_message) => Some(transformed_message), - Either::Right(adjustment_info) => { + Either::Left(complete_msg) => Some(complete_msg), + Either::Right(unaccepted_msg) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 match self .scanners .payable - .perform_payment_adjustment(adjustment_info, &self.logger) + .perform_payment_adjustment(unaccepted_msg, &self.logger) { Some(instructions) => Some(instructions), None => None, @@ -671,7 +676,7 @@ impl Accountant { match blockchain_bridge_instructions_opt { Some(blockchain_bridge_instructions) => self - .outcoming_payments_instructions_sub_opt + .outbound_payments_instructions_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(blockchain_bridge_instructions) @@ -835,7 +840,7 @@ impl Accountant { &self.logger, ) { Ok(scan_message) => { - self.pps_for_blockchain_bridge_sub_opt + self.qualified_payables_sub_opt .as_ref() .expect("BlockchainBridge is unbound") .try_send(scan_message) @@ -1005,62 +1010,33 @@ pub fn wei_to_gwei, S: Display + Copy + Div + From(wei.div(S::from(WEIS_IN_GWEI as u32))) } -#[cfg(test)] -pub mod check_sqlite_fns { - use super::*; - use crate::sub_lib::accountant::DEFAULT_PAYMENT_THRESHOLDS; - use actix::System; - - #[derive(Message)] - pub struct TestUserDefinedSqliteFnsForNewDelinquencies {} - - impl Handler for Accountant { - type Result = (); - - fn handle( - &mut self, - _msg: TestUserDefinedSqliteFnsForNewDelinquencies, - _ctx: &mut Self::Context, - ) -> Self::Result { - //will crash a test if our user-defined SQLite fns have been unregistered - self.receivable_dao - .new_delinquencies(SystemTime::now(), &DEFAULT_PAYMENT_THRESHOLDS); - System::current().stop(); - } - } -} - #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::payable_dao::{ - PayableAccount, PayableDaoError, PayableDaoFactory, PendingPayable, + use crate::accountant::db_access_objects::payable_dao::{ + PayableAccount, PayableDaoError, PayableDaoFactory, }; - use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; - use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; - use crate::accountant::database_access_objects::utils::from_time_t; - use crate::accountant::database_access_objects::utils::{to_time_t, CustomQuery}; - use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjusterError}; - use crate::accountant::scanners::payable_scan_setup_msgs::{ - FinancialAndTechDetails, StageData, + use crate::accountant::db_access_objects::pending_payable_dao::{ + PendingPayable, PendingPayableDaoError, }; - use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; + use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; + use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; + use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjusterError}; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::scanners::test_utils::protect_payables_in_test; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - assert_real_scan_schedulers, bc_from_earning_wallet, bc_from_wallets, make_payable_account, - make_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, - PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, - PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, - ReceivableDaoMock, ScannerMock, + bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, + BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, + PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, + PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; use crate::blockchain::blockchain_bridge::BlockchainBridge; - use crate::blockchain::blockchain_interface::BlockchainTransaction; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; use crate::blockchain::test_utils::{make_tx_hash, BlockchainInterfaceMock}; use crate::match_every_type_id; use crate::sub_lib::accountant::{ @@ -1068,13 +1044,14 @@ mod tests { DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, OutcomingPaymentsInstructions, + ConsumingWalletBalances, OutboundPaymentsInstructions, }; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; use crate::test_utils::recorder::Recorder; use crate::test_utils::recorder_stop_conditions::{StopCondition, StopConditions}; + use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; use crate::test_utils::unshared_test_utils::{ @@ -1083,7 +1060,7 @@ mod tests { }; use crate::test_utils::{make_paying_wallet, make_wallet}; use actix::{Arbiter, System}; - use ethereum_types::U64; + use ethereum_types::{U256, U64}; use ethsign_crypto::Keccak256; use itertools::Itertools; use log::Level; @@ -1109,7 +1086,7 @@ mod tests { use std::sync::Mutex; use std::time::Duration; use std::vec; - use web3::types::{TransactionReceipt, U256}; + use web3::types::TransactionReceipt; impl Handler> for Accountant { type Result = (); @@ -1213,7 +1190,30 @@ mod tests { ); let financial_statistics = result.financial_statistics().clone(); - assert_real_scan_schedulers(&result.scan_schedulers, ScanIntervals::default()); + let assert_scan_scheduler = |scan_type: ScanType, expected_scan_interval: Duration| { + assert_eq!( + result + .scan_schedulers + .schedulers + .get(&scan_type) + .unwrap() + .interval(), + expected_scan_interval + ) + }; + let default_scan_intervals = ScanIntervals::default(); + assert_scan_scheduler( + ScanType::Payables, + default_scan_intervals.payable_scan_interval, + ); + assert_scan_scheduler( + ScanType::PendingPayables, + default_scan_intervals.pending_payable_scan_interval, + ); + assert_scan_scheduler( + ScanType::Receivables, + default_scan_intervals.receivable_scan_interval, + ); assert_eq!(result.consuming_wallet_opt, None); assert_eq!(*result.earning_wallet, *DEFAULT_EARNING_WALLET); assert_eq!(result.suppress_initial_scans, false); @@ -1352,10 +1352,9 @@ mod tests { system.run(); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &PayablePaymentSetup { - qualified_payables: vec![payable_account], - this_stage_data_opt: None, + blockchain_bridge_recording.get_record::(0), + &QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(vec![payable_account]), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -1381,7 +1380,7 @@ mod tests { let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); let sent_payable = SentPayables { - payment_procedure_result: Ok(vec![Correct(PendingPayable { + payment_procedure_result: Ok(vec![Ok(PendingPayable { recipient_wallet: make_wallet("blah"), hash: make_tx_hash(123), })]), @@ -1415,8 +1414,8 @@ mod tests { let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; let search_for_indispensable_adjustment_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(OutcomingPaymentsInstructions)) + let instructions_recipient = blockchain_bridge + .system_stop_conditions(match_every_type_id!(OutboundPaymentsInstructions)) .start() .recipient(); let mut subject = AccountantBuilder::default().build(); @@ -1429,56 +1428,60 @@ mod tests { .payment_adjuster(payment_adjuster) .build(); subject.scanners.payable = Box::new(payable_scanner); - subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); + subject.outbound_payments_instructions_sub_opt = Some(instructions_recipient); subject.logger = Logger::new(test_name); let subject_addr = subject.start(); let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); - let system = System::new(test_name); - let consuming_balances_and_qualified_payments = PayablePaymentSetup { - qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u32::MAX as u128, - masq_tokens_minor: u32::MAX as u128, - }, - estimated_gas_limit_per_transaction: 112_000, - agreed_transaction_fee_per_computed_unit_major: 123, - }, - )), + let system = System::new("test"); + let expected_agent_id_stamp = ArbitraryIdStamp::new(); + let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); + let accounts = vec![account_1, account_2]; + let msg = BlockchainAgentWithContextMessage { + protected_qualified_payables: protect_payables_in_test(accounts.clone()), + agent: Box::new(agent), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, }), }; - subject_addr - .try_send(consuming_balances_and_qualified_payments.clone()) - .unwrap(); + subject_addr.try_send(msg).unwrap(); system.run(); let mut search_for_indispensable_adjustment_params = search_for_indispensable_adjustment_params_arc .lock() .unwrap(); - let payable_payment_setup_msg = search_for_indispensable_adjustment_params.remove(0); + let (actual_qualified_payables, actual_agent_id_stamp) = + search_for_indispensable_adjustment_params.remove(0); + assert_eq!(actual_qualified_payables, accounts.clone()); + assert_eq!(actual_agent_id_stamp, expected_agent_id_stamp); assert!(search_for_indispensable_adjustment_params.is_empty()); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + let payments_instructions = + blockchain_bridge_recording.get_record::(0); + assert_eq!(payments_instructions.affordable_accounts, accounts); assert_eq!( - payable_payment_setup_msg, - consuming_balances_and_qualified_payments + payments_instructions.response_skeleton_opt, + Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }) ); - let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &OutcomingPaymentsInstructions { - accounts: vec![account_1, account_2], - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 1234, - context_id: 4321, - }) - } + payments_instructions.agent.arbitrary_id_stamp(), + expected_agent_id_stamp ); + assert_eq!(blockchain_bridge_recording.len(), 1); + } + + fn test_use_of_the_same_logger(logger_clone: &Logger, test_name: &str) { + let experiment_msg = format!("DEBUG: {test_name}: hello world"); + let log_handler = TestLogHandler::default(); + log_handler.exists_no_log_containing(&experiment_msg); + debug!(logger_clone, "hello world"); + log_handler.exists_log_containing(&experiment_msg); } #[test] @@ -1492,7 +1495,7 @@ mod tests { let adjust_payments_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let report_recipient = blockchain_bridge - .system_stop_conditions(match_every_type_id!(OutcomingPaymentsInstructions)) + .system_stop_conditions(match_every_type_id!(OutboundPaymentsInstructions)) .start() .recipient(); let mut subject = AccountantBuilder::default().build(); @@ -1500,75 +1503,89 @@ mod tests { let unadjusted_account_2 = make_payable_account(222_222); let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1 + ..unadjusted_account_1.clone() }; let adjusted_account_2 = PayableAccount { balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2 + ..unadjusted_account_2.clone() }; let response_skeleton = ResponseSkeleton { client_id: 12, context_id: 55, }; - let adjusted_payments_instructions = OutcomingPaymentsInstructions { - accounts: vec![adjusted_account_1.clone(), adjusted_account_2.clone()], + let unadjusted_accounts = vec![unadjusted_account_1, unadjusted_account_2]; + let agent_id_stamp_first_phase = ArbitraryIdStamp::new(); + let agent = + BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp_first_phase); + let payable_payments_setup_msg = BlockchainAgentWithContextMessage { + protected_qualified_payables: protect_payables_in_test(unadjusted_accounts.clone()), + agent: Box::new(agent), + response_skeleton_opt: Some(response_skeleton), + }; + // In the real world the agents are identical, here they bear different ids + // so that we can watch their journey better + let agent_id_stamp_second_phase = ArbitraryIdStamp::new(); + let agent = + BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp_second_phase); + let affordable_accounts = vec![adjusted_account_1.clone(), adjusted_account_2.clone()]; + let payments_instructions = OutboundPaymentsInstructions { + affordable_accounts: affordable_accounts.clone(), + agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_params(&adjust_payments_params_arc) - .adjust_payments_result(Ok(adjusted_payments_instructions)); + .adjust_payments_result(Ok(payments_instructions)); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); subject.scanners.payable = Box::new(payable_scanner); - subject.outcoming_payments_instructions_sub_opt = Some(report_recipient); + subject.outbound_payments_instructions_sub_opt = Some(report_recipient); subject.logger = Logger::new(test_name); let subject_addr = subject.start(); - let account_1 = make_payable_account(111_111); - let account_2 = make_payable_account(222_222); - let system = System::new(test_name); - let consuming_balances_and_qualified_payments = PayablePaymentSetup { - qualified_payables: vec![account_1.clone(), account_2.clone()], - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u32::MAX as u128, - masq_tokens_minor: 150_000_000_000, - }, - estimated_gas_limit_per_transaction: 110_000, - agreed_transaction_fee_per_computed_unit_major: 0, - }, - )), - response_skeleton_opt: Some(response_skeleton), - }; + let system = System::new("test"); - subject_addr - .try_send(consuming_balances_and_qualified_payments.clone()) - .unwrap(); + subject_addr.try_send(payable_payments_setup_msg).unwrap(); let before = SystemTime::now(); assert_eq!(system.run(), 0); let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); - let (cwbqp_msg, captured_now) = adjust_payments_params.remove(0); + let (actual_prepared_adjustment, captured_now) = adjust_payments_params.remove(0); + assert_eq!(actual_prepared_adjustment.adjustment, Adjustment::MasqToken); assert_eq!( - cwbqp_msg, - PreparedAdjustment { - original_setup_msg: consuming_balances_and_qualified_payments, - adjustment: Adjustment::MasqToken - } + actual_prepared_adjustment.qualified_payables, + unadjusted_accounts + ); + assert_eq!( + actual_prepared_adjustment.agent.arbitrary_id_stamp(), + agent_id_stamp_first_phase + ); + assert!( + before <= captured_now && captured_now <= after, + "captured timestamp should have been between {:?} and {:?} but was {:?}", + before, + after, + captured_now ); - assert!(before <= captured_now && captured_now <= after); assert!(adjust_payments_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + let actual_payments_instructions = + blockchain_bridge_recording.get_record::(0); assert_eq!( - blockchain_bridge_recording.get_record::(0), - &OutcomingPaymentsInstructions { - accounts: vec![adjusted_account_1, adjusted_account_2], - response_skeleton_opt: Some(response_skeleton) - } + actual_payments_instructions.affordable_accounts, + affordable_accounts + ); + assert_eq!( + actual_payments_instructions.response_skeleton_opt, + Some(response_skeleton) ); + assert_eq!( + actual_payments_instructions.agent.arbitrary_id_stamp(), + agent_id_stamp_second_phase + ); + assert_eq!(blockchain_bridge_recording.len(), 1); } fn test_handling_payment_adjuster_error( @@ -1591,7 +1608,7 @@ mod tests { let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); - subject.outcoming_payments_instructions_sub_opt = Some(blockchain_bridge_recipient); + subject.outbound_payments_instructions_sub_opt = Some(blockchain_bridge_recipient); subject.ui_message_sub_opt = Some(ui_gateway_recipient); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); @@ -1600,31 +1617,20 @@ mod tests { let subject_addr = subject.start(); let account_1 = make_payable_account(111_111); let system = System::new(test_name); - let consuming_balances_and_qualified_payments = PayablePaymentSetup { - qualified_payables: vec![account_1], - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: wei_to_gwei::( - cw_transaction_fee_balance_major_opt.unwrap_or(u64::MAX), - ), - masq_tokens_minor: 150_000_000_000, - }, - estimated_gas_limit_per_transaction: 55_000, - agreed_transaction_fee_per_computed_unit_major: 60, - }, - )), - response_skeleton_opt: Some(response_skeleton), - }; + let agent = BlockchainAgentMock::default(); + let protected_qualified_payables = protect_payables_in_test(vec![account_1]); + let msg = BlockchainAgentWithContextMessage::new( + protected_qualified_payables, + Box::new(agent), + Some(response_skeleton), + ); let assertion_message = AssertionsMessage { assertions: Box::new(|accountant: &mut Accountant| { assert_eq!(accountant.scanners.payable.scan_started_at(), None) // meaning the scan wa called off }), }; - subject_addr - .try_send(consuming_balances_and_qualified_payments) - .unwrap(); + subject_addr.try_send(msg).unwrap(); subject_addr.try_send(assertion_message).unwrap(); assert_eq!(system.run(), 0); @@ -1642,9 +1648,9 @@ mod tests { } #[test] - fn payment_adjuster_spit_out_an_error_from_the_insolvency_check() { + fn payment_adjuster_throws_out_an_error_from_the_insolvency_check() { init_test_logging(); - let test_name = "payment_adjuster_spit_out_an_error_from_the_insolvency_check"; + let test_name = "payment_adjuster_throws_out_an_error_from_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { @@ -1672,10 +1678,10 @@ mod tests { } #[test] - fn payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment() { + fn payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment() { init_test_logging(); let test_name = - "payment_adjuster_spit_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; + "payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsUnexpectedlyEliminated)); @@ -1707,7 +1713,7 @@ mod tests { AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: 20, per_transaction_requirement_minor: 40_000_000_000, - cw_transaction_fee_balance_minor: 123, + cw_transaction_fee_balance_minor: U256::from(123), }, ))); let payable_scanner = PayableScannerBuilder::new() @@ -1715,26 +1721,19 @@ mod tests { .build(); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); - let account_1 = make_payable_account(111_111); - let msg = PayablePaymentSetup { - qualified_payables: vec![account_1], - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u128::MAX, - masq_tokens_minor: 150_000_000_000, - }, - estimated_gas_limit_per_transaction: 55_000, - agreed_transaction_fee_per_computed_unit_major: 60, - }, - )), - response_skeleton_opt: None, - }; + let account = make_payable_account(111_111); + let protected_payables = protect_payables_in_test(vec![account]); + let blockchain_agent = BlockchainAgentMock::default(); + let msg = BlockchainAgentWithContextMessage::new( + protected_payables, + Box::new(blockchain_agent), + None, + ); - subject.handle_payable_payment_setup_msg(msg); + subject.handle_payable_payment_setup(msg); - // test didn't blow up while the subject was disconnected and the system didn't - // run therefore we didn't attempt to send the NodeUiMessage + // Test didn't blow up while the subject was unbound to other actors + // therefore we didn't attempt to send the NodeUiMessage TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ Preventing to settle already mature payables could have serious consequences" @@ -1910,7 +1909,7 @@ mod tests { .build(); let expected_payable = PendingPayable::new(expected_wallet.clone(), expected_hash.clone()); let sent_payable = SentPayables { - payment_procedure_result: Ok(vec![Correct(expected_payable.clone())]), + payment_procedure_result: Ok(vec![Ok(expected_payable.clone())]), response_skeleton_opt: None, }; let subject = accountant.start(); @@ -1932,8 +1931,7 @@ mod tests { } #[test] - fn accountant_sends_payable_payment_setup_msg_to_blockchain_bridge_when_qualified_payable_found( - ) { + fn accountant_sends_qualified_payables_msg_when_qualified_payable_found() { let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let now = SystemTime::now(); let payment_thresholds = PaymentThresholds::default(); @@ -1941,7 +1939,9 @@ mod tests { make_payables(now, &payment_thresholds); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); - let system = System::new("request for balances forwarded to blockchain_bridge"); + let system = System::new( + "accountant_sends_initial_payable_payments_msg_when_qualified_payable_found", + ); let mut subject = AccountantBuilder::default() .bootstrapper_config(bc_from_earning_wallet(make_wallet("some_wallet_address"))) .payable_daos(vec![ForPayableScanner(payable_dao)]) @@ -1961,25 +1961,23 @@ mod tests { system.run(); let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!(blockchain_bridge_recorder.len(), 1); - let message = blockchain_bridge_recorder.get_record::(0); + let message = blockchain_bridge_recorder.get_record::(0); assert_eq!( message, - &PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: None, + &QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(qualified_payables), response_skeleton_opt: None, } ); } #[test] - fn accountant_sends_request_to_blockchain_bridge_to_scan_for_received_payments() { + fn accountant_requests_blockchain_bridge_to_scan_for_received_payments() { init_test_logging(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let earning_wallet = make_wallet("someearningwallet"); - let system = System::new( - "accountant_sends_request_to_blockchain_bridge_to_scan_for_received_payments", - ); + let system = + System::new("accountant_requests_blockchain_bridge_to_scan_for_received_payments"); let receivable_dao = ReceivableDaoMock::new() .new_delinquencies_result(vec![]) .paid_delinquencies_result(vec![]); @@ -2014,7 +2012,7 @@ mod tests { } #[test] - fn accountant_processes_msg_with_received_payments_using_receivables_dao() { + fn accountant_uses_receivables_dao_to_process_received_payments() { let now = SystemTime::now(); let earning_wallet = make_wallet("earner3000"); let expected_receivable_1 = BlockchainTransaction { @@ -2035,8 +2033,7 @@ mod tests { .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) .receivable_daos(vec![ForReceivableScanner(receivable_dao)]) .build(); - let system = - System::new("accountant_processes_msg_with_received_payments_using_receivables_dao"); + let system = System::new("accountant_uses_receivables_dao_to_process_received_payments"); let subject = accountant.start(); subject @@ -2136,7 +2133,7 @@ mod tests { recipient: make_wallet("some_recipient"), response_skeleton_opt: None, })) - .stop_the_system(); + .stop_the_system_after_last_msg(); let mut config = make_bc_with_defaults(); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(100), @@ -2155,7 +2152,7 @@ mod tests { Some(Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_receivable_params_arc) - .permit_to_send_out(), + .capture_msg_and_let_it_fly_on(), )), None, ); @@ -2207,7 +2204,7 @@ mod tests { pending_payable: vec![], response_skeleton_opt: None, })) - .stop_the_system(); + .stop_the_system_after_last_msg(); let mut config = make_bc_with_defaults(); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(100), @@ -2226,7 +2223,7 @@ mod tests { Some(Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_pending_payable_params_arc) - .permit_to_send_out(), + .capture_msg_and_let_it_fly_on(), )), None, ); @@ -2275,12 +2272,13 @@ mod tests { let payable_scanner = ScannerMock::new() .begin_scan_params(&begin_scan_params_arc) .begin_scan_result(Err(BeginScanError::NothingToProcess)) - .begin_scan_result(Ok(PayablePaymentSetup { - qualified_payables: vec![], - this_stage_data_opt: None, + .begin_scan_result(Ok(QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(vec![make_payable_account( + 123, + )]), response_skeleton_opt: None, })) - .stop_the_system(); + .stop_the_system_after_last_msg(); let mut config = bc_from_earning_wallet(make_wallet("hi")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(97), @@ -2299,7 +2297,7 @@ mod tests { Some(Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_payables_params_arc) - .permit_to_send_out(), + .capture_msg_and_let_it_fly_on(), )), None, ); @@ -2424,13 +2422,13 @@ mod tests { "scan_for_payable_message_does_not_trigger_payment_for_balances_below_the_curve", ); let blockchain_bridge_addr: Addr = blockchain_bridge.start(); - let report_accounts_payable_sub = - blockchain_bridge_addr.recipient::(); + let outbound_payments_instructions_sub = + blockchain_bridge_addr.recipient::(); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_daos(vec![ForPayableScanner(payable_dao)]) .build(); - subject.outcoming_payments_instructions_sub_opt = Some(report_accounts_payable_sub); + subject.outbound_payments_instructions_sub_opt = Some(outbound_payments_instructions_sub); let _result = subject .scanners @@ -2484,8 +2482,8 @@ mod tests { let payable_dao = PayableDaoMock::default().non_pending_payables_result(qualified_payables.clone()); let (blockchain_bridge, _, blockchain_bridge_recordings_arc) = make_recorder(); - let blockchain_bridge = - blockchain_bridge.system_stop_conditions(match_every_type_id!(PayablePaymentSetup)); + let blockchain_bridge = blockchain_bridge + .system_stop_conditions(match_every_type_id!(QualifiedPayablesMessage)); let system = System::new("scan_for_payable_message_triggers_payment_for_balances_over_the_curve"); let peer_actors = peer_actors_builder() @@ -2505,12 +2503,11 @@ mod tests { system.run(); let blockchain_bridge_recordings = blockchain_bridge_recordings_arc.lock().unwrap(); - let message = blockchain_bridge_recordings.get_record::(0); + let message = blockchain_bridge_recordings.get_record::(0); assert_eq!( message, - &PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: None, + &QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(qualified_payables), response_skeleton_opt: None, } ); @@ -2524,8 +2521,8 @@ mod tests { let (blockchain_bridge, _, blockchain_bridge_recording) = make_recorder(); let blockchain_bridge_addr = blockchain_bridge .system_stop_conditions(match_every_type_id!( - PayablePaymentSetup, - PayablePaymentSetup + QualifiedPayablesMessage, + QualifiedPayablesMessage )) .start(); let pps_for_blockchain_bridge_sub = blockchain_bridge_addr.clone().recipient(); @@ -2560,7 +2557,7 @@ mod tests { context_id: 444, }), }; - subject.pps_for_blockchain_bridge_sub_opt = Some(pps_for_blockchain_bridge_sub); + subject.qualified_payables_sub_opt = Some(pps_for_blockchain_bridge_sub); let addr = subject.start(); addr.try_send(message_before.clone()).unwrap(); @@ -2586,12 +2583,12 @@ mod tests { let recording = blockchain_bridge_recording.lock().unwrap(); let messages_received = recording.len(); assert_eq!(messages_received, 2); - let first_message: &PayablePaymentSetup = recording.get_record(0); + let first_message: &QualifiedPayablesMessage = recording.get_record(0); assert_eq!( first_message.response_skeleton_opt, message_before.response_skeleton_opt ); - let second_message: &PayablePaymentSetup = recording.get_record(1); + let second_message: &QualifiedPayablesMessage = recording.get_record(1); assert_eq!( second_message.response_skeleton_opt, message_after.response_skeleton_opt @@ -3292,6 +3289,7 @@ mod tests { #[test] fn pending_transaction_is_registered_and_monitored_until_it_gets_confirmed_or_canceled() { init_test_logging(); + let build_blockchain_agent_params = Arc::new(Mutex::new(vec![])); let mark_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); let transactions_confirmed_params_arc = Arc::new(Mutex::new(vec![])); let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); @@ -3302,7 +3300,7 @@ mod tests { let delete_record_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_scan_for_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_scan_for_pending_payable_arc_cloned = - notify_later_scan_for_pending_payable_params_arc.clone(); //because it moves into a closure + notify_later_scan_for_pending_payable_params_arc.clone(); // because it moves into a closure let pending_tx_hash_1 = make_tx_hash(0x7b); let pending_tx_hash_2 = make_tx_hash(0x237); let rowid_for_account_1 = 3; @@ -3326,25 +3324,23 @@ mod tests { let transaction_receipt_tx_1_second_round = TransactionReceipt::default(); let transaction_receipt_tx_2_second_round = TransactionReceipt::default(); let mut transaction_receipt_tx_1_third_round = TransactionReceipt::default(); - transaction_receipt_tx_1_third_round.status = Some(U64::from(0)); //failure + transaction_receipt_tx_1_third_round.status = Some(U64::from(0)); // failure let transaction_receipt_tx_2_third_round = TransactionReceipt::default(); let mut transaction_receipt_tx_2_fourth_round = TransactionReceipt::default(); transaction_receipt_tx_2_fourth_round.status = Some(U64::from(1)); // confirmed + let agent = BlockchainAgentMock::default(); let blockchain_interface = BlockchainInterfaceMock::default() - .get_gas_balance_result(Ok(U256::from(u128::MAX))) - .get_token_balance_result(Ok(U256::from(u128::MAX))) - .get_transaction_count_result(Ok(web3::types::U256::from(1))) - .estimated_gas_limit_per_payable_result(55_000) - .get_transaction_count_result(Ok(web3::types::U256::from(2))) - //because we cannot have both, resolution on the high level and also of what's inside blockchain interface, - //there is one component missing in this wholesome test - the part where we send a request for - //a fingerprint of that payable in the DB - this happens inside send_raw_transaction() + .build_blockchain_agent_params(&build_blockchain_agent_params) + .build_blockchain_agent_result(Ok(Box::new(agent))) + // because we cannot have both, resolution on the high level and also of what's inside blockchain interface, + // there is one component missing in this wholesome test - the part where we send a request for + // a fingerprint of that payable in the DB - this happens inside send_raw_transaction() .send_batch_of_payables_result(Ok(vec![ - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_1.clone(), hash: pending_tx_hash_1, }), - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_2.clone(), hash: pending_tx_hash_2, }), @@ -3359,12 +3355,14 @@ mod tests { .get_transaction_receipt_result(Ok(Some(transaction_receipt_tx_2_fourth_round))); let consuming_wallet = make_paying_wallet(b"wallet"); let system = System::new("pending_transaction"); - let persistent_config = PersistentConfigurationMock::default().gas_price_result(Ok(130)); + let persistent_config_id_stamp = ArbitraryIdStamp::new(); + let persistent_config = PersistentConfigurationMock::default() + .set_arbitrary_id_stamp(persistent_config_id_stamp); let blockchain_bridge = BlockchainBridge::new( Box::new(blockchain_interface), Box::new(persistent_config), false, - Some(consuming_wallet), + Some(consuming_wallet.clone()), ); let account_1 = PayableAccount { wallet: wallet_account_1.clone(), @@ -3378,7 +3376,7 @@ mod tests { last_paid_timestamp: past_payable_timestamp_2, pending_payable_opt: None, }; - let pending_payable_scan_interval = 200; //should be slightly less than 1/5 of the time until shutting the system + let pending_payable_scan_interval = 200; // should be slightly less than 1/5 of the time until shutting the system let payable_dao_for_payable_scanner = PayableDaoMock::new() .non_pending_payables_params(&non_pending_payables_params_arc) .non_pending_payables_result(vec![account_1, account_2]) @@ -3389,8 +3387,8 @@ mod tests { .transactions_confirmed_result(Ok(())); let mut bootstrapper_config = bc_from_earning_wallet(make_wallet("some_wallet_address")); bootstrapper_config.scan_intervals_opt = Some(ScanIntervals { - payable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan - receivable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan + payable_scan_interval: Duration::from_secs(1_000_000), // we don't care about this scan + receivable_scan_interval: Duration::from_secs(1_000_000), // we don't care about this scan pending_payable_scan_interval: Duration::from_millis(pending_payable_scan_interval), }); let fingerprint_1_first_round = PendingPayableFingerprint { @@ -3459,10 +3457,10 @@ mod tests { .increment_scan_attempts_result(Ok(())) .increment_scan_attempts_result(Ok(())) .mark_failures_params(&mark_failure_params_arc) - //we don't have a better solution yet, so we mark this down + // we don't have a better solution yet, so we mark this down .mark_failures_result(Ok(())) .delete_fingerprints_params(&delete_record_params_arc) - //this is used during confirmation of the successful one + // this is used during confirmation of the successful one .delete_fingerprints_result(Ok(())); pending_payable_dao_for_pending_payable_scanner .have_return_all_errorless_fingerprints_shut_down_the_system = true; @@ -3471,19 +3469,25 @@ mod tests { .start(move |_| { let mut subject = AccountantBuilder::default() .bootstrapper_config(bootstrapper_config) - .payable_daos(vec![ - ForPayableScanner(payable_dao_for_payable_scanner), - ForPendingPayableScanner(payable_dao_for_pending_payable_scanner), - ]) - .pending_payable_daos(vec![ - ForPayableScanner(pending_payable_dao_for_payable_scanner), - ForPendingPayableScanner(pending_payable_dao_for_pending_payable_scanner), - ]) + .payable_daos(vec![ForPendingPayableScanner( + payable_dao_for_pending_payable_scanner, + )]) + .pending_payable_daos(vec![ForPendingPayableScanner( + pending_payable_dao_for_pending_payable_scanner, + )]) .build(); subject.scanners.receivable = Box::new(NullScanner::new()); + let payment_adjuster = PaymentAdjusterMock::default() + .search_for_indispensable_adjustment_result(Ok(None)); + let payable_scanner = PayableScannerBuilder::new() + .payable_dao(payable_dao_for_payable_scanner) + .pending_payable_dao(pending_payable_dao_for_payable_scanner) + .payment_adjuster(payment_adjuster) + .build(); + subject.scanners.payable = Box::new(payable_scanner); let notify_later_half_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_scan_for_pending_payable_arc_cloned) - .permit_to_send_out(); + .capture_msg_and_let_it_fly_on(); subject.scan_schedulers.update_scheduler( ScanType::PendingPayables, Some(Box::new(notify_later_half_mock)), @@ -3519,10 +3523,10 @@ mod tests { assert_eq!(second_payable.1, rowid_for_account_2); let return_all_errorless_fingerprints_params = return_all_errorless_fingerprints_params_arc.lock().unwrap(); - //it varies with machines and sometimes we manage more cycles than necessary + // it varies with machines and sometimes we manage more cycles than necessary assert!(return_all_errorless_fingerprints_params.len() >= 5); let non_pending_payables_params = non_pending_payables_params_arc.lock().unwrap(); - assert_eq!(*non_pending_payables_params, vec![()]); //because we disabled further scanning for payables + assert_eq!(*non_pending_payables_params, vec![()]); // because we disabled further scanning for payables let get_transaction_receipt_params = get_transaction_receipt_params_arc.lock().unwrap(); assert_eq!( *get_transaction_receipt_params, @@ -3536,6 +3540,11 @@ mod tests { pending_tx_hash_2, ] ); + let build_blockchain_agent_params = build_blockchain_agent_params.lock().unwrap(); + assert_eq!( + *build_blockchain_agent_params, + vec![(consuming_wallet, persistent_config_id_stamp)] + ); let update_fingerprints_params = update_fingerprint_params_arc.lock().unwrap(); assert_eq!( *update_fingerprints_params, @@ -3564,7 +3573,7 @@ mod tests { notify_later_scan_for_pending_payable_params_arc .lock() .unwrap(); - //it varies with machines and sometimes we manage more cycles than necessary + // it varies with machines and sometimes we manage more cycles than necessary let vector_of_first_five_cycles = notify_later_check_for_confirmation .drain(0..=4) .collect_vec(); @@ -3580,10 +3589,17 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing( - "WARN: Accountant: Broken transactions 0x000000000000000000000000000000000000000000000000000000000000007b marked as an error. \ - You should take over the care of those to make sure your debts are going to be settled properly. At the moment, there is no automated process fixing that without your assistance"); - log_handler.exists_log_matching("INFO: Accountant: Transaction 0x0000000000000000000000000000000000000000000000000000000000000237 has been added to the blockchain; detected locally at attempt 4 at \\d{2,}ms after its sending"); - log_handler.exists_log_containing("INFO: Accountant: Transactions 0x0000000000000000000000000000000000000000000000000000000000000237 completed their confirmation process succeeding"); + "WARN: Accountant: Broken transactions 0x00000000000000000000000000000000000000000000000\ + 0000000000000007b marked as an error. You should take over the care of those to make sure \ + your debts are going to be settled properly. At the moment, there is no automated process \ + fixing that without your assistance"); + log_handler.exists_log_matching("INFO: Accountant: Transaction 0x000000000000000000000000000\ + 0000000000000000000000000000000000237 has been added to the blockchain; detected locally at \ + attempt 4 at \\d{2,}ms after its sending"); + log_handler.exists_log_containing( + "INFO: Accountant: Transactions 0x000000000000000000000000\ + 0000000000000000000000000000000000000237 completed their confirmation process succeeding", + ); } #[test] @@ -3718,59 +3734,81 @@ mod tests { confirmation for all these transactions: 0x00000000000000000000000000000000000000000000000000000000000001c8"); } + const EXAMPLE_RESPONSE_SKELETON: ResponseSkeleton = ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }; + + const EXAMPLE_ERROR_MSG: &str = "My tummy hurts"; + #[test] - fn handles_scan_error() { - let response_skeleton = ResponseSkeleton { - client_id: 1234, - context_id: 4321, - }; - let msg = "My tummy hurts"; + fn handling_scan_error_for_externally_triggered_payables() { assert_scan_error_is_handled_properly( - "payables_externally_triggered", + "handling_scan_error_for_externally_triggered_payables", ScanError { scan_type: ScanType::Payables, - response_skeleton_opt: Some(response_skeleton), - msg: msg.to_string(), + response_skeleton_opt: Some(EXAMPLE_RESPONSE_SKELETON), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); + } + + #[test] + fn handling_scan_error_for_externally_triggered_pending_payables() { assert_scan_error_is_handled_properly( - "pending_payables_externally_triggered", + "handling_scan_error_for_externally_triggered_pending_payables", ScanError { scan_type: ScanType::PendingPayables, - response_skeleton_opt: Some(response_skeleton), - msg: msg.to_string(), + response_skeleton_opt: Some(EXAMPLE_RESPONSE_SKELETON), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); + } + + #[test] + fn handling_scan_error_for_externally_triggered_receivables() { assert_scan_error_is_handled_properly( - "receivables_externally_triggered", + "handling_scan_error_for_externally_triggered_receivables", ScanError { scan_type: ScanType::Receivables, - response_skeleton_opt: Some(response_skeleton), - msg: msg.to_string(), + response_skeleton_opt: Some(EXAMPLE_RESPONSE_SKELETON), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); + } + + #[test] + fn handling_scan_error_for_internally_triggered_payables() { assert_scan_error_is_handled_properly( - "payables_internally_triggered", + "handling_scan_error_for_internally_triggered_payables", ScanError { scan_type: ScanType::Payables, response_skeleton_opt: None, - msg: msg.to_string(), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); + } + + #[test] + fn handling_scan_error_for_internally_triggered_pending_payables() { assert_scan_error_is_handled_properly( - "pending_payables_internally_triggered", + "handling_scan_error_for_internally_triggered_pending_payables", ScanError { scan_type: ScanType::PendingPayables, response_skeleton_opt: None, - msg: msg.to_string(), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); + } + + #[test] + fn handling_scan_error_for_internally_triggered_receivables() { assert_scan_error_is_handled_properly( - "receivables_internally_triggered", + "handling_scan_error_for_internally_triggered_receivables", ScanError { scan_type: ScanType::Receivables, response_skeleton_opt: None, - msg: msg.to_string(), + msg: EXAMPLE_ERROR_MSG.to_string(), }, ); } @@ -4616,3 +4654,199 @@ mod tests { assert_on_initialization_with_panic_on_migration(&data_dir, &act); } } + +#[cfg(test)] +pub mod exportable_test_parts { + use super::*; + use crate::accountant::test_utils::bc_from_earning_wallet; + use crate::actor_system_factory::SubsFactory; + use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; + use crate::sub_lib::accountant::DEFAULT_PAYMENT_THRESHOLDS; + use crate::test_utils::actor_system_factory::BannedCacheLoaderMock; + use crate::test_utils::make_wallet; + use crate::test_utils::recorder::make_accountant_subs_from_recorder; + use crate::test_utils::unshared_test_utils::{AssertionsMessage, SubsFactoryTestAddrLeaker}; + use actix::System; + use crossbeam_channel::bounded; + use masq_lib::test_utils::utils::ShouldWeRunTheTest::{GoAhead, Skip}; + use masq_lib::test_utils::utils::{ + check_if_source_code_is_attached, ensure_node_home_directory_exists, ShouldWeRunTheTest, + }; + use regex::Regex; + use std::env::current_dir; + use std::fs::File; + use std::io::{BufRead, BufReader}; + use std::path::PathBuf; + + impl SubsFactory for SubsFactoryTestAddrLeaker { + fn make(&self, addr: &Addr) -> AccountantSubs { + self.send_leaker_msg_and_return_meaningless_subs( + addr, + make_accountant_subs_from_recorder, + ) + } + } + + fn verify_presence_of_user_defined_sqlite_fns_in_new_delinquencies_for_receivable_dao( + ) -> ShouldWeRunTheTest { + fn skip_down_to_first_line_saying_new_delinquencies( + previous: impl Iterator, + ) -> impl Iterator { + previous + .skip_while(|line| { + let adjusted_line: String = line + .chars() + .skip_while(|char| char.is_whitespace()) + .collect(); + !adjusted_line.starts_with("fn new_delinquencies(") + }) + .skip(1) + } + fn assert_is_not_trait_definition(body_lines: impl Iterator) -> String { + fn yield_if_contains_semicolon(line: &str) -> Option { + line.contains(';').then(|| line.to_string()) + } + let mut semicolon_line_opt = None; + let line_undivided_fn_body = body_lines + .map(|line| { + if semicolon_line_opt.is_none() { + if let Some(result) = yield_if_contains_semicolon(&line) { + semicolon_line_opt = Some(result) + } + } + line + }) + .collect::(); + if let Some(line) = semicolon_line_opt { + let regex = Regex::new(r"Vec<\w+>;").unwrap(); + if regex.is_match(&line) { + // The important part of the regex is the semicolon at the end. Trait + // implementations don't use it. They go on with an opening bracket of + // the function body. Its presence therefore signifies we have to do + // with a trait definition + panic!( + "The second parsed chunk of code is a trait definition \ + and the implementation lies before it. Conventions say the opposite. Simply \ + change the placement order in the production code." + ) + } + } + line_undivided_fn_body + } + fn scope_fn_new_delinquency_alone(reader: BufReader) -> String { + let all_lines_in_the_file = reader.lines().flatten(); + let lines_with_cut_fn_trait_definition = + skip_down_to_first_line_saying_new_delinquencies(all_lines_in_the_file); + let assumed_implemented_function_body = + skip_down_to_first_line_saying_new_delinquencies( + lines_with_cut_fn_trait_definition, + ) + .take_while(|line| { + let adjusted_line: String = line + .chars() + .skip_while(|char| char.is_whitespace()) + .collect(); + !adjusted_line.starts_with("fn") + }); + assert_is_not_trait_definition(assumed_implemented_function_body) + } + fn user_defined_functions_detected(line_undivided_fn_body: &str) -> bool { + line_undivided_fn_body.contains(" slope_drop_high_bytes(") + && line_undivided_fn_body.contains(" slope_drop_low_bytes(") + } + + let current_dir = current_dir().unwrap(); + let file_path = current_dir.join(PathBuf::from_iter([ + "src", + "accountant", + "db_access_objects", + "receivable_dao.rs", + ])); + let file = match File::open(file_path) { + Ok(file) => file, + Err(_) => match check_if_source_code_is_attached(¤t_dir) { + Skip => return Skip, + _ => panic!( + "if panics, the file receivable_dao.rs probably doesn't exist or \ + has moved to an unexpected location" + ), + }, + }; + let reader = BufReader::new(file); + let function_body_ready_for_final_check = scope_fn_new_delinquency_alone(reader); + if user_defined_functions_detected(&function_body_ready_for_final_check) { + GoAhead + } else { + panic!( + "was about to test user-defined SQLite functions (slope_drop_high_bytes and + slope_drop_low_bytes) in new_delinquencies() but found out those are absent at the + expected place and would leave falsely positive results" + ) + } + } + + pub fn test_accountant_is_constructed_with_upgraded_db_connection_recognizing_our_extra_sqlite_functions< + A, + >( + test_module: &str, + test_name: &str, + act: A, + ) where + A: FnOnce( + BootstrapperConfig, + DbInitializerReal, + BannedCacheLoaderMock, + SubsFactoryTestAddrLeaker, + ) -> AccountantSubs, + { + // precondition: .new_delinquencies() still encompasses the considered functions, otherwise + // the test is false-positive + if let Skip = + verify_presence_of_user_defined_sqlite_fns_in_new_delinquencies_for_receivable_dao() + { + eprintln!( + "skipping test {test_name} due to having been unable to find receivable_dao.rs" + ); + return; + }; + let data_dir = ensure_node_home_directory_exists(test_module, test_name); + let _ = DbInitializerReal::default() + .initialize(data_dir.as_ref(), DbInitializationConfig::test_default()) + .unwrap(); + let mut bootstrapper_config = bc_from_earning_wallet(make_wallet("mine")); + bootstrapper_config.data_directory = data_dir; + let db_initializer = DbInitializerReal::default(); + let banned_cache_loader = BannedCacheLoaderMock::default(); + let (tx, accountant_addr_rv) = bounded(1); + let address_leaker = SubsFactoryTestAddrLeaker { address_leaker: tx }; + let system = System::new(test_name); + + act( + bootstrapper_config, + db_initializer, + banned_cache_loader, + address_leaker, + ); + + let accountant_addr = accountant_addr_rv.try_recv().unwrap(); + let assertion_msg = AssertionsMessage { + assertions: Box::new(|accountant: &mut Accountant| { + // Will crash a test if our user-defined SQLite fns have been unreachable; + // We cannot rely on failures in the DAO tests, because Account's database connection + // has to be set up specially first (we teach it about the extra functions) as we're + // creating the actor + + accountant + .receivable_dao + .new_delinquencies(SystemTime::now(), &DEFAULT_PAYMENT_THRESHOLDS); + // Don't move this to the main test, it could produce a deceiving result. + // It wouldn't actually process this message. I don't know why exactly + System::current().stop(); + }), + }; + accountant_addr.try_send(assertion_msg).unwrap(); + assert_eq!(system.run(), 0); + // We didn't blow up, it recognized the functions. + // This is an example of the error: "no such function: slope_drop_high_bytes" + } +} diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index f3e53cdad..67a3f713a 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; @@ -97,7 +97,6 @@ fn adjust_last_one( last_account: PayableAccount, ) -> Option { let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance_minor(); - eprintln!("cw balance: {}", cw_balance); let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { last_account.balance_wei } else { @@ -133,7 +132,7 @@ fn empty_or_single_element( #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ adjust_last_one, empty_or_single_element, AdjustmentRunner, MasqAndTransactionFeeRunner, MasqOnlyRunner, @@ -141,7 +140,6 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; - use crate::accountant::scanners::payable_scan_setup_msgs::FinancialAndTechDetails; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; @@ -151,17 +149,9 @@ mod tests { use std::time::{Duration, SystemTime}; fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { - let details = FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 0, - masq_tokens_minor: cw_balance, - }, - agreed_transaction_fee_per_computed_unit_major: 30, - estimated_gas_limit_per_transaction: 100, - }; let adjustment = Adjustment::MasqToken; let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.initialize_inner(details, adjustment, now); + payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); payment_adjuster } @@ -281,15 +271,15 @@ mod tests { }; let mut payment_adjuster = PaymentAdjusterReal::new(); let cw_balance = 9_000_000; - let details = FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 0, - masq_tokens_minor: cw_balance, - }, - agreed_transaction_fee_per_computed_unit_major: 30, - estimated_gas_limit_per_transaction: 100, - }; - payment_adjuster.initialize_inner(details, adjustment, now); + // let details = FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // transaction_fee_minor: 0, + // masq_tokens_minor: cw_balance, + // }, + // agreed_transaction_fee_per_computed_unit_major: 30, + // estimated_gas_limit_per_transaction: 100, + // }; + payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = MasqOnlyRunner {}; let criteria_and_accounts = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index d2d63abcc..90b546d39 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, CalculatorWithNamedMainParameter, }; diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index cfc957d35..2a807f789 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, CalculatorWithNamedMainParameter, }; diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 115f02556..a30892bd5 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -3,7 +3,7 @@ pub mod age_criterion_calculator; pub mod balance_criterion_calculator; -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ compute_progressive_characteristics, DiagnosticsConfig, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 120f92f24..ed1c93ba6 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -42,7 +42,7 @@ where } } -pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { +pub fn collection_diagnostics(label: &str, accounts: &[D]) { if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { eprintln!("{}", label); accounts @@ -52,7 +52,7 @@ pub fn diagnostics_for_collections(label: &str, accounts: &[D]) { } pub mod separately_defined_diagnostic_functions { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::CalculatorWithNamedMainParameter; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 32210b2d7..1ee6b8bb0 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; @@ -11,6 +11,7 @@ use std::collections::HashMap; use std::iter::once; use std::ops::Not; use thousands::Separable; +use web3::types::U256; const REFILL_RECOMMENDATION: &str = "\ In order to continue consuming services from other Nodes and avoid delinquency bans it is necessary \ @@ -146,7 +147,7 @@ pub fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_m pub fn log_insufficient_transaction_fee_balance( logger: &Logger, required_transactions_count: u16, - transaction_fee_minor: u128, + transaction_fee_minor: U256, limiting_count: u16, ) { warning!( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 807656c1e..d1c080c1d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; #[derive(Debug)] pub enum AdjustmentIterationResult { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 739c7eb7e..3c28b17d3 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ account_nominated_for_disqualification_diagnostics, exhausting_cw_balance_diagnostics, @@ -376,7 +376,7 @@ impl From for PayableAccount { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, }; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 687cad575..e88ba8c99 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -10,7 +10,7 @@ mod miscellaneous; mod test_utils; mod verifier; -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::gwei_to_wei; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, MasqAndTransactionFeeRunner, MasqOnlyRunner, @@ -20,7 +20,7 @@ use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; -use crate::accountant::payment_adjuster::diagnostics::{diagnostics, diagnostics_for_collections}; +use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; @@ -37,13 +37,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_sums_and_leave_accounts, keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; -use crate::accountant::scanners::payable_scan_setup_msgs::{ - FinancialAndTechDetails, PayablePaymentSetup, StageData, -}; -use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; +use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use itertools::Either; use masq_lib::logger::Logger; @@ -55,20 +51,24 @@ use std::iter::once; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, - msg: &PayablePaymentSetup, + qualified_payables: &[PayableAccount], + agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError>; fn adjust_payments( &mut self, setup: PreparedAdjustment, now: SystemTime, - ) -> Result; + ) -> Result; - declare_as_any!(); + as_any_in_trait!(); } pub struct PaymentAdjusterReal { @@ -79,17 +79,13 @@ pub struct PaymentAdjusterReal { impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( &self, - msg: &PayablePaymentSetup, + qualified_payables: &[PayableAccount], + agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError> { - let qualified_payables = msg.qualified_payables.as_slice(); let required_tx_count = qualified_payables.len(); - let this_stage_data = match msg.this_stage_data_opt.as_ref().expectv("collected data") { - StageData::FinancialAndTechDetails(details) => details, - }; - match Self::determine_transaction_count_limit_by_transaction_fee( - &this_stage_data, + agent, required_tx_count, &self.logger, ) { @@ -102,10 +98,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => return Err(e), }; + let service_fee_balance_minor = agent + .consuming_wallet_balances() + .service_fee_balance_in_minor_units; match Self::check_need_of_masq_adjustment( &self.logger, Either::Left(qualified_payables), - this_stage_data.consuming_wallet_balances.masq_tokens_minor, + service_fee_balance_minor, ) { Ok(false) => Ok(None), Ok(true) => Ok(Some(Adjustment::MasqToken)), @@ -117,16 +116,16 @@ impl PaymentAdjuster for PaymentAdjusterReal { &mut self, setup: PreparedAdjustment, now: SystemTime, - ) -> Result { - let msg = setup.original_setup_msg; - let qualified_payables: Vec = msg.qualified_payables; - let response_skeleton_opt = msg.response_skeleton_opt; - let current_stage_data = match msg.this_stage_data_opt.expectv("complete setup data") { - StageData::FinancialAndTechDetails(details) => details, - }; + ) -> Result { + let qualified_payables = setup.qualified_payables; + let response_skeleton_opt = setup.response_skeleton_opt; + let agent = setup.agent; + let initial_service_fee_balance_minor = agent + .consuming_wallet_balances() + .service_fee_balance_in_minor_units; let required_adjustment = setup.adjustment; - self.initialize_inner(current_stage_data, required_adjustment, now); + self.initialize_inner(initial_service_fee_balance_minor, required_adjustment, now); let debug_info_opt = self.debug_info_opt(&qualified_payables); @@ -138,13 +137,14 @@ impl PaymentAdjuster for PaymentAdjusterReal { before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) ); - Ok(OutcomingPaymentsInstructions { - accounts: adjusted_accounts, + Ok(OutboundPaymentsInstructions { + affordable_accounts: adjusted_accounts, response_skeleton_opt, + agent, }) } - implement_as_any!(); + as_any_in_trait_impl!(); } impl Default for PaymentAdjusterReal { @@ -162,22 +162,20 @@ impl PaymentAdjusterReal { } fn determine_transaction_count_limit_by_transaction_fee( - tech_info: &FinancialAndTechDetails, + agent: &dyn BlockchainAgent, required_tx_count: usize, logger: &Logger, ) -> Result, PaymentAdjusterError> { - let transaction_fee_required_per_transaction_major = - tech_info.estimated_gas_limit_per_transaction as u128 - * tech_info.agreed_transaction_fee_per_computed_unit_major as u128; - - let per_transaction_requirement_minor: u128 = - gwei_to_wei(transaction_fee_required_per_transaction_major); + let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction(); - let cw_transaction_fee_balance_minor = - tech_info.consuming_wallet_balances.transaction_fee_minor; + let cw_transaction_fee_balance_minor = agent + .consuming_wallet_balances() + .transaction_fee_balance_in_minor_units; - let max_doable_tx_count = - cw_transaction_fee_balance_minor / per_transaction_requirement_minor; + let max_doable_tx_count = u128::try_from( + cw_transaction_fee_balance_minor / U256::from(per_transaction_requirement_minor), + ) + .expect("consuming wallet with too a big balance for the transaction fee"); let (max_doable_tx_count_u16, required_tx_count_u16) = Self::put_bigger_unsigned_integers_under_u16_ceiling( @@ -246,7 +244,7 @@ impl PaymentAdjusterReal { fn initialize_inner( &mut self, - setup: FinancialAndTechDetails, + cw_service_fee_balance: u128, required_adjustment: Adjustment, now: SystemTime, ) { @@ -256,10 +254,12 @@ impl PaymentAdjusterReal { } => Some(affordable_transaction_count), Adjustment::MasqToken => None, }; - let cw_masq_balance = setup.consuming_wallet_balances.masq_tokens_minor; - let inner = - PaymentAdjusterInnerReal::new(now, transaction_fee_limitation_opt, cw_masq_balance); + let inner = PaymentAdjusterInnerReal::new( + now, + transaction_fee_limitation_opt, + cw_service_fee_balance, + ); self.inner = Box::new(inner); } @@ -292,7 +292,7 @@ impl PaymentAdjusterReal { where A: AdjustmentRunner, { - diagnostics_for_collections( + collection_diagnostics( "\nUNRESOLVED QUALIFIED ACCOUNTS:", &unresolved_qualified_accounts, ); @@ -374,7 +374,9 @@ impl PaymentAdjusterReal { let downstream_decided_iter = downstream_decided_accounts.into_iter(); let merged: Vec = here_decided_iter.chain(downstream_decided_iter).collect(); - diagnostics_for_collections("\nFINAL ADJUSTED ACCOUNTS:", &merged); + + collection_diagnostics("\nFINAL ADJUSTED ACCOUNTS:", &merged); + merged } @@ -617,7 +619,7 @@ impl PaymentAdjusterReal { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Adjustment { MasqToken, PriorityTransactionFee { affordable_transaction_count: u16 }, @@ -634,7 +636,7 @@ pub enum AnalysisError { NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: usize, per_transaction_requirement_minor: u128, - cw_transaction_fee_balance_minor: u128, + cw_transaction_fee_balance_minor: U256, }, RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: usize, @@ -682,9 +684,115 @@ impl Display for PaymentAdjusterError { } } +// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// +// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; +// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; +// use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +// use masq_lib::logger::Logger; +// use std::time::SystemTime; +// +// pub trait PaymentAdjuster { +// fn search_for_indispensable_adjustment( +// &self, +// msg: &BlockchainAgentWithContextMessage, +// logger: &Logger, +// ) -> Result, AnalysisError>; +// +// fn adjust_payments( +// &self, +// setup: PreparedAdjustment, +// now: SystemTime, +// logger: &Logger, +// ) -> OutboundPaymentsInstructions; +// +// as_any_in_trait!(); +// } +// +// pub struct PaymentAdjusterReal {} +// +// impl PaymentAdjuster for PaymentAdjusterReal { +// fn search_for_indispensable_adjustment( +// &self, +// _msg: &BlockchainAgentWithContextMessage, +// _logger: &Logger, +// ) -> Result, AnalysisError> { +// Ok(None) +// } +// +// fn adjust_payments( +// &self, +// _setup: PreparedAdjustment, +// _now: SystemTime, +// _logger: &Logger, +// ) -> OutboundPaymentsInstructions { +// todo!("this function is dead until the card GH-711 is played") +// } +// +// as_any_in_trait_impl!(); +// } +// +// impl PaymentAdjusterReal { +// pub fn new() -> Self { +// Self {} +// } +// } +// +// impl Default for PaymentAdjusterReal { +// fn default() -> Self { +// Self::new() +// } +// } +// +// #[derive(Debug, PartialEq, Eq, Clone, Copy)] +// pub enum Adjustment { +// MasqToken, +// TransactionFeeCurrency { limiting_count: u16 }, +// Both, +// } +// +// #[derive(Debug, PartialEq, Eq)] +// pub enum AnalysisError {} +// +// #[cfg(test)] +// mod tests { +// use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; +// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; +// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; +// use crate::accountant::scanners::test_utils::protect_payables_in_test; +// use crate::accountant::test_utils::make_payable_account; +// use masq_lib::logger::Logger; +// use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; +// +// #[test] +// fn search_for_indispensable_adjustment_always_returns_none() { +// init_test_logging(); +// let test_name = "is_adjustment_required_always_returns_none"; +// let mut payable = make_payable_account(111); +// payable.balance_wei = 100_000_000; +// let agent = BlockchainAgentMock::default(); +// let setup_msg = BlockchainAgentWithContextMessage { +// protected_qualified_payables: protect_payables_in_test(vec![payable]), +// agent: Box::new(agent), +// response_skeleton_opt: None, +// }; +// let logger = Logger::new(test_name); +// let subject = PaymentAdjusterReal::new(); +// +// let result = subject.search_for_indispensable_adjustment(&setup_msg, &logger); +// +// assert_eq!(result, Ok(None)); +// TestLogHandler::default().exists_no_log_containing(test_name); +// // Nobody in this test asked about the wallet balances and the transaction fee +// // requirement, yet we got through with the final None. +// // How do we know? The mock agent didn't blow up while missing these +// // results +// } +// } + #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; @@ -695,15 +803,9 @@ mod tests { use crate::accountant::payment_adjuster::{ Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; - use crate::accountant::scanners::payable_scan_setup_msgs::{ - FinancialAndTechDetails, PayablePaymentSetup, StageData, - }; - use crate::accountant::scanners::scan_mid_procedures::PreparedAdjustment; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ResponseSkeleton}; - use crate::sub_lib::blockchain_bridge::{ - ConsumingWalletBalances, OutcomingPaymentsInstructions, - }; + use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances, OutboundPaymentsInstructions}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; @@ -712,6 +814,12 @@ mod tests { use std::time::{Duration, SystemTime}; use std::{usize, vec}; use thousands::Separable; + use web3::types::U256; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_web3::BlockchainAgentWeb3; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; #[test] #[should_panic( @@ -736,7 +844,7 @@ mod tests { let logger = Logger::new(test_name); subject.logger = logger; // MASQ balance > payments - let msg_1 = make_payable_setup_msg_sent_from_blockchain_bridge( + let input_1 = make_qualified_payables_and_blockchain_agent_for_initial_check( Some(PayableBalancesAndCWBalanceTestConfig { balances_of_accounts: Either::Right(vec![ gwei_to_wei::(85), @@ -747,7 +855,7 @@ mod tests { None, ); // MASQ balance == payments - let msg_2 = make_payable_setup_msg_sent_from_blockchain_bridge( + let input_2 = make_qualified_payables_and_blockchain_agent_for_initial_check( Some(PayableBalancesAndCWBalanceTestConfig { balances_of_accounts: Either::Left(vec![85, 15]), cw_balance_major: 100, @@ -755,7 +863,7 @@ mod tests { None, ); // transaction fee balance > payments - let msg_3 = make_payable_setup_msg_sent_from_blockchain_bridge( + let input_3 = make_qualified_payables_and_blockchain_agent_for_initial_check( None, Some(TransactionFeeTestConfig { agreed_transaction_fee_per_computed_unit_major: 100, @@ -765,7 +873,7 @@ mod tests { }), ); // transaction fee balance == payments - let msg_4 = make_payable_setup_msg_sent_from_blockchain_bridge( + let input_4 = make_qualified_payables_and_blockchain_agent_for_initial_check( None, Some(TransactionFeeTestConfig { agreed_transaction_fee_per_computed_unit_major: 100, @@ -775,14 +883,17 @@ mod tests { }), ); - [msg_1, msg_2, msg_3, msg_4].into_iter().for_each(|msg| { - assert_eq!( - subject.search_for_indispensable_adjustment(&msg), - Ok(None), - "failed for msg {:?}", - msg - ) - }); + [input_1, input_2, input_3, input_4] + .into_iter() + .enumerate() + .for_each(|(idx, (qualified_payables, agent))| { + assert_eq!( + subject.search_for_indispensable_adjustment(&qualified_payables, &*agent), + Ok(None), + "failed for tested input number {:?}", + idx + 1 + ) + }); TestLogHandler::new().exists_no_log_containing(&format!("WARN: {test_name}:")); } @@ -797,17 +908,18 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; let number_of_accounts = 3; - let msg = make_payable_setup_msg_sent_from_blockchain_bridge( - masq_balances_setup_opt, - Some(TransactionFeeTestConfig { - agreed_transaction_fee_per_computed_unit_major: 100, - number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, - cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, - }), - ); + let (qualified_payables, agent) = + make_qualified_payables_and_blockchain_agent_for_initial_check( + masq_balances_setup_opt, + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, + cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, + }), + ); - let result = subject.search_for_indispensable_adjustment(&msg); + let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!( result, @@ -833,18 +945,19 @@ mod tests { let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; - let msg = make_payable_setup_msg_sent_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceTestConfig { - balances_of_accounts: Either::Right(vec![ - gwei_to_wei::(85), - gwei_to_wei::(15) + 1, - ]), - cw_balance_major: 100, - }), - None, - ); + let (qualified_payables, agent) = + make_qualified_payables_and_blockchain_agent_for_initial_check( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Right(vec![ + gwei_to_wei::(85), + gwei_to_wei::(15) + 1, + ]), + cw_balance_major: 100, + }), + None, + ); - let result = subject.search_for_indispensable_adjustment(&msg); + let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); let log_handler = TestLogHandler::new(); @@ -864,12 +977,16 @@ mod tests { balances_of_accounts: Either::Left(vec![120, 300, 500]), cw_balance_major: masq_too_low_major, }); - let msg = make_payable_setup_msg_sent_from_blockchain_bridge(masq_balances_setup_opt, None); + let (qualified_payables, agent) = + make_qualified_payables_and_blockchain_agent_for_initial_check( + masq_balances_setup_opt, + None, + ); let mut subject = PaymentAdjusterReal::new(); let logger = Logger::new(test_name); subject.logger = logger; - let result = subject.search_for_indispensable_adjustment(&msg); + let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!( result, @@ -886,20 +1003,21 @@ mod tests { fn not_enough_transaction_fee_balance_for_even_a_single_transaction() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; - let msg = make_payable_setup_msg_sent_from_blockchain_bridge( - Some(PayableBalancesAndCWBalanceTestConfig { - balances_of_accounts: Either::Left(vec![123]), - cw_balance_major: 444, - }), - Some(TransactionFeeTestConfig { - agreed_transaction_fee_per_computed_unit_major: 100, - number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, - cw_transaction_fee_balance_major: 54_000 * 100, - }), - ); + let (qualified_payables, agent) = + make_qualified_payables_and_blockchain_agent_for_initial_check( + Some(PayableBalancesAndCWBalanceTestConfig { + balances_of_accounts: Either::Left(vec![123]), + cw_balance_major: 444, + }), + Some(TransactionFeeTestConfig { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, + cw_transaction_fee_balance_major: 54_000 * 100, + }), + ); - let result = subject.search_for_indispensable_adjustment(&msg); + let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!( result, @@ -907,7 +1025,8 @@ mod tests { AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts, per_transaction_requirement_minor: 55_000 * gwei_to_wei::(100), - cw_transaction_fee_balance_minor: 54_000 * gwei_to_wei::(100) + cw_transaction_fee_balance_minor: U256::from(54_000) + * gwei_to_wei::(100) } )) ); @@ -933,7 +1052,7 @@ mod tests { AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: 4, per_transaction_requirement_minor: 70_000_000_000_000, - cw_transaction_fee_balance_minor: 90_000, + cw_transaction_fee_balance_minor: U256::from(90_000), }, ), "Found smaller transaction fee balance than does for a single payment. \ @@ -1192,385 +1311,403 @@ mod tests { fn loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers( ) { init_test_logging(); - let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; - let now = SystemTime::now(); - // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria - let qualified_payables = { - let debt_age_in_months = vec![120, 120, 120]; - make_extreme_accounts( - Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), - now, - ) - }; - let mut subject = PaymentAdjusterReal::new(); - subject.logger = Logger::new(test_name); - // for change extremely small cw balance - let cw_masq_balance = 1_000; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u32::MAX as u128, - masq_tokens_minor: cw_masq_balance, - }, - estimated_gas_limit_per_transaction: 70_000, - agreed_transaction_fee_per_computed_unit_major: 120, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; - // normally, the initial feasibility check wouldn't allow this - assert_eq!(result.accounts, vec![]); - let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { - format!( - "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ - Seems unavoidable to disregard payable {wallet} at the moment. \ - Reason is the computed possible payment of {} wei \ - would not be at least half of the original debt {}", - proposed_adjusted_balance_in_this_iteration.separate_with_commas(), - (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() - ) - }; - let log_handler = TestLogHandler::new(); - // Notice that the proposals grow as one disqualified account drops in each iteration - log_handler.exists_log_containing(&expected_log( - "0x000000000000000000000000000000626c616830", - 333, - )); - log_handler.exists_log_containing(&expected_log( - "0x000000000000000000000000000000626c616831", - 499, - )); - log_handler.exists_log_containing(&expected_log( - "0x000000000000000000000000000000626c616832", - 1000, - )); + todo!("fix me") + // let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; + // let now = SystemTime::now(); + // // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria + // let qualified_payables = { + // let debt_age_in_months = vec![120, 120, 120]; + // make_extreme_accounts( + // Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), + // now, + // ) + // }; + // let mut subject = PaymentAdjusterReal::new(); + // subject.logger = Logger::new(test_name); + // // for change extremely small cw balance + // let cw_masq_balance = 1_000; + // let setup_msg = todo!() + // + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: u32::MAX as u128, + // // masq_tokens_minor: cw_masq_balance, + // // }, + // // estimated_gas_limit_per_transaction: 70_000, + // // agreed_transaction_fee_per_computed_unit_major: 120, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::MasqToken, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; + // // normally, the initial feasibility check wouldn't allow this + // assert_eq!(result.accounts, vec![]); + // let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { + // format!( + // "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ + // Seems unavoidable to disregard payable {wallet} at the moment. \ + // Reason is the computed possible payment of {} wei \ + // would not be at least half of the original debt {}", + // proposed_adjusted_balance_in_this_iteration.separate_with_commas(), + // (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() + // ) + // }; + // let log_handler = TestLogHandler::new(); + // // Notice that the proposals grow as one disqualified account drops in each iteration + // log_handler.exists_log_containing(&expected_log( + // "0x000000000000000000000000000000626c616830", + // 333, + // )); + // log_handler.exists_log_containing(&expected_log( + // "0x000000000000000000000000000000626c616831", + // 499, + // )); + // log_handler.exists_log_containing(&expected_log( + // "0x000000000000000000000000000000626c616832", + // 1000, + // )); } #[test] fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { init_test_logging(); - let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 4_444_444_444_444_444_444, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 6_000_000_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 6_666_666_666_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let mut subject = PaymentAdjusterReal::new(); - subject.logger = Logger::new(test_name); - let accounts_sum: u128 = - 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; - let consuming_wallet_masq_balance_minor = accounts_sum - 2_000_000_000_000_000_000; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u32::MAX as u128, - masq_tokens_minor: consuming_wallet_masq_balance_minor, - }, - estimated_gas_limit_per_transaction: 70_000, - agreed_transaction_fee_per_computed_unit_major: 120, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - let expected_criteria_computation_output = { - let account_1_adjusted = PayableAccount { - balance_wei: 3_918_231_688_187_775_576, - ..account_1 - }; - let account_2_adjusted = PayableAccount { - balance_wei: 5_921_593_128_688_275_336, - ..account_2 - }; - let account_3_adjusted = PayableAccount { - balance_wei: 5_271_286_293_568_393_532, - ..account_3 - }; - vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] - }; - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: expected_criteria_computation_output, - response_skeleton_opt: None - } - ); - let log_msg = format!( - "DEBUG: {test_name}: \n\ -|Payable Account Balance Wei -|---------------------------------------------------------- -|Successfully Adjusted Original -| Adjusted -| -|0x0000000000000000000000000000000000646566 6000000000000000000 -| 5921593128688275336 -|0x0000000000000000000000000000000000676869 6666666666000000000 -| 5271286293568393532 -|0x0000000000000000000000000000000000616263 4444444444444444444 -| 3918231688187775576" - ); - TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + todo!("fix me") + // let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; + // let now = SystemTime::now(); + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 4_444_444_444_444_444_444, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 6_000_000_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghi"), + // balance_wei: 6_666_666_666_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + // let mut subject = PaymentAdjusterReal::new(); + // subject.logger = Logger::new(test_name); + // let accounts_sum: u128 = + // 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; + // let consuming_wallet_masq_balance_minor = accounts_sum - 2_000_000_000_000_000_000; + // let setup_msg = todo!() + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: u32::MAX as u128, + // // masq_tokens_minor: consuming_wallet_masq_balance_minor, + // // }, + // // estimated_gas_limit_per_transaction: 70_000, + // // agreed_transaction_fee_per_computed_unit_major: 120, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // let expected_criteria_computation_output = { + // let account_1_adjusted = PayableAccount { + // balance_wei: 3_918_231_688_187_775_576, + // ..account_1 + // }; + // let account_2_adjusted = PayableAccount { + // balance_wei: 5_921_593_128_688_275_336, + // ..account_2 + // }; + // let account_3_adjusted = PayableAccount { + // balance_wei: 5_271_286_293_568_393_532, + // ..account_3 + // }; + // vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] + // }; + // assert_eq!( + // result, + // OutboundPaymentsInstructions { + // accounts: expected_criteria_computation_output, + // response_skeleton_opt: None + // } + // ); + // let log_msg = format!( + // "DEBUG: {test_name}: \n\ + // |Payable Account Balance Wei + // |---------------------------------------------------------- + // |Successfully Adjusted Original + // | Adjusted + // | + // |0x0000000000000000000000000000000000646566 6000000000000000000 + // | 5921593128688275336 + // |0x0000000000000000000000000000000000676869 6666666666000000000 + // | 5271286293568393532 + // |0x0000000000000000000000000000000000616263 4444444444444444444 + // | 3918231688187775576" + // ); + // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large( ) { init_test_logging(); - let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let mut subject = PaymentAdjusterReal::new(); - subject.logger = Logger::new(test_name); - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_minor: 10_u128.pow(22), - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: vec![account_3, account_2], - response_skeleton_opt: None - } - ); - let log_msg = format!( - "DEBUG: {test_name}: \n\ -|Payable Account Balance Wei -|---------------------------------------------------------- -|Successfully Adjusted Original -| Adjusted -| -|0x0000000000000000000000000000000000646566 333000000000000 -| 333000000000000 -|0x0000000000000000000000000000000000676869 222000000000000 -| 222000000000000 -| -|Ruled Out Original -| -|0x0000000000000000000000000000000000616263 111000000000000" - ); - TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + todo!("fix me") + // let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; + // let now = SystemTime::now(); + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 111_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghi"), + // balance_wei: 222_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + // let mut subject = PaymentAdjusterReal::new(); + // subject.logger = Logger::new(test_name); + // let setup_msg = todo!() + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, + // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // // masq_tokens_minor: 10_u128.pow(22), + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::PriorityTransactionFee { + // affordable_transaction_count: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // assert_eq!( + // result, + // OutboundPaymentsInstructions { + // accounts: vec![account_3, account_2], + // response_skeleton_opt: None + // } + // ); + // let log_msg = format!( + // "DEBUG: {test_name}: \n\ + // |Payable Account Balance Wei + // |---------------------------------------------------------- + // |Successfully Adjusted Original + // | Adjusted + // | + // |0x0000000000000000000000000000000000646566 333000000000000 + // | 333000000000000 + // |0x0000000000000000000000000000000000676869 222000000000000 + // | 222000000000000 + // | + // |Ruled Out Original + // | + // |0x0000000000000000000000000000000000616263 111000000000000" + // ); + // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn both_balances_are_insufficient_but_adjustment_by_masq_will_not_affect_the_accounts_count() { init_test_logging(); - let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; - let response_skeleton_opt = Some(ResponseSkeleton { - client_id: 123, - context_id: 321, - }); //just hardening, not so important - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_minor: consuming_wallet_masq_balance, - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - // account_1, the least important one, was eliminated for missing enough - // transaction fee balance - let expected_accounts = { - let account_2_adjusted = PayableAccount { - balance_wei: 222_000_000_000_000, - ..account_2 - }; - vec![account_3, account_2_adjusted] - }; - assert_eq!( - result, - OutcomingPaymentsInstructions { - accounts: expected_accounts, - response_skeleton_opt - } - ); + todo!("fix me") + // let now = SystemTime::now(); + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 111_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghk"), + // balance_wei: 222_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + // let mut subject = PaymentAdjusterReal::new(); + // let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; + // let response_skeleton_opt = Some(ResponseSkeleton { + // client_id: 123, + // context_id: 321, + // }); //just hardening, not so important + // let setup_msg = todo!() + // + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, + // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // // masq_tokens_minor: consuming_wallet_masq_balance, + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::PriorityTransactionFee { + // affordable_transaction_count: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // // account_1, the least important one, was eliminated for missing enough + // // transaction fee balance + // let expected_accounts = { + // let account_2_adjusted = PayableAccount { + // balance_wei: 222_000_000_000_000, + // ..account_2 + // }; + // vec![account_3, account_2_adjusted] + // }; + // assert_eq!( + // result, + // OutboundPaymentsInstructions { + // accounts: expected_accounts, + // response_skeleton_opt + // } + // ); } #[test] fn adjust_payments_when_only_masq_balance_limits_the_final_transaction_count() { init_test_logging(); - let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; - let now = SystemTime::now(); - let wallet_1 = make_wallet("def"); - // account to be adjusted up to maximum - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 333_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), - pending_payable_opt: None, - }; - // account to be outweighed and fully taken - let wallet_2 = make_wallet("abc"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 111_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), - pending_payable_opt: None, - }; - // account to be disqualified - let wallet_3 = make_wallet("ghk"); - let balance_3 = 600_000_000_000; - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; - let mut subject = PaymentAdjusterReal::new(); - subject.logger = Logger::new(test_name); - let consuming_wallet_masq_balance_minor = 333_000_000_000 + 50_000_000_000; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 5_000_000_000_000_000_000_000_000_u128, - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_minor: consuming_wallet_masq_balance_minor, - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt: Some(ResponseSkeleton { - client_id: 111, - context_id: 234, - }), - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - let expected_accounts = { - let account_1_adjusted = PayableAccount { - balance_wei: 272_000_000_000, - ..account_1 - }; - vec![account_1_adjusted, account_2] - }; - assert_eq!(result.accounts, expected_accounts); - assert_eq!( - result.response_skeleton_opt, - Some(ResponseSkeleton { - client_id: 111, - context_id: 234 - }) - ); - TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ - being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ - at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ - would not be at least half of the original debt 600,000,000,000")); + todo!("fix me") + // let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; + // let now = SystemTime::now(); + // let wallet_1 = make_wallet("def"); + // // account to be adjusted up to maximum + // let account_1 = PayableAccount { + // wallet: wallet_1.clone(), + // balance_wei: 333_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), + // pending_payable_opt: None, + // }; + // // account to be outweighed and fully taken + // let wallet_2 = make_wallet("abc"); + // let account_2 = PayableAccount { + // wallet: wallet_2.clone(), + // balance_wei: 111_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), + // pending_payable_opt: None, + // }; + // // account to be disqualified + // let wallet_3 = make_wallet("ghk"); + // let balance_3 = 600_000_000_000; + // let account_3 = PayableAccount { + // wallet: wallet_3.clone(), + // balance_wei: balance_3, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; + // let mut subject = PaymentAdjusterReal::new(); + // subject.logger = Logger::new(test_name); + // let consuming_wallet_masq_balance_minor = 333_000_000_000 + 50_000_000_000; + // let setup_msg = todo!() + // + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: 5_000_000_000_000_000_000_000_000_u128, + // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // // masq_tokens_minor: consuming_wallet_masq_balance_minor, + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt: Some(ResponseSkeleton { + // // client_id: 111, + // // context_id: 234, + // // }), + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::MasqToken, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // let expected_accounts = { + // let account_1_adjusted = PayableAccount { + // balance_wei: 272_000_000_000, + // ..account_1 + // }; + // vec![account_1_adjusted, account_2] + // }; + // assert_eq!(result.accounts, expected_accounts); + // assert_eq!( + // result.response_skeleton_opt, + // Some(ResponseSkeleton { + // client_id: 111, + // context_id: 234 + // }) + // ); + // TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ + // being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ + // at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ + // would not be at least half of the original debt 600,000,000,000")); } struct CompetitiveAccountsTestInputs<'a> { @@ -1618,29 +1755,37 @@ mod tests { }; let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u128::MAX, - masq_tokens_minor: consuming_wallet_balance_minor, - }, - estimated_gas_limit_per_transaction: 55_000, - agreed_transaction_fee_per_computed_unit_major: 150, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; + let setup_msg = todo!() + // PayablePaymentSetup { + // qualified_payables, + // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // transaction_fee_minor: u128::MAX, + // masq_tokens_minor: consuming_wallet_balance_minor, + // }, + // estimated_gas_limit_per_transaction: 55_000, + // agreed_transaction_fee_per_computed_unit_major: 150, + // }, + // )), + // response_skeleton_opt: None, + // } + ; + + let adjustment_setup = + todo!() + // PreparedAdjustment { + // qualified_payables, + // agent: Box::new(()), + // adjustment: Adjustment::MasqToken, + // response_skeleton_opt: None, + // ) + ; let mut result = subject .adjust_payments(adjustment_setup, now) .unwrap() - .accounts; + .affordable_accounts; let winning_account = result.remove(0); assert_eq!( @@ -1728,233 +1873,242 @@ mod tests { #[test] fn adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count() { init_test_logging(); - let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; - let now = SystemTime::now(); - // Thrown away as the second for the proposed balance insignificance - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 10_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - // Thrown away as the first one for the proposed balance insignificance - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 55_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - let wallet_3 = make_wallet("ghi"); - let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2, account_3.clone()]; - let mut subject = PaymentAdjusterReal::new(); - subject.logger = Logger::new(test_name); - let consuming_wallet_masq_balance = 300_000_000_000_000_u128; - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_minor: consuming_wallet_masq_balance, - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - assert_eq!( - result.accounts, - vec![PayableAccount { - wallet: wallet_3, - balance_wei: consuming_wallet_masq_balance, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }] - ); - assert_eq!(result.response_skeleton_opt, None); - let log_msg = format!( - "DEBUG: {test_name}: \n\ -|Payable Account Balance Wei -|---------------------------------------------------------- -|Successfully Adjusted Original -| Adjusted -| -|0x0000000000000000000000000000000000676869 333000000000000 -| 300000000000000 -| -|Ruled Out Original -| -|0x0000000000000000000000000000000000616263 10000000000000 -|0x0000000000000000000000000000000000646566 55000000000" - ); - TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + todo!("fix me") + // let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; + // let now = SystemTime::now(); + // // Thrown away as the second for the proposed balance insignificance + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 10_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + // pending_payable_opt: None, + // }; + // // Thrown away as the first one for the proposed balance insignificance + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 55_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + // pending_payable_opt: None, + // }; + // let wallet_3 = make_wallet("ghi"); + // let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); + // let account_3 = PayableAccount { + // wallet: wallet_3.clone(), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: last_paid_timestamp_3, + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2, account_3.clone()]; + // let mut subject = PaymentAdjusterReal::new(); + // subject.logger = Logger::new(test_name); + // let consuming_wallet_masq_balance = 300_000_000_000_000_u128; + // let setup_msg = todo!() + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, + // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // // masq_tokens_minor: consuming_wallet_masq_balance, + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::PriorityTransactionFee { + // affordable_transaction_count: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // assert_eq!( + // result.accounts, + // vec![PayableAccount { + // wallet: wallet_3, + // balance_wei: consuming_wallet_masq_balance, + // last_paid_timestamp: last_paid_timestamp_3, + // pending_payable_opt: None, + // }] + // ); + // assert_eq!(result.response_skeleton_opt, None); + // let log_msg = format!( + // "DEBUG: {test_name}: \n\ + // |Payable Account Balance Wei + // |---------------------------------------------------------- + // |Successfully Adjusted Original + // | Adjusted + // | + // |0x0000000000000000000000000000000000676869 333000000000000 + // | 300000000000000 + // | + // |Ruled Out Original + // | + // |0x0000000000000000000000000000000000616263 10000000000000 + // |0x0000000000000000000000000000000000646566 55000000000" + // ); + // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient( ) { init_test_logging(); - let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; - let now = SystemTime::now(); - //this account gets eliminated in the transaction-fee cut - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2, account_3]; - let mut subject = PaymentAdjusterReal::new(); - // This is exactly the amount which will provoke an error - let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; - subject.logger = Logger::new(test_name); - let setup_msg = PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - // Gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - masq_tokens_minor: cw_masq_balance_minor, - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::PriorityTransactionFee { - affordable_transaction_count: 2, - }, - }; - - let result = subject.adjust_payments(adjustment_setup, now); - - assert_eq!( - result, - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: 2, - cw_masq_balance_minor - } - )) - ); - TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ - facing critical scarcity of MASQ balance. Operation will abort." - )); + todo!("fix me") + // let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; + // let now = SystemTime::now(); + // //this account gets eliminated in the transaction-fee cut + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 111_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 333_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + // pending_payable_opt: None, + // }; + // let account_3 = PayableAccount { + // wallet: make_wallet("ghi"), + // balance_wei: 222_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2, account_3]; + // let mut subject = PaymentAdjusterReal::new(); + // // This is exactly the amount which will provoke an error + // let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; + // subject.logger = Logger::new(test_name); + // let setup_msg = todo!() + // // PayablePaymentSetup { + // // qualified_payables, + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, + // // // Gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + // // masq_tokens_minor: cw_masq_balance_minor, + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::PriorityTransactionFee { + // affordable_transaction_count: 2, + // }, + // }; + // + // let result = subject.adjust_payments(adjustment_setup, now); + // + // assert_eq!( + // result, + // Err(PaymentAdjusterError::AnalysisError( + // AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + // number_of_accounts: 2, + // cw_masq_balance_minor + // } + // )) + // ); + // TestLogHandler::new().exists_log_containing(&format!( + // "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ + // facing critical scarcity of MASQ balance. Operation will abort." + // )); } #[test] fn the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies( ) { - let test_name = "the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies"; - let now = SystemTime::now(); - // Disqualified in the first iteration - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 10_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - // Disqualified in the second iteration - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 550_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), - pending_payable_opt: None, - }; - // Eventually picked fulfilling the keep condition and returned - let wallet_3 = make_wallet("ghi"); - let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let balance_3 = 100_000_000_000; - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: balance_3, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2, account_3.clone()]; - let mut subject = PaymentAdjusterReal::new(); - let logger = Logger::new(test_name); - subject.logger = logger.clone(); - // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, - // the final winning resolution of the third account will work out because of the only - // additional one. - // The strategies advance reversed. The initial check seeks the smallest account, - // the disqualification strategy always takes from the largest accounts first. - // As a result, we can forecast the chances if the adjustment would succeed, not having to - // move forward beyond the entry check. - let consuming_wallet_masq_balance = (balance_3 / 2) + 1; - let setup_msg = PayablePaymentSetup { - qualified_payables: qualified_payables.clone(), - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: u128::MAX, - masq_tokens_minor: consuming_wallet_masq_balance, - }, - estimated_gas_limit_per_transaction: 77_000, - agreed_transaction_fee_per_computed_unit_major: 24, - }, - )), - response_skeleton_opt: None, - }; - let adjustment_setup = PreparedAdjustment { - original_setup_msg: setup_msg, - adjustment: Adjustment::MasqToken, - }; - - let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( - &logger, - Either::Left(&qualified_payables), - consuming_wallet_masq_balance, - ); - let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - assert_eq!(check_result, Ok(true)); - assert_eq!( - adjustment_result.accounts, - vec![PayableAccount { - wallet: wallet_3, - balance_wei: consuming_wallet_masq_balance, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }] - ); - assert_eq!(adjustment_result.response_skeleton_opt, None); + todo!() + // let test_name = "the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies"; + // let now = SystemTime::now(); + // // Disqualified in the first iteration + // let account_1 = PayableAccount { + // wallet: make_wallet("abc"), + // balance_wei: 10_000_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + // pending_payable_opt: None, + // }; + // // Disqualified in the second iteration + // let account_2 = PayableAccount { + // wallet: make_wallet("def"), + // balance_wei: 550_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), + // pending_payable_opt: None, + // }; + // // Eventually picked fulfilling the keep condition and returned + // let wallet_3 = make_wallet("ghi"); + // let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); + // let balance_3 = 100_000_000_000; + // let account_3 = PayableAccount { + // wallet: wallet_3.clone(), + // balance_wei: balance_3, + // last_paid_timestamp: last_paid_timestamp_3, + // pending_payable_opt: None, + // }; + // let qualified_payables = vec![account_1, account_2, account_3.clone()]; + // let mut subject = PaymentAdjusterReal::new(); + // let logger = Logger::new(test_name); + // subject.logger = logger.clone(); + // // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, + // // the final winning resolution of the third account will work out because of the only + // // additional one. + // // The strategies advance reversed. The initial check seeks the smallest account, + // // the disqualification strategy always takes from the largest accounts first. + // // As a result, we can forecast the chances if the adjustment would succeed, not having to + // // move forward beyond the entry check. + // let consuming_wallet_masq_balance = (balance_3 / 2) + 1; + // let setup_msg = todo!() + // // PayablePaymentSetup { + // // qualified_payables: qualified_payables.clone(), + // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // // FinancialAndTechDetails { + // // consuming_wallet_balances: ConsumingWalletBalances { + // // transaction_fee_minor: u128::MAX, + // // masq_tokens_minor: consuming_wallet_masq_balance, + // // }, + // // estimated_gas_limit_per_transaction: 77_000, + // // agreed_transaction_fee_per_computed_unit_major: 24, + // // }, + // // )), + // // response_skeleton_opt: None, + // // } + // ; + // let adjustment_setup = PreparedAdjustment { + // original_setup_msg: setup_msg, + // adjustment: Adjustment::MasqToken, + // }; + // + // let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( + // &logger, + // Either::Left(&qualified_payables), + // consuming_wallet_masq_balance, + // ); + // let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // + // assert_eq!(check_result, Ok(true)); + // assert_eq!( + // adjustment_result.accounts, + // vec![PayableAccount { + // wallet: wallet_3, + // balance_wei: consuming_wallet_masq_balance, + // last_paid_timestamp: last_paid_timestamp_3, + // pending_payable_opt: None, + // }] + // ); + // assert_eq!(adjustment_result.response_skeleton_opt, None); } struct TransactionFeeTestConfig { @@ -1964,10 +2118,10 @@ mod tests { cw_transaction_fee_balance_major: u64, } - fn make_payable_setup_msg_sent_from_blockchain_bridge( + fn make_qualified_payables_and_blockchain_agent_for_initial_check( masq_balances_config_opt: Option, transaction_fee_config_opt: Option, - ) -> PayablePaymentSetup { + ) -> (Vec, Box) { let masq_balances_setup = match masq_balances_config_opt { Some(config) => config, None => PayableBalancesAndCWBalanceTestConfig { @@ -1976,7 +2130,7 @@ mod tests { }, }; - let cw_masq_balance_minor: u128 = gwei_to_wei(masq_balances_setup.cw_balance_major); + let cw_service_fee_minor: u128 = gwei_to_wei(masq_balances_setup.cw_balance_major); let balances_of_accounts_minor = match masq_balances_setup.balances_of_accounts { Either::Left(in_major) => in_major @@ -2018,22 +2172,32 @@ mod tests { }) .collect() }; - let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); + let estimated_transaction_fee_per_transaction = + (estimated_transaction_fee_limit_per_tx * desired_transaction_fee_price) as u128; - PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - FinancialAndTechDetails { - consuming_wallet_balances: ConsumingWalletBalances { - transaction_fee_minor: cw_transaction_fee_minor, - masq_tokens_minor: cw_masq_balance_minor, - }, - estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, - agreed_transaction_fee_per_computed_unit_major: desired_transaction_fee_price, - }, - )), - response_skeleton_opt: None, - } + let blockchain_agent = BlockchainAgentMock::default() + .consuming_wallet_balances_result(ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: cw_transaction_fee_minor, + service_fee_balance_in_minor_units: cw_service_fee_minor, + }) + .estimated_transaction_fee_per_transaction_result( + estimated_transaction_fee_per_transaction, + ); + // PayablePaymentSetup { + // qualified_payables, + // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( + // FinancialAndTechDetails { + // consuming_wallet_balances: ConsumingWalletBalances { + // transaction_fee_minor: cw_transaction_fee_minor, + // masq_tokens_minor: cw_masq_balance_minor, + // }, + // estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, + // agreed_transaction_fee_per_computed_unit_major: desired_transaction_fee_price, + // }, + // )), + // response_skeleton_opt: None, + // } + (qualified_payables, Box::new(blockchain_agent)) } } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index c47b82788..90141ffc2 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -2,7 +2,7 @@ #![cfg(test)] -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::test_utils::make_wallet; diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index 270aba73d..54dd3745a 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; use itertools::Itertools; @@ -41,7 +41,7 @@ impl MasqAdjustmentPossibilityVerifier { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/mod.rs new file mode 100644 index 000000000..16331e4bf --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/mod.rs @@ -0,0 +1,3 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod payable_scanner; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs new file mode 100644 index 000000000..0a4cad1c9 --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -0,0 +1,192 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; +use crate::sub_lib::wallet::Wallet; +use ethereum_types::U256; +use masq_lib::logger::Logger; + +#[derive(Clone)] +pub struct BlockchainAgentNull { + wallet: Wallet, + logger: Logger, +} + +impl BlockchainAgent for BlockchainAgentNull { + fn estimated_transaction_fee_per_transaction(&self) -> u128 { + self.log_function_call("estimated_transaction_fee_per_transaction()"); + 0 + } + + fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { + self.log_function_call("consuming_wallet_balances()"); + ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::zero(), + service_fee_balance_in_minor_units: 0, + } + } + + fn agreed_fee_per_computation_unit(&self) -> u64 { + self.log_function_call("agreed_fee_per_computation_unit()"); + 0 + } + + fn consuming_wallet(&self) -> &Wallet { + self.log_function_call("consuming_wallet()"); + &self.wallet + } + + fn pending_transaction_id(&self) -> U256 { + self.log_function_call("pending_transaction_id()"); + U256::zero() + } + + #[cfg(test)] + fn dup(&self) -> Box { + intentionally_blank!() + } + + #[cfg(test)] + as_any_in_trait_impl!(); +} + +impl BlockchainAgentNull { + pub fn new() -> Self { + Self { + wallet: Wallet::null(), + logger: Logger::new("BlockchainAgentNull"), + } + } + + fn log_function_call(&self, function_call: &str) { + error!( + self.logger, + "calling null version of {function_call} for BlockchainAgentNull will be without effect", + ); + } +} + +impl Default for BlockchainAgentNull { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_null::BlockchainAgentNull; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + use crate::sub_lib::wallet::Wallet; + + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use web3::types::U256; + + fn blockchain_agent_null_constructor_works(constructor: C) + where + C: Fn() -> BlockchainAgentNull, + { + init_test_logging(); + + let result = constructor(); + + assert_eq!(result.wallet, Wallet::null()); + warning!(result.logger, "blockchain_agent_null_constructor_works"); + TestLogHandler::default().exists_log_containing( + "WARN: BlockchainAgentNull: \ + blockchain_agent_null_constructor_works", + ); + } + + #[test] + fn blockchain_agent_null_constructor_works_for_new() { + blockchain_agent_null_constructor_works(BlockchainAgentNull::new) + } + + #[test] + fn blockchain_agent_null_constructor_works_for_default() { + blockchain_agent_null_constructor_works(BlockchainAgentNull::default) + } + + fn assert_error_log(test_name: &str, expected_operation: &str) { + TestLogHandler::default().exists_log_containing(&format!( + "ERROR: {test_name}: calling \ + null version of {expected_operation}() for BlockchainAgentNull \ + will be without effect" + )); + } + + #[test] + fn null_agent_estimated_transaction_fee_per_transaction() { + init_test_logging(); + let test_name = "null_agent_estimated_transaction_fee_per_transaction"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.estimated_transaction_fee_per_transaction(); + + assert_eq!(result, 0); + assert_error_log(test_name, "estimated_transaction_fee_per_transaction"); + } + + #[test] + fn null_agent_consuming_wallet_balances() { + init_test_logging(); + let test_name = "null_agent_consuming_wallet_balances"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.consuming_wallet_balances(); + + assert_eq!( + result, + ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::zero(), + service_fee_balance_in_minor_units: 0 + } + ); + assert_error_log(test_name, "consuming_wallet_balances") + } + + #[test] + fn null_agent_agreed_fee_per_computation_unit() { + init_test_logging(); + let test_name = "null_agent_agreed_fee_per_computation_unit"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.agreed_fee_per_computation_unit(); + + assert_eq!(result, 0); + assert_error_log(test_name, "agreed_fee_per_computation_unit") + } + + #[test] + fn null_agent_consuming_wallet() { + init_test_logging(); + let test_name = "null_agent_consuming_wallet"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.consuming_wallet(); + + assert_eq!(result, &Wallet::null()); + assert_error_log(test_name, "consuming_wallet") + } + + #[test] + fn null_agent_pending_transaction_id() { + init_test_logging(); + let test_name = "null_agent_pending_transaction_id"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.pending_transaction_id(); + + assert_eq!(result, U256::zero()); + assert_error_log(test_name, "pending_transaction_id"); + } +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs new file mode 100644 index 000000000..c60689122 --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; +use crate::sub_lib::wallet::Wallet; + +use web3::types::U256; + +#[derive(Debug, Clone)] +pub struct BlockchainAgentWeb3 { + gas_price_gwei: u64, + gas_limit_const_part: u64, + maximum_added_gas_margin: u64, + consuming_wallet: Wallet, + consuming_wallet_balances: ConsumingWalletBalances, + pending_transaction_id: U256, +} + +impl BlockchainAgent for BlockchainAgentWeb3 { + fn estimated_transaction_fee_per_transaction(&self) -> u128 { + let gas_price = self.gas_price_gwei as u128; + let max_gas_limit = (self.maximum_added_gas_margin + self.gas_limit_const_part) as u128; + gas_price * max_gas_limit + } + + fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { + self.consuming_wallet_balances + } + + fn agreed_fee_per_computation_unit(&self) -> u64 { + self.gas_price_gwei + } + + fn consuming_wallet(&self) -> &Wallet { + &self.consuming_wallet + } + + fn pending_transaction_id(&self) -> U256 { + self.pending_transaction_id + } +} + +// 64 * (64 - 12) ... std transaction has data of 64 bytes and 12 bytes are never used with us; +// each non-zero byte costs 64 units of gas +pub const WEB3_MAXIMAL_GAS_LIMIT_MARGIN: u64 = 3328; + +impl BlockchainAgentWeb3 { + pub fn new( + gas_price_gwei: u64, + gas_limit_const_part: u64, + consuming_wallet: Wallet, + consuming_wallet_balances: ConsumingWalletBalances, + pending_transaction_id: U256, + ) -> Self { + Self { + gas_price_gwei, + gas_limit_const_part, + consuming_wallet, + maximum_added_gas_margin: WEB3_MAXIMAL_GAS_LIMIT_MARGIN, + consuming_wallet_balances, + pending_transaction_id, + } + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_web3::{ + BlockchainAgentWeb3, WEB3_MAXIMAL_GAS_LIMIT_MARGIN, + }; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; + use crate::test_utils::make_wallet; + + use web3::types::U256; + + #[test] + fn constants_are_correct() { + assert_eq!(WEB3_MAXIMAL_GAS_LIMIT_MARGIN, 3328) + } + + #[test] + fn blockchain_agent_can_return_non_computed_input_values() { + let gas_price_gwei = 123; + let gas_limit_const_part = 44_000; + let consuming_wallet = make_wallet("abcde"); + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::from(456_789), + service_fee_balance_in_minor_units: 123_000_000, + }; + let pending_transaction_id = U256::from(777); + + let subject = BlockchainAgentWeb3::new( + gas_price_gwei, + gas_limit_const_part, + consuming_wallet.clone(), + consuming_wallet_balances, + pending_transaction_id, + ); + + assert_eq!(subject.agreed_fee_per_computation_unit(), gas_price_gwei); + assert_eq!(subject.consuming_wallet(), &consuming_wallet); + assert_eq!( + subject.consuming_wallet_balances(), + consuming_wallet_balances + ); + assert_eq!(subject.pending_transaction_id(), pending_transaction_id) + } + + #[test] + fn estimated_transaction_fee_works() { + let consuming_wallet = make_wallet("efg"); + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: Default::default(), + service_fee_balance_in_minor_units: Default::default(), + }; + let nonce = U256::from(55); + let agent = BlockchainAgentWeb3::new( + 444, + 77_777, + consuming_wallet, + consuming_wallet_balances, + nonce, + ); + + let result = agent.estimated_transaction_fee_per_transaction(); + + assert_eq!(agent.gas_limit_const_part, 77_777); + assert_eq!( + agent.maximum_added_gas_margin, + WEB3_MAXIMAL_GAS_LIMIT_MARGIN + ); + assert_eq!( + result, + (77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) as u128 * 444 + ); + } +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs new file mode 100644 index 000000000..e9779f891 --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::arbitrary_id_stamp_in_trait; +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; +use crate::sub_lib::wallet::Wallet; +use web3::types::U256; + +// Table of chains by +// +// a) adoption of the fee market (variations on "gas price") +// b) customizable limit of allowed computation ("gas limit") +// +// CHAINS a) | b) +//-------------------------------+------ +// Ethereum yes | yes +// Polygon yes | yes +// Qtum yes | yes +// NEO yes | no* +// Cardano no | yes +// Bitcoin yes | no + +//* defaulted limit + +pub trait BlockchainAgent: Send { + fn estimated_transaction_fee_per_transaction(&self) -> u128; + fn consuming_wallet_balances(&self) -> ConsumingWalletBalances; + fn agreed_fee_per_computation_unit(&self) -> u64; + fn consuming_wallet(&self) -> &Wallet; + fn pending_transaction_id(&self) -> U256; + + #[cfg(test)] + fn dup(&self) -> Box { + intentionally_blank!() + } + as_any_in_trait!(); + arbitrary_id_stamp_in_trait!(); +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs new file mode 100644 index 000000000..0b62c772e --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -0,0 +1,79 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod agent_null; +pub mod agent_web3; +pub mod blockchain_agent; +pub mod msgs; +pub mod test_utils; + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::Adjustment; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; +use crate::accountant::scanners::Scanner; +use crate::accountant::ResponseSkeleton; +use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +use actix::Message; +use itertools::Either; +use masq_lib::logger::Logger; + +pub trait MultistagePayableScanner: + Scanner + SolvencySensitivePaymentInstructor +where + BeginMessage: Message, + EndMessage: Message, +{ +} + +pub trait SolvencySensitivePaymentInstructor { + fn try_skipping_payment_adjustment( + &self, + msg: BlockchainAgentWithContextMessage, + logger: &Logger, + ) -> Option>; + + fn perform_payment_adjustment( + &mut self, + setup: PreparedAdjustment, + logger: &Logger, + ) -> Option; +} + +pub struct PreparedAdjustment { + pub qualified_payables: Vec, + pub agent: Box, + pub response_skeleton_opt: Option, + pub adjustment: Adjustment, +} + +impl PreparedAdjustment { + pub fn new( + qualified_payables: Vec, + agent: Box, + response_skeleton_opt: Option, + adjustment: Adjustment, + ) -> Self { + Self { + qualified_payables, + agent, + response_skeleton_opt, + adjustment, + } + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; + + impl Clone for PreparedAdjustment { + fn clone(&self) -> Self { + Self { + qualified_payables: self.qualified_payables.clone(), + agent: self.agent.dup(), + response_skeleton_opt: self.response_skeleton_opt, + adjustment: self.adjustment.clone(), + } + } + } +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/msgs.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/msgs.rs new file mode 100644 index 000000000..d4cd40be4 --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/msgs.rs @@ -0,0 +1,72 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::{ResponseSkeleton, SkeletonOptHolder}; +use actix::Message; +use masq_lib::type_obfuscation::Obfuscated; +use std::fmt::Debug; + +#[derive(Debug, Message, PartialEq, Eq, Clone)] +pub struct QualifiedPayablesMessage { + pub protected_qualified_payables: Obfuscated, + pub response_skeleton_opt: Option, +} + +impl QualifiedPayablesMessage { + pub(in crate::accountant) fn new( + protected_qualified_payables: Obfuscated, + response_skeleton_opt: Option, + ) -> Self { + Self { + protected_qualified_payables, + response_skeleton_opt, + } + } +} + +impl SkeletonOptHolder for QualifiedPayablesMessage { + fn skeleton_opt(&self) -> Option { + self.response_skeleton_opt + } +} + +#[derive(Message)] +pub struct BlockchainAgentWithContextMessage { + pub protected_qualified_payables: Obfuscated, + pub agent: Box, + pub response_skeleton_opt: Option, +} + +impl BlockchainAgentWithContextMessage { + pub fn new( + qualified_payables: Obfuscated, + blockchain_agent: Box, + response_skeleton_opt: Option, + ) -> Self { + Self { + protected_qualified_payables: qualified_payables, + agent: blockchain_agent, + response_skeleton_opt, + } + } +} + +#[cfg(test)] +mod tests { + + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + + impl Clone for BlockchainAgentWithContextMessage { + fn clone(&self) -> Self { + let original_agent_id = self.agent.arbitrary_id_stamp(); + let cloned_agent = + BlockchainAgentMock::default().set_arbitrary_id_stamp(original_agent_id); + Self { + protected_qualified_payables: self.protected_qualified_payables.clone(), + agent: Box::new(cloned_agent), + response_skeleton_opt: self.response_skeleton_opt, + } + } + } +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs new file mode 100644 index 000000000..a26e2d440 --- /dev/null +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; +use crate::sub_lib::wallet::Wallet; +use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; +use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; +use ethereum_types::U256; +use std::cell::RefCell; + +#[derive(Default)] +pub struct BlockchainAgentMock { + estimated_transaction_fee_per_transaction_results: RefCell>, + consuming_wallet_balances_results: RefCell>, + agreed_fee_per_computation_unit_results: RefCell>, + consuming_wallet_result_opt: Option, + pending_transaction_id_results: RefCell>, + arbitrary_id_stamp_opt: Option, +} + +impl BlockchainAgent for BlockchainAgentMock { + fn estimated_transaction_fee_per_transaction(&self) -> u128 { + self.estimated_transaction_fee_per_transaction_results + .borrow_mut() + .remove(0) + } + + fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { + self.consuming_wallet_balances_results + .borrow_mut() + .remove(0) + } + + fn agreed_fee_per_computation_unit(&self) -> u64 { + self.agreed_fee_per_computation_unit_results + .borrow_mut() + .remove(0) + } + + fn consuming_wallet(&self) -> &Wallet { + self.consuming_wallet_result_opt.as_ref().unwrap() + } + + fn pending_transaction_id(&self) -> U256 { + self.pending_transaction_id_results.borrow_mut().remove(0) + } + + fn dup(&self) -> Box { + intentionally_blank!() + } + + arbitrary_id_stamp_in_trait_impl!(); +} + +impl BlockchainAgentMock { + pub fn estimated_transaction_fee_per_transaction_result(self, result: u128) -> Self { + self.estimated_transaction_fee_per_transaction_results + .borrow_mut() + .push(result); + self + } + + pub fn consuming_wallet_balances_result(self, result: ConsumingWalletBalances) -> Self { + self.consuming_wallet_balances_results + .borrow_mut() + .push(result); + self + } + + pub fn agreed_fee_per_computation_unit_result(self, result: u64) -> Self { + self.agreed_fee_per_computation_unit_results + .borrow_mut() + .push(result); + self + } + + pub fn consuming_wallet_result(mut self, consuming_wallet_result: Wallet) -> Self { + self.consuming_wallet_result_opt = Some(consuming_wallet_result); + self + } + + pub fn pending_transaction_id_result(self, result: U256) -> Self { + self.pending_transaction_id_results + .borrow_mut() + .push(result); + self + } + + set_arbitrary_id_stamp_in_mock_impl!(); +} diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 8d4bd1b21..e7b25289f 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1,17 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod payable_scan_setup_msgs; -pub mod scan_mid_procedures; +pub mod mid_scan_msg_handling; pub mod scanners_utils; +pub mod test_utils; -use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDao, PendingPayable}; -use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDao; -use crate::accountant::database_access_objects::receivable_dao::ReceivableDao; -use crate::accountant::scanners::payable_scan_setup_msgs::{PayablePaymentSetup}; +use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDao}; +use crate::accountant::db_access_objects::pending_payable_dao::{PendingPayable, PendingPayableDao}; +use crate::accountant::db_access_objects::receivable_dao::ReceivableDao; use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; -use crate::accountant::scanners::scan_mid_procedures::{ - PreparedAdjustment, PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, -}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; @@ -33,14 +29,13 @@ use crate::accountant::{ ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayables, }; -use crate::accountant::database_access_objects::banned_dao::BannedDao; +use crate::accountant::db_access_objects::banned_dao::BannedDao; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; -use crate::blockchain::blockchain_interface::PayableTransactionError; use crate::sub_lib::accountant::{ DaoFactories, FinancialStatistics, PaymentThresholds, ScanIntervals, }; use crate::sub_lib::blockchain_bridge::{ - OutcomingPaymentsInstructions, + OutboundPaymentsInstructions, }; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; use crate::sub_lib::wallet::Wallet; @@ -60,9 +55,13 @@ use std::time::{Duration, SystemTime}; use time::format_description::parse; use time::OffsetDateTime; use web3::types::{TransactionReceipt, H256}; +use masq_lib::type_obfuscation::Obfuscated; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::{PreparedAdjustment, MultistagePayableScanner, SolvencySensitivePaymentInstructor}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{BlockchainAgentWithContextMessage, QualifiedPayablesMessage}; +use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; pub struct Scanners { - pub payable: Box>, + pub payable: Box>, pub pending_payable: Box>, pub receivable: Box>, } @@ -116,7 +115,7 @@ where fn mark_as_started(&mut self, timestamp: SystemTime); fn mark_as_ended(&mut self, logger: &Logger); - declare_as_any!(); + as_any_in_trait!(); } pub struct ScannerCommon { @@ -183,13 +182,13 @@ pub struct PayableScanner { pub payment_adjuster: Box, } -impl Scanner for PayableScanner { +impl Scanner for PayableScanner { fn begin_scan( &mut self, timestamp: SystemTime, response_skeleton_opt: Option, logger: &Logger, - ) -> Result { + ) -> Result { if let Some(timestamp) = self.scan_started_at() { return Err(BeginScanError::ScanAlreadyRunning(timestamp)); } @@ -217,11 +216,10 @@ impl Scanner for PayableScanner { "Chose {} qualified debts to pay", qualified_payables.len() ); - Ok(PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: None, - response_skeleton_opt, - }) + let protected_payables = self.protect_payables(qualified_payables); + let outgoing_msg = + QualifiedPayablesMessage::new(protected_payables, response_skeleton_opt); + Ok(outgoing_msg) } } } @@ -251,29 +249,35 @@ impl Scanner for PayableScanner { time_marking_methods!(Payables); - implement_as_any!(); + as_any_in_trait_impl!(); } -impl PayableScannerMiddleProcedures for PayableScanner { +impl SolvencySensitivePaymentInstructor for PayableScanner { fn try_skipping_payment_adjustment( &self, - msg: PayablePaymentSetup, + msg: BlockchainAgentWithContextMessage, logger: &Logger, - ) -> Option> { + ) -> Option> { + let protected = msg.protected_qualified_payables; + let unprotected = self.expose_payables(protected); + match self .payment_adjuster - .search_for_indispensable_adjustment(&msg) + .search_for_indispensable_adjustment(&unprotected, &*msg.agent) { Ok(adjustment_opt) => match adjustment_opt { - None => { - let blockchain_bridge_instructions = OutcomingPaymentsInstructions { - accounts: msg.qualified_payables, - response_skeleton_opt: msg.response_skeleton_opt, - }; - Some(Either::Left(blockchain_bridge_instructions)) - } + None => Some(Either::Left(OutboundPaymentsInstructions::new( + unprotected, + msg.agent, + msg.response_skeleton_opt, + ))), Some(adjustment) => { - let prepared_adjustment = PreparedAdjustment::new(msg, adjustment); + let prepared_adjustment = PreparedAdjustment::new( + unprotected, + msg.agent, + msg.response_skeleton_opt, + adjustment, + ); Some(Either::Right(prepared_adjustment)) } }, @@ -292,8 +296,9 @@ impl PayableScannerMiddleProcedures for PayableScanner { &mut self, setup: PreparedAdjustment, logger: &Logger, - ) -> Option { + ) -> Option { let now = SystemTime::now(); + //TODO should be PaymentAdjuster behind RefCell? match self.payment_adjuster.adjust_payments(setup, now) { Ok(instructions) => Some(instructions), Err(e) => { @@ -312,7 +317,7 @@ impl PayableScannerMiddleProcedures for PayableScanner { } } -impl PayableScannerWithMiddleProcedures for PayableScanner {} +impl MultistagePayableScanner for PayableScanner {} impl PayableScanner { pub fn new( @@ -394,26 +399,67 @@ impl PayableScanner { fn separate_id_triples_by_existent_and_nonexistent_fingerprints<'a>( &'a self, - sent_payments: &'a [&'a PendingPayable], + sent_payables: &'a [&'a PendingPayable], ) -> (Vec, Vec) { - let hashes = sent_payments + fn make_triples( + ((rowid_opt, hash), pending_payable): ((Option, H256), &PendingPayable), + ) -> PendingPayableTriple { + PendingPayableTriple::new(&pending_payable.recipient_wallet, hash, rowid_opt) + } + + let hashes = sent_payables .iter() .map(|pending_payable| pending_payable.hash) .collect::>(); - self.pending_payable_dao + + let sent_payables_sorted_by_hashes = sent_payables + .iter() + .sorted_by(|a, b| Ord::cmp(&a.hash, &b.hash)) + .copied() + .collect::>(); + + let rowid_pairs_sorted_by_hashes = self + .pending_payable_dao .fingerprints_rowids(&hashes) .into_iter() - .zip(sent_payments.iter()) - .map( - |((rowid_opt, hash), pending_payable)| PendingPayableTriple { - recipient: &pending_payable.recipient_wallet, - hash, - rowid_opt, - }, - ) + .sorted_by(|(_, hash_a), (_, hash_b)| Ord::cmp(&hash_a, &hash_b)) + .collect::, H256)>>(); + + Self::symmetry_check( + &sent_payables_sorted_by_hashes, + &rowid_pairs_sorted_by_hashes, + ); + + rowid_pairs_sorted_by_hashes + .into_iter() + .zip(sent_payables_sorted_by_hashes.into_iter()) + .map(make_triples) .partition(|pp_triple| pp_triple.rowid_opt.is_some()) } + fn symmetry_check( + sent_payables_sorted_by_hashes: &[&PendingPayable], + rowid_pairs_sorted_by_hashes: &[(Option, H256)], + ) { + let set_a = sent_payables_sorted_by_hashes + .iter() + .map(|pp| pp.hash) + .sorted() + .collect::>(); + let set_b = rowid_pairs_sorted_by_hashes + .iter() + .map(|(_, hash)| *hash) + .sorted() + .collect::>(); + if set_a != set_b { + panic!( + "Inconsistency in two data sets, they cannot be matched by hashes. Set A: {:?}, \ + set B: {:?}", + sent_payables_sorted_by_hashes, rowid_pairs_sorted_by_hashes + ) + } + } + fn mark_pending_payable(&self, sent_payments: &[&PendingPayable], logger: &Logger) { fn missing_fingerprints_msg(nonexistent: &[PendingPayableTriple]) -> String { format!( @@ -524,6 +570,14 @@ impl PayableScanner { panic!("{}", msg) }; } + + fn protect_payables(&self, payables: Vec) -> Obfuscated { + Obfuscated::obfuscate_vector(payables) + } + + fn expose_payables(&self, obfuscated: Obfuscated) -> Vec { + obfuscated.expose_vector() + } } pub struct PendingPayableScanner { @@ -595,7 +649,7 @@ impl Scanner for PendingP time_marking_methods!(PendingPayables); - implement_as_any!(); + as_any_in_trait_impl!(); } impl PendingPayableScanner { @@ -855,7 +909,7 @@ impl Scanner for ReceivableScanner { time_marking_methods!(Receivables); - implement_as_any!(); + as_any_in_trait_impl!(); } impl ReceivableScanner { @@ -1006,8 +1060,11 @@ pub struct PeriodicalScanScheduler { pub trait ScanScheduler { fn schedule(&self, ctx: &mut Context); + fn interval(&self) -> Duration { + intentionally_blank!() + } - declare_as_any!(); + as_any_in_trait!(); #[cfg(test)] fn as_any_mut(&mut self) -> &mut dyn Any; @@ -1019,8 +1076,11 @@ impl ScanScheduler for PeriodicalScanScheduler { // because scheduled scans don't respond let _ = self.handle.notify_later(T::default(), self.interval, ctx); } + fn interval(&self) -> Duration { + self.interval + } - implement_as_any!(); + as_any_in_trait_impl!(); #[cfg(test)] fn as_any_mut(&mut self) -> &mut dyn Any { @@ -1030,38 +1090,35 @@ impl ScanScheduler for PeriodicalScanScheduler { #[cfg(test)] mod tests { + use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDaoError}; + use crate::accountant::db_access_objects::pending_payable_dao::{ + PendingPayable, PendingPayableDaoError, + }; + use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; + use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PendingPayableTriple; + use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; + use crate::accountant::scanners::test_utils::protect_payables_in_test; use crate::accountant::scanners::{ BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, ScanSchedulers, Scanner, ScannerCommon, Scanners, }; use crate::accountant::test_utils::{ - assert_real_scan_schedulers, make_custom_payment_thresholds, make_payable_account, - make_payables, make_pending_payable_fingerprint, make_receivable_account, - BannedDaoFactoryMock, BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, - PayableScannerBuilder, PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, - PendingPayableDaoMock, PendingPayableScannerBuilder, ReceivableDaoFactoryMock, - ReceivableDaoMock, ReceivableScannerBuilder, + make_custom_payment_thresholds, make_payable_account, make_payables, + make_pending_payable_fingerprint, make_receivable_account, BannedDaoFactoryMock, + BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, + PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, + PendingPayableScannerBuilder, ReceivableDaoFactoryMock, ReceivableDaoMock, + ReceivableScannerBuilder, }; use crate::accountant::{ gwei_to_wei, PendingPayableId, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; - use std::cell::RefCell; - use std::ops::Sub; - use std::panic::{catch_unwind, AssertUnwindSafe}; - - use crate::accountant::database_access_objects::payable_dao::{ - PayableAccount, PayableDaoError, PendingPayable, - }; - use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoError; - use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; - use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; - use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; - use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::{Correct, Failed}; - use crate::blockchain::blockchain_interface::{ - BlockchainTransaction, PayableTransactionError, RpcPayableFailure, + use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; + use crate::blockchain::blockchain_interface::data_structures::{ + BlockchainTransaction, RpcPayablesFailure, }; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::{ @@ -1075,6 +1132,9 @@ mod tests { use masq_lib::messages::ScanType; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use regex::Regex; + use std::cell::RefCell; + use std::ops::Sub; + use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; @@ -1135,11 +1195,6 @@ mod tests { &payment_thresholds ); assert_eq!(payable_scanner.common.initiated_at_opt.is_some(), false); - payable_scanner - .payable_threshold_gauge - .as_any() - .downcast_ref::() - .unwrap(); assert_eq!( pending_payable_scanner.when_pending_too_long_sec, when_pending_too_long_sec @@ -1175,6 +1230,18 @@ mod tests { ); } + #[test] + fn protected_payables_can_be_cast_from_and_back_to_vec_of_payable_accounts_by_payable_scanner() + { + let initial_unprotected = vec![make_payable_account(123), make_payable_account(456)]; + let subject = PayableScannerBuilder::new().build(); + + let protected = subject.protect_payables(initial_unprotected.clone()); + let again_unprotected: Vec = subject.expose_payables(protected); + + assert_eq!(initial_unprotected, again_unprotected) + } + #[test] fn payable_scanner_can_initiate_a_scan() { init_test_logging(); @@ -1194,9 +1261,10 @@ mod tests { assert_eq!(timestamp, Some(now)); assert_eq!( result, - Ok(PayablePaymentSetup { - qualified_payables: qualified_payable_accounts.clone(), - this_stage_data_opt: None, + Ok(QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test( + qualified_payable_accounts.clone() + ), response_skeleton_opt: None, }) ); @@ -1263,7 +1331,7 @@ mod tests { let failure_payable_hash_2 = make_tx_hash(0xde); let failure_payable_rowid_2 = 126; let failure_payable_wallet_2 = make_wallet("hihihi"); - let failure_payable_2 = RpcPayableFailure { + let failure_payable_2 = RpcPayablesFailure { rpc_error: Error::InvalidResponse( "Learn how to write before you send your garbage!".to_string(), ), @@ -1278,8 +1346,8 @@ mod tests { let pending_payable_dao = PendingPayableDaoMock::default() .fingerprints_rowids_params(&fingerprints_rowids_params_arc) .fingerprints_rowids_result(vec![ - (Some(correct_payable_rowid_1), correct_payable_hash_1), (Some(correct_payable_rowid_3), correct_payable_hash_3), + (Some(correct_payable_rowid_1), correct_payable_hash_1), ]) .fingerprints_rowids_result(vec![( Some(failure_payable_rowid_2), @@ -1298,9 +1366,9 @@ mod tests { let logger = Logger::new(test_name); let sent_payable = SentPayables { payment_procedure_result: Ok(vec![ - Correct(correct_pending_payable_1), - Failed(failure_payable_2), - Correct(correct_pending_payable_3), + Ok(correct_pending_payable_1), + Err(failure_payable_2), + Ok(correct_pending_payable_3), ]), response_skeleton_opt: None, }; @@ -1354,11 +1422,139 @@ mod tests { )); } + #[test] + fn keeping_entries_consistent_and_aligned_is_so_important() { + let wallet_1 = make_wallet("abc"); + let hash_1 = make_tx_hash(123); + let wallet_2 = make_wallet("def"); + let hash_2 = make_tx_hash(345); + let wallet_3 = make_wallet("ghi"); + let hash_3 = make_tx_hash(546); + let wallet_4 = make_wallet("jkl"); + let hash_4 = make_tx_hash(678); + let pending_payables_owned = vec![ + PendingPayable::new(wallet_1.clone(), hash_1), + PendingPayable::new(wallet_2.clone(), hash_2), + PendingPayable::new(wallet_3.clone(), hash_3), + PendingPayable::new(wallet_4.clone(), hash_4), + ]; + let pending_payables_ref = pending_payables_owned + .iter() + .collect::>(); + let pending_payable_dao = PendingPayableDaoMock::new().fingerprints_rowids_result(vec![ + (Some(4), hash_4), + (Some(1), hash_1), + (Some(3), hash_3), + (Some(2), hash_2), + ]); + let subject = PayableScannerBuilder::new() + .pending_payable_dao(pending_payable_dao) + .build(); + + let (existent, nonexistent) = subject + .separate_id_triples_by_existent_and_nonexistent_fingerprints(&pending_payables_ref); + + assert_eq!( + existent, + vec![ + PendingPayableTriple::new(&wallet_1, hash_1, Some(1)), + PendingPayableTriple::new(&wallet_2, hash_2, Some(2)), + PendingPayableTriple::new(&wallet_3, hash_3, Some(3)), + PendingPayableTriple::new(&wallet_4, hash_4, Some(4)) + ] + ); + assert!(nonexistent.is_empty()) + } + + #[test] + fn symmetry_check_happy_path() { + let hash_1 = make_tx_hash(123); + let hash_2 = make_tx_hash(456); + let hash_3 = make_tx_hash(789); + let blockchain_bridge_returned_pending_payables = vec![ + PendingPayable::new(make_wallet("abc"), hash_1), + PendingPayable::new(make_wallet("def"), hash_2), + PendingPayable::new(make_wallet("ghi"), hash_3), + ]; + let bb_returned_p_payables_ref = blockchain_bridge_returned_pending_payables + .iter() + .collect::>(); + let rowids_and_hashes_from_fingerprints = + vec![(Some(3), hash_1), (Some(5), hash_2), (Some(6), hash_3)]; + + PayableScanner::symmetry_check( + &bb_returned_p_payables_ref, + &rowids_and_hashes_from_fingerprints, + ) + // No panic, test passed + } + #[test] #[should_panic( - expected = "Expected pending payable fingerprints for (tx: 0x0000000000000000000000000000000000000000000000000000000000000315, \ - to wallet: 0x000000000000000000000000000000626f6f6761), (tx: 0x000000000000000000000000000000000000000000000000000000000000007b, \ - to wallet: 0x00000000000000000000000000000061676f6f62) were not found; system unreliable" + expected = "Inconsistency in two data sets, they cannot be matched by hashes. \ + Set A: \ + [PendingPayable { recipient_wallet: Wallet { kind: Address(0x0000000000000000000000000000000000616263) }, \ + hash: 0x000000000000000000000000000000000000000000000000000000000000007b }, \ + PendingPayable { recipient_wallet: Wallet { kind: Address(0x0000000000000000000000000000000000646566) }, \ + hash: 0x00000000000000000000000000000000000000000000000000000000000001c8 }, \ + PendingPayable { recipient_wallet: Wallet { kind: Address(0x0000000000000000000000000000000000676869) }, \ + hash: 0x0000000000000000000000000000000000000000000000000000000000000315 }], \ + set B: \ + [(Some(3), 0x000000000000000000000000000000000000000000000000000000000000007b), \ + (Some(5), 0x0000000000000000000000000000000000000000000000000000000000000237), \ + (Some(6), 0x0000000000000000000000000000000000000000000000000000000000000315)]" + )] + fn symmetry_check_sad_path_for_intruder() { + let hash_1 = make_tx_hash(123); + let hash_2 = make_tx_hash(456); + let hash_3 = make_tx_hash(789); + let intruder = make_tx_hash(567); + let blockchain_bridge_returned_pending_payables = vec![ + PendingPayable::new(make_wallet("abc"), hash_1), + PendingPayable::new(make_wallet("def"), hash_2), + PendingPayable::new(make_wallet("ghi"), hash_3), + ]; + let bb_returned_p_payables_ref = blockchain_bridge_returned_pending_payables + .iter() + .collect::>(); + let rowids_and_hashes_from_fingerprints = + vec![(Some(3), hash_1), (Some(5), intruder), (Some(6), hash_3)]; + + PayableScanner::symmetry_check( + &bb_returned_p_payables_ref, + &rowids_and_hashes_from_fingerprints, + ) + } + + #[test] + fn symmetry_check_indifferent_to_wrong_order_on_the_input() { + let hash_1 = make_tx_hash(123); + let hash_2 = make_tx_hash(456); + let hash_3 = make_tx_hash(789); + let blockchain_bridge_returned_pending_payables = vec![ + PendingPayable::new(make_wallet("abc"), hash_1), + PendingPayable::new(make_wallet("def"), hash_2), + PendingPayable::new(make_wallet("ghi"), hash_3), + ]; + let bb_returned_p_payables_ref = blockchain_bridge_returned_pending_payables + .iter() + .collect::>(); + // Not in an ascending order + let rowids_and_hashes_from_fingerprints = + vec![(Some(3), hash_1), (Some(5), hash_3), (Some(6), hash_2)]; + + PayableScanner::symmetry_check( + &bb_returned_p_payables_ref, + &rowids_and_hashes_from_fingerprints, + ) + // No panic, test passed + } + + #[test] + #[should_panic( + expected = "Expected pending payable fingerprints for (tx: 0x000000000000000000000000000000000000000000000000000000000000007b, \ + to wallet: 0x00000000000000000000000000000061676f6f62), (tx: 0x0000000000000000000000000000000000000000000000000000000000000315, \ + to wallet: 0x000000000000000000000000000000626f6f6761) were not found; system unreliable" )] fn payable_scanner_panics_when_fingerprints_for_correct_payments_not_found() { let hash_1 = make_tx_hash(0x315); @@ -1373,7 +1569,7 @@ mod tests { .pending_payable_dao(pending_payable_dao) .build(); let sent_payable = SentPayables { - payment_procedure_result: Ok(vec![Correct(payment_1), Correct(payment_2)]), + payment_procedure_result: Ok(vec![Ok(payment_1), Ok(payment_2)]), response_skeleton_opt: None, }; @@ -1396,7 +1592,7 @@ mod tests { .pending_payable_dao(pending_payable_dao) .build(); let sent_payables = SentPayables { - payment_procedure_result: Ok(vec![Correct(payable_1), Correct(payable_2)]), + payment_procedure_result: Ok(vec![Ok(payable_1), Ok(payable_2)]), response_skeleton_opt: None, }; @@ -1430,10 +1626,7 @@ mod tests { hash_2, ); - // the panic captured in the shared body cannot take record in the test log collection, - // despite it does get "ERROR: ..." printed in the reality; - // for that reason we can look for evidences of no ERROR log, which would've spoken for - // missing fingerprints otherwise + // Missing fingerprints, being an additional issue, would provoke an error log, but not here. TestLogHandler::new().exists_no_log_containing(&format!("ERROR: {test_name}:")); } @@ -1516,7 +1709,7 @@ mod tests { Deleting fingerprints for failed transactions 0x00000000000000000000000000000000000000000000000000000000000015b3, \ 0x0000000000000000000000000000000000000000000000000000000000003039", )); - //we haven't supplied any result for mark_pending_payable() and so it's proved uncalled + // we haven't supplied any result for mark_pending_payable() and so it's proved uncalled } #[test] @@ -1580,16 +1773,16 @@ mod tests { 00000000000000000000000000000000000000000315 failed due to RecordDeletion(\"Gosh, I overslept \ without an alarm set\")"); let log_handler = TestLogHandler::new(); - // there is a situation when we also stumble over missing fingerprints and so we log this - // actuality. Here we don't and so that ERROR log shouldn't be there + // There is a possible situation when we stumble over missing fingerprints and so we log it. + // Here we don't and so any ERROR log shouldn't turn up log_handler.exists_no_log_containing(&format!("ERROR: {}", test_name)) } #[test] - fn payable_scanner_panics_for_missing_fingerprints_of_some_failed_payments_but_deletion_works() - { + fn payable_scanner_panics_for_missing_fingerprints_but_deletion_of_some_works() { init_test_logging(); - let test_name = "payable_scanner_panics_for_missing_fingerprints_of_some_failed_payments_but_deletion_works"; + let test_name = + "payable_scanner_panics_for_missing_fingerprints_but_deletion_of_some_works"; let hash_1 = make_tx_hash(0x1b669); let hash_2 = make_tx_hash(0x3039); let hash_3 = make_tx_hash(0x223d); @@ -1630,11 +1823,12 @@ mod tests { } #[test] - fn payable_scanner_failed_txs_with_fingerprint_missing_and_deletion_of_the_other_one_fails() { - // two fatal failures, missing fingerprints and fingerprint deletion error are both legitimate - // reasons for panic + fn payable_scanner_for_failed_rpcs_one_fingerprint_missing_and_deletion_of_the_other_one_fails() + { + // Two fatal failures at once, missing fingerprints and fingerprint deletion error are both + // legitimate reasons for panic init_test_logging(); - let test_name = "payable_scanner_failed_txs_with_fingerprint_missing_and_deletion_of_the_other_one_fails"; + let test_name = "payable_scanner_for_failed_rpcs_one_fingerprint_missing_and_deletion_of_the_other_one_fails"; let existent_record_hash = make_tx_hash(0xb26e); let nonexistent_record_hash = make_tx_hash(0x4d2); let pending_payable_dao = PendingPayableDaoMock::default() @@ -1648,12 +1842,12 @@ mod tests { let mut subject = PayableScannerBuilder::new() .pending_payable_dao(pending_payable_dao) .build(); - let failed_payment_1 = Failed(RpcPayableFailure { + let failed_payment_1 = Err(RpcPayablesFailure { rpc_error: Error::Unreachable, recipient_wallet: make_wallet("abc"), hash: existent_record_hash, }); - let failed_payment_2 = Failed(RpcPayableFailure { + let failed_payment_2 = Err(RpcPayablesFailure { rpc_error: Error::Internal, recipient_wallet: make_wallet("def"), hash: nonexistent_record_hash, @@ -1712,7 +1906,7 @@ mod tests { threshold_value, DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec ) - //no other method was called (absence of panic) and that means we returned early + // No panic and so no other method was called, which means an early return } #[test] @@ -2880,7 +3074,7 @@ mod tests { let logger = Logger::new(test_name); let log_handler = TestLogHandler::new(); - assert_elapsed_time_in_mark_as_ended::( + assert_elapsed_time_in_mark_as_ended::( &mut PayableScannerBuilder::new().build(), "Payables", test_name, @@ -2905,17 +3099,37 @@ mod tests { #[test] fn scan_schedulers_can_be_properly_initialized() { - let payable_interval = Duration::from_secs(240); - let pending_payable_interval = Duration::from_secs(300); - let receivable_interval = Duration::from_secs(360); let scan_intervals = ScanIntervals { - payable_scan_interval: payable_interval, - pending_payable_scan_interval: pending_payable_interval, - receivable_scan_interval: receivable_interval, + payable_scan_interval: Duration::from_secs(240), + pending_payable_scan_interval: Duration::from_secs(300), + receivable_scan_interval: Duration::from_secs(360), }; let result = ScanSchedulers::new(scan_intervals); - assert_real_scan_schedulers(&result, scan_intervals) + assert_eq!( + result + .schedulers + .get(&ScanType::Payables) + .unwrap() + .interval(), + scan_intervals.payable_scan_interval + ); + assert_eq!( + result + .schedulers + .get(&ScanType::PendingPayables) + .unwrap() + .interval(), + scan_intervals.pending_payable_scan_interval + ); + assert_eq!( + result + .schedulers + .get(&ScanType::Receivables) + .unwrap() + .interval(), + scan_intervals.receivable_scan_interval + ); } } diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs index 9a0f7a986..b006b922c 100644 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ b/node/src/accountant/scanners/payable_scan_setup_msgs.rs @@ -1,37 +1,37 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::ResponseSkeleton; -use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; -use actix::Message; - -#[derive(Debug, Message, PartialEq, Eq, Clone)] -pub struct PayablePaymentSetup { - //this field should stay private for anybody outside Accountant - pub(in crate::accountant) qualified_payables: Vec, - pub this_stage_data_opt: Option, - pub response_skeleton_opt: Option, -} - -impl From<(PayablePaymentSetup, StageData)> for PayablePaymentSetup { - fn from((previous_msg, this_stage_data): (PayablePaymentSetup, StageData)) -> Self { - PayablePaymentSetup { - qualified_payables: previous_msg.qualified_payables, - this_stage_data_opt: Some(this_stage_data), - response_skeleton_opt: previous_msg.response_skeleton_opt, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum StageData { - FinancialAndTechDetails(FinancialAndTechDetails), -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct FinancialAndTechDetails { - pub consuming_wallet_balances: ConsumingWalletBalances, - pub agreed_transaction_fee_per_computed_unit_major: u64, - //rather technical stuff below - pub estimated_gas_limit_per_transaction: u64, -} +// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// +// use crate::accountant::db_access_objects::payable_dao::PayableAccount; +// use crate::accountant::ResponseSkeleton; +// use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; +// use actix::Message; +// +// #[derive(Debug, Message, PartialEq, Eq, Clone)] +// pub struct PayablePaymentSetup { +// //this field should stay private for anybody outside Accountant +// pub(in crate::accountant) qualified_payables: Vec, +// pub this_stage_data_opt: Option, +// pub response_skeleton_opt: Option, +// } +// +// impl From<(PayablePaymentSetup, StageData)> for PayablePaymentSetup { +// fn from((previous_msg, this_stage_data): (PayablePaymentSetup, StageData)) -> Self { +// PayablePaymentSetup { +// qualified_payables: previous_msg.qualified_payables, +// this_stage_data_opt: Some(this_stage_data), +// response_skeleton_opt: previous_msg.response_skeleton_opt, +// } +// } +// } +// +// #[derive(Debug, PartialEq, Eq, Clone)] +// pub enum StageData { +// FinancialAndTechDetails(FinancialAndTechDetails), +// } +// +// #[derive(Debug, PartialEq, Eq, Clone)] +// pub struct FinancialAndTechDetails { +// pub consuming_wallet_balances: ConsumingWalletBalances, +// pub agreed_transaction_fee_per_computed_unit_major: u64, +// //rather technical stuff below +// pub estimated_gas_limit_per_transaction: u64, +// } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs index 8c10d11ae..a5958753f 100644 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ b/node/src/accountant/scanners/scan_mid_procedures.rs @@ -1,49 +1,49 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::payment_adjuster::Adjustment; -use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; -use crate::accountant::scanners::Scanner; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use actix::Message; -use itertools::Either; -use masq_lib::logger::Logger; - -pub trait PayableScannerWithMiddleProcedures: - Scanner + PayableScannerMiddleProcedures -where - BeginMessage: Message, - EndMessage: Message, -{ -} - -pub trait PayableScannerMiddleProcedures { - fn try_skipping_payment_adjustment( - &self, - _msg: PayablePaymentSetup, - _logger: &Logger, - ) -> Option> { - intentionally_blank!() - } - fn perform_payment_adjustment( - &mut self, - _setup: PreparedAdjustment, - _logger: &Logger, - ) -> Option { - intentionally_blank!() - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct PreparedAdjustment { - pub original_setup_msg: PayablePaymentSetup, - pub adjustment: Adjustment, -} - -impl PreparedAdjustment { - pub fn new(original_setup_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { - Self { - original_setup_msg, - adjustment, - } - } -} +// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// +// use crate::accountant::payment_adjuster::Adjustment; +// use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; +// use crate::accountant::scanners::Scanner; +// use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +// use actix::Message; +// use itertools::Either; +// use masq_lib::logger::Logger; +// +// pub trait PayableScannerWithMiddleProcedures: +// Scanner + PayableScannerMiddleProcedures +// where +// BeginMessage: Message, +// EndMessage: Message, +// { +// } +// +// pub trait PayableScannerMiddleProcedures { +// fn try_softly( +// &self, +// _msg: PayablePaymentSetup, +// _logger: &Logger, +// ) -> Result, String> { +// intentionally_blank!() +// } +// fn exacting_payments_instructions( +// &self, +// _setup: AwaitedAdjustment, +// _logger: &Logger, +// ) -> OutboundPaymentsInstructions { +// intentionally_blank!() +// } +// } +// +// #[derive(Debug, PartialEq, Eq)] +// pub struct AwaitedAdjustment { +// pub original_setup_msg: PayablePaymentSetup, +// pub adjustment: Adjustment, +// } +// +// impl AwaitedAdjustment { +// pub fn new(original_setup_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { +// Self { +// original_setup_msg, +// adjustment, +// } +// } +// } diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 428ca7eb3..318aff52e 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -1,28 +1,25 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod payable_scanner_utils { - use crate::accountant::database_access_objects::utils::ThresholdUtils; - use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PayableDaoError, PendingPayable}; + use crate::accountant::db_access_objects::utils::ThresholdUtils; + use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDaoError}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; - use crate::accountant::{comma_joined_stringifiable, SentPayables}; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::{Correct, Failed}; - use crate::blockchain::blockchain_interface::{ - PayableTransactionError, ProcessedPayableFallible, RpcPayableFailure, - }; + use crate::accountant::{comma_joined_stringifiable, ProcessedPayableFallible, SentPayables}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use masq_lib::logger::Logger; - #[cfg(test)] - use std::any::Any; use std::cmp::Ordering; use std::ops::Not; use std::time::SystemTime; use thousands::Separable; use web3::types::H256; + use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; + use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; + use crate::blockchain::blockchain_interface::data_structures::{RpcPayablesFailure}; pub type VecOfRowidOptAndHash = Vec<(Option, H256)>; @@ -157,8 +154,8 @@ pub mod payable_scanner_utils { logger: &'b Logger, ) -> SeparateTxsByResult<'a> { match rpc_result { - Correct(pending_payable) => add_pending_payable(acc, pending_payable), - Failed(RpcPayableFailure { + Ok(pending_payable) => add_pending_payable(acc, pending_payable), + Err(RpcPayablesFailure { rpc_error, recipient_wallet, hash, @@ -223,12 +220,27 @@ pub mod payable_scanner_utils { } } + #[derive(Debug, PartialEq, Eq)] pub struct PendingPayableTriple<'a> { pub recipient: &'a Wallet, pub hash: H256, pub rowid_opt: Option, } + impl<'a> PendingPayableTriple<'a> { + pub fn new( + recipient: &'a Wallet, + hash: H256, + rowid_opt: Option, + ) -> PendingPayableTriple<'a> { + PendingPayableTriple { + recipient, + hash, + rowid_opt, + } + } + } + pub fn mark_pending_payable_fatal_error( sent_payments: &[&PendingPayable], nonexistent: &[PendingPayableTriple], @@ -279,7 +291,7 @@ pub mod payable_scanner_utils { payment_thresholds: &PaymentThresholds, x: u64, ) -> u128; - declare_as_any!(); + as_any_in_trait!(); } #[derive(Default)] @@ -301,7 +313,7 @@ pub mod payable_scanner_utils { ) -> u128 { ThresholdUtils::calculate_finite_debt_limit_by_age(payment_thresholds, debt_age) } - implement_as_any!(); + as_any_in_trait_impl!(); } } @@ -399,7 +411,7 @@ pub mod pending_payable_scanner_utils { } pub mod receivable_scanner_utils { - use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; + use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::wei_to_gwei; use std::time::{Duration, SystemTime}; use thousands::Separable; @@ -415,9 +427,9 @@ pub mod receivable_scanner_utils { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; - use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; - use crate::accountant::database_access_objects::receivable_dao::ReceivableAccount; + use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; + use crate::accountant::db_access_objects::payable_dao::{PayableAccount}; + use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; @@ -428,10 +440,6 @@ mod tests { }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::{checked_conversion, gwei_to_wei, SentPayables}; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::{Correct, Failed}; - use crate::blockchain::blockchain_interface::{ - BlockchainError, PayableTransactionError, RpcPayableFailure, - }; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; @@ -439,6 +447,9 @@ mod tests { use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::SystemTime; + use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; + use crate::blockchain::blockchain_interface::data_structures::errors::{BlockchainError, PayableTransactionError}; + use crate::blockchain::blockchain_interface::data_structures::{RpcPayablesFailure}; #[test] fn investigate_debt_extremes_picks_the_most_relevant_records() { @@ -508,8 +519,8 @@ mod tests { }; let sent_payable = SentPayables { payment_procedure_result: Ok(vec![ - Correct(correct_payment_1.clone()), - Correct(correct_payment_2.clone()), + Ok(correct_payment_1.clone()), + Ok(correct_payment_2.clone()), ]), response_skeleton_opt: None, }; @@ -550,16 +561,13 @@ mod tests { recipient_wallet: make_wallet("blah"), hash: make_tx_hash(123), }; - let bad_rpc_call = RpcPayableFailure { - rpc_error: web3::Error::InvalidResponse("that donkey screwed it up".to_string()), + let bad_rpc_call = RpcPayablesFailure { + rpc_error: web3::Error::InvalidResponse("That jackass screwed it up".to_string()), recipient_wallet: make_wallet("whooa"), hash: make_tx_hash(0x315), }; let sent_payable = SentPayables { - payment_procedure_result: Ok(vec![ - Correct(payable_ok.clone()), - Failed(bad_rpc_call.clone()), - ]), + payment_procedure_result: Ok(vec![Ok(payable_ok.clone()), Err(bad_rpc_call.clone())]), response_skeleton_opt: None, }; @@ -567,9 +575,10 @@ mod tests { assert_eq!(oks, vec![&payable_ok]); assert_eq!(errs, Some(RemotelyCausedErrors(vec![make_tx_hash(0x315)]))); - TestLogHandler::new().exists_log_containing("WARN: test_logger: Remote transaction failure: 'Got invalid response: \ - that donkey screwed it up' for payment to 0x00000000000000000000000000000077686f6f61 and transaction hash \ - 0x0000000000000000000000000000000000000000000000000000000000000315. Please check your blockchain service URL configuration."); + TestLogHandler::new().exists_log_containing("WARN: test_logger: Remote transaction failure: \ + 'Got invalid response: That jackass screwed it up' for payment to 0x000000000000000000000000\ + 00000077686f6f61 and transaction hash 0x0000000000000000000000000000000000000000000000000000\ + 000000000315. Please check your blockchain service URL configuration."); } #[test] @@ -755,7 +764,7 @@ mod tests { #[test] fn count_total_errors_says_unknown_number_for_early_local_errors() { let early_local_errors = [ - PayableTransactionError::TransactionCount(BlockchainError::QueryFailed( + PayableTransactionError::TransactionID(BlockchainError::QueryFailed( "blah".to_string(), )), PayableTransactionError::MissingConsumingWallet, diff --git a/node/src/accountant/scanners/test_utils.rs b/node/src/accountant/scanners/test_utils.rs new file mode 100644 index 000000000..c43d6f71b --- /dev/null +++ b/node/src/accountant/scanners/test_utils.rs @@ -0,0 +1,10 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use masq_lib::type_obfuscation::Obfuscated; + +pub fn protect_payables_in_test(payables: Vec) -> Obfuscated { + Obfuscated::obfuscate_vector(payables) +} diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index feb2ce9ba..5ed557c2c 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -2,21 +2,26 @@ #![cfg(test)] -use crate::accountant::database_access_objects::banned_dao::{BannedDao, BannedDaoFactory}; -use crate::accountant::database_access_objects::payable_dao::{ +use crate::accountant::db_access_objects::banned_dao::{BannedDao, BannedDaoFactory}; +use crate::accountant::db_access_objects::payable_dao::{ PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; -use crate::accountant::database_access_objects::pending_payable_dao::{ +use crate::accountant::db_access_objects::pending_payable_dao::{ PendingPayableDao, PendingPayableDaoError, PendingPayableDaoFactory, }; -use crate::accountant::database_access_objects::receivable_dao::{ +use crate::accountant::db_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; -use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; -use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError}; -use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; -use crate::accountant::scanners::scan_mid_procedures::{ - PayableScannerMiddleProcedures, PayableScannerWithMiddleProcedures, PreparedAdjustment, +use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; +use crate::accountant::payment_adjuster::{ + Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterError, +}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ + BlockchainAgentWithContextMessage, QualifiedPayablesMessage, +}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::{ + MultistagePayableScanner, PreparedAdjustment, SolvencySensitivePaymentInstructor, }; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; use crate::accountant::scanners::{ @@ -24,33 +29,33 @@ use crate::accountant::scanners::{ ReceivableScanner, ScanSchedulers, Scanner, }; use crate::accountant::{ - gwei_to_wei, Accountant, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, - ScanForReceivables, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, + gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; -use crate::blockchain::blockchain_interface::BlockchainTransaction; +use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; use crate::blockchain::test_utils::make_tx_hash; use crate::bootstrapper::BootstrapperConfig; use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; -use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics, ScanIntervals}; +use crate::sub_lib::accountant::{DaoFactories, FinancialStatistics}; use crate::sub_lib::accountant::{MessageIdGenerator, PaymentThresholds}; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; +use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +use crate::sub_lib::utils::NotifyLaterHandle; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; +use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::test_utils::unshared_test_utils::make_bc_with_defaults; use actix::{Message, System}; use ethereum_types::H256; +use itertools::Either; use masq_lib::logger::Logger; use masq_lib::messages::ScanType; use masq_lib::ui_gateway::NodeToUiMessage; -use masq_lib::utils::plus; use rusqlite::{Connection, Row}; use std::any::type_name; -use std::any::Any; use std::cell::RefCell; use std::fmt::Debug; +use std::ops::Deref; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; @@ -156,7 +161,7 @@ impl DaoWithDestination { fn guts_for_dao_factory_queue_initialization( customized_supplied_daos: &mut Vec>, - acc: Vec>, + mut acc: Vec>, used_input: usize, position: DestinationMarker, ) -> (Vec>, usize) { @@ -167,9 +172,13 @@ fn guts_for_dao_factory_queue_initialization( Some(idx) => { let customized_dao = customized_supplied_daos.remove(idx).into_inner(); let used_input_updated = used_input + 1; - (plus(acc, Box::new(customized_dao)), used_input_updated) + acc.push(Box::new(customized_dao)); + (acc, used_input_updated) + } + None => { + acc.push(Box::new(Default::default())); + (acc, used_input) } - None => (plus(acc, Box::new(Default::default())), used_input), } } @@ -201,39 +210,8 @@ fn fill_vacancies_with_given_or_default_daos( make_queue_for_factory } -macro_rules! create_or_update_factory { - ( - $dao_set: expr, //Vec> - $dao_initialization_order_in_regard_to_accountant: expr, //[DestinationMarker;N] - $factory_field_in_builder: ident, //Option - $dao_factory_mock: ident, // XxxDaoFactoryMock - $dao_trait: ident, - $self: expr //mut AccountantBuilder - ) => {{ - let make_queue_uncast = fill_vacancies_with_given_or_default_daos( - $dao_initialization_order_in_regard_to_accountant, - $dao_set, - ); - - let finished_make_queue: Vec> = make_queue_uncast - .into_iter() - .map(|elem| elem as Box) - .collect(); - - let ready_factory = match $self.$factory_field_in_builder.take() { - Some(existing_factory) => { - existing_factory.make_results.replace(finished_make_queue); - existing_factory - } - None => { - let mut new_factory = $dao_factory_mock::new(); - new_factory.make_results = RefCell::new(finished_make_queue); - new_factory - } - }; - $self.$factory_field_in_builder = Some(ready_factory); - $self - }}; +pub trait DaoFactoryWithMakeReplace { + fn replace_make_results(&self, results: Vec>); } const PAYABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER: [DestinationMarker; 3] = [ @@ -268,42 +246,72 @@ impl AccountantBuilder { mut self, specially_configured_daos: Vec>, ) -> Self { - create_or_update_factory!( + Self::create_or_update_factory( specially_configured_daos, PENDING_PAYABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - pending_payable_dao_factory, - PendingPayableDaoFactoryMock, - PendingPayableDao, - self - ) + &mut self.pending_payable_dao_factory, + PendingPayableDaoFactoryMock::new(), + ); + self } pub fn payable_daos( mut self, specially_configured_daos: Vec>, ) -> Self { - create_or_update_factory!( + Self::create_or_update_factory( specially_configured_daos, PAYABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - payable_dao_factory, - PayableDaoFactoryMock, - PayableDao, - self - ) + &mut self.payable_dao_factory, + PayableDaoFactoryMock::new(), + ); + self } pub fn receivable_daos( mut self, specially_configured_daos: Vec>, ) -> Self { - create_or_update_factory!( + Self::create_or_update_factory( specially_configured_daos, RECEIVABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - receivable_dao_factory, - ReceivableDaoFactoryMock, - ReceivableDao, - self - ) + &mut self.receivable_dao_factory, + ReceivableDaoFactoryMock::new(), + ); + self + } + + fn create_or_update_factory( + dao_set: Vec>, + dao_initialization_order_in_regard_to_accountant: [DestinationMarker; N], + factory_field_in_builder: &mut Option, + dao_factory_mock: DAOFactory, + ) where + DAO: Default, + DAOFactory: DaoFactoryWithMakeReplace, + { + let make_queue_uncast = fill_vacancies_with_given_or_default_daos( + dao_initialization_order_in_regard_to_accountant, + dao_set, + ); + + let finished_make_queue: Vec> = make_queue_uncast + .into_iter() + .map(|elem| elem as Box) + .collect(); + + let ready_factory = match factory_field_in_builder.take() { + Some(existing_factory) => { + existing_factory.replace_make_results(finished_make_queue); + existing_factory + } + None => { + let mut new_factory = dao_factory_mock; + new_factory.replace_make_results(finished_make_queue); + new_factory + } + }; + factory_field_in_builder.replace(ready_factory); } //TODO this method seems to be never used? @@ -365,7 +373,7 @@ impl AccountantBuilder { pub struct PayableDaoFactoryMock { make_params: Arc>>, - make_results: RefCell>>, + make_results: RefCell>>, } impl PayableDaoFactory for PayableDaoFactoryMock { @@ -380,6 +388,12 @@ impl PayableDaoFactory for PayableDaoFactoryMock { } } +impl DaoFactoryWithMakeReplace for PayableDaoFactoryMock { + fn replace_make_results(&self, results: Vec>) { + self.make_results.replace(results); + } +} + impl PayableDaoFactoryMock { pub fn new() -> Self { Self { @@ -399,9 +413,51 @@ impl PayableDaoFactoryMock { } } +pub struct PendingPayableDaoFactoryMock { + make_params: Arc>>, + make_results: RefCell>>, +} + +impl PendingPayableDaoFactory for PendingPayableDaoFactoryMock { + fn make(&self) -> Box { + if self.make_results.borrow().len() == 0 { + panic!( + "PendingPayableDao Missing. This problem mostly occurs when PendingPayableDao is only supplied for Accountant and not for the Scanner while building Accountant." + ) + }; + self.make_params.lock().unwrap().push(()); + self.make_results.borrow_mut().remove(0) + } +} + +impl DaoFactoryWithMakeReplace for PendingPayableDaoFactoryMock { + fn replace_make_results(&self, results: Vec>) { + self.make_results.replace(results); + } +} + +impl PendingPayableDaoFactoryMock { + pub fn new() -> Self { + Self { + make_params: Arc::new(Mutex::new(vec![])), + make_results: RefCell::new(vec![]), + } + } + + pub fn make_params(mut self, params: &Arc>>) -> Self { + self.make_params = params.clone(); + self + } + + pub fn make_result(self, result: PendingPayableDaoMock) -> Self { + self.make_results.borrow_mut().push(Box::new(result)); + self + } +} + pub struct ReceivableDaoFactoryMock { make_params: Arc>>, - make_results: RefCell>>, + make_results: RefCell>>, } impl ReceivableDaoFactory for ReceivableDaoFactoryMock { @@ -416,6 +472,12 @@ impl ReceivableDaoFactory for ReceivableDaoFactoryMock { } } +impl DaoFactoryWithMakeReplace for ReceivableDaoFactoryMock { + fn replace_make_results(&self, results: Vec>) { + self.make_results.replace(results); + } +} + impl ReceivableDaoFactoryMock { pub fn new() -> Self { Self { @@ -1016,42 +1078,6 @@ impl PendingPayableDaoMock { } } -pub struct PendingPayableDaoFactoryMock { - make_params: Arc>>, - make_results: RefCell>>, -} - -impl PendingPayableDaoFactory for PendingPayableDaoFactoryMock { - fn make(&self) -> Box { - if self.make_results.borrow().len() == 0 { - panic!( - "PendingPayableDao Missing. This problem mostly occurs when PendingPayableDao is only supplied for Accountant and not for the Scanner while building Accountant." - ) - }; - self.make_params.lock().unwrap().push(()); - self.make_results.borrow_mut().remove(0) - } -} - -impl PendingPayableDaoFactoryMock { - pub fn new() -> Self { - Self { - make_params: Arc::new(Mutex::new(vec![])), - make_results: RefCell::new(vec![]), - } - } - - pub fn make_params(mut self, params: &Arc>>) -> Self { - self.make_params = params.clone(); - self - } - - pub fn make_result(self, result: PendingPayableDaoMock) -> Self { - self.make_results.borrow_mut().push(Box::new(result)); - self - } -} - pub struct PayableScannerBuilder { payable_dao: PayableDaoMock, pending_payable_dao: PendingPayableDaoMock, @@ -1391,23 +1417,25 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { - search_for_indispensable_adjustment_params: Arc>>, + search_for_indispensable_adjustment_params: + Arc, ArbitraryIdStamp)>>>, search_for_indispensable_adjustment_results: RefCell, PaymentAdjusterError>>>, adjust_payments_params: Arc>>, adjust_payments_results: - RefCell>>, + RefCell>>, } impl PaymentAdjuster for PaymentAdjusterMock { fn search_for_indispensable_adjustment( &self, - msg: &PayablePaymentSetup, + qualified_payables: &[PayableAccount], + agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError> { self.search_for_indispensable_adjustment_params .lock() .unwrap() - .push(msg.clone()); + .push((qualified_payables.to_vec(), agent.arbitrary_id_stamp())); self.search_for_indispensable_adjustment_results .borrow_mut() .remove(0) @@ -1417,7 +1445,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { &mut self, setup: PreparedAdjustment, now: SystemTime, - ) -> Result { + ) -> Result { self.adjust_payments_params .lock() .unwrap() @@ -1429,7 +1457,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn search_for_indispensable_adjustment_params( mut self, - params: &Arc>>, + params: &Arc, ArbitraryIdStamp)>>>, ) -> Self { self.search_for_indispensable_adjustment_params = params.clone(); self @@ -1455,22 +1483,35 @@ impl PaymentAdjusterMock { pub fn adjust_payments_result( self, - result: Result, + result: Result, ) -> Self { self.adjust_payments_results.borrow_mut().push(result); self } } -pub fn make_initial_payable_payment_setup_message( - qualified_payables: Vec, - response_skeleton_opt: Option, -) -> PayablePaymentSetup { - PayablePaymentSetup { - qualified_payables, - this_stage_data_opt: None, - response_skeleton_opt, - } +macro_rules! formal_traits_for_payable_mid_scan_msg_handling { + ($scanner:ty) => { + impl MultistagePayableScanner for $scanner {} + + impl SolvencySensitivePaymentInstructor for $scanner { + fn try_skipping_payment_adjustment( + &self, + _msg: BlockchainAgentWithContextMessage, + _logger: &Logger, + ) -> Option> { + intentionally_blank!() + } + + fn perform_payment_adjustment( + &mut self, + _setup: PreparedAdjustment, + _logger: &Logger, + ) -> Option { + intentionally_blank!() + } + } + }; } pub struct NullScanner {} @@ -1505,12 +1546,10 @@ where panic!("Called mark_as_ended() from NullScanner"); } - implement_as_any!(); + as_any_in_trait_impl!(); } -impl PayableScannerWithMiddleProcedures for NullScanner {} - -impl PayableScannerMiddleProcedures for NullScanner {} +formal_traits_for_payable_mid_scan_msg_handling!(NullScanner); impl Default for NullScanner { fn default() -> Self { @@ -1599,7 +1638,7 @@ impl ScannerMock { self } - pub fn stop_the_system(self) -> Self { + pub fn stop_the_system_after_last_msg(self) -> Self { self.stop_system_after_last_message.replace(true); self } @@ -1621,12 +1660,7 @@ impl ScannerMock { } } -impl PayableScannerWithMiddleProcedures - for ScannerMock -{ -} - -impl PayableScannerMiddleProcedures for ScannerMock {} +formal_traits_for_payable_mid_scan_msg_handling!(ScannerMock); impl ScanSchedulers { pub fn update_scheduler( @@ -1650,54 +1684,3 @@ impl ScanSchedulers { } } } - -pub fn assert_real_scan_schedulers(subject: &ScanSchedulers, scan_intervals: ScanIntervals) { - let payable_scheduler = subject - .schedulers - .get(&ScanType::Payables) - .unwrap() - .as_any() - .downcast_ref::>() - .unwrap(); - assert_eq!( - payable_scheduler.interval, - scan_intervals.payable_scan_interval - ); - payable_scheduler - .handle - .as_any() - .downcast_ref::>() - .unwrap(); - let pending_payable_scheduler = subject - .schedulers - .get(&ScanType::PendingPayables) - .unwrap() - .as_any() - .downcast_ref::>() - .unwrap(); - assert_eq!( - pending_payable_scheduler.interval, - scan_intervals.pending_payable_scan_interval - ); - pending_payable_scheduler - .handle - .as_any() - .downcast_ref::>() - .unwrap(); - let receivable_scheduler = subject - .schedulers - .get(&ScanType::Receivables) - .unwrap() - .as_any() - .downcast_ref::>() - .unwrap(); - assert_eq!( - receivable_scheduler.interval, - scan_intervals.receivable_scan_interval - ); - receivable_scheduler - .handle - .as_any() - .downcast_ref::>() - .unwrap(); -} diff --git a/node/src/actor_system_factory.rs b/node/src/actor_system_factory.rs index 5265286bf..870460dd1 100644 --- a/node/src/actor_system_factory.rs +++ b/node/src/actor_system_factory.rs @@ -10,18 +10,14 @@ use super::stream_handler_pool::StreamHandlerPool; use super::stream_handler_pool::StreamHandlerPoolSubs; use super::stream_messages::PoolBindMessage; use super::ui_gateway::UiGateway; -use crate::accountant::database_access_objects::banned_dao::{ - BannedCacheLoader, BannedCacheLoaderReal, -}; -use crate::blockchain::blockchain_bridge::BlockchainBridge; +use crate::accountant::db_access_objects::banned_dao::{BannedCacheLoader, BannedCacheLoaderReal}; +use crate::blockchain::blockchain_bridge::{BlockchainBridge, BlockchainBridgeSubsFactoryReal}; use crate::bootstrapper::CryptDEPair; use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::{connection_or_panic, DbInitializer, DbInitializerReal}; use crate::db_config::persistent_configuration::PersistentConfiguration; use crate::node_configurator::configurator::Configurator; -use crate::sub_lib::accountant::{ - AccountantSubs, AccountantSubsFactory, AccountantSubsFactoryReal, DaoFactories, -}; +use crate::sub_lib::accountant::{AccountantSubs, AccountantSubsFactoryReal, DaoFactories}; use crate::sub_lib::blockchain_bridge::BlockchainBridgeSubs; use crate::sub_lib::configurator::ConfiguratorSubs; use crate::sub_lib::cryptde::CryptDE; @@ -71,18 +67,13 @@ impl ActorSystemFactory for ActorSystemFactoryReal { &self, config: BootstrapperConfig, actor_factory: Box, - persist_config: Box, + persistent_config: Box, ) -> StreamHandlerPoolSubs { - self.tools.validate_database_chain( - persist_config.as_ref(), - config.blockchain_bridge_config.chain, - ); - self.tools.prepare_initial_messages( - self.tools.cryptdes(), - config, - persist_config, - actor_factory, - ) + self.tools + .validate_database_chain(&*persistent_config, config.blockchain_bridge_config.chain); + let cryptdes = self.tools.cryptdes(); + self.tools + .prepare_initial_messages(cryptdes, config, persistent_config, actor_factory) } } @@ -157,7 +148,8 @@ impl ActorSystemFactoryTools for ActorSystemFactoryToolsReal { is_decentralized: config.neighborhood_config.mode.is_decentralized(), crashable: is_crashable(&config), }); - let blockchain_bridge_subs = actor_factory.make_and_start_blockchain_bridge(&config); + let blockchain_bridge_subs = actor_factory + .make_and_start_blockchain_bridge(&config, &BlockchainBridgeSubsFactoryReal {}); let neighborhood_subs = actor_factory.make_and_start_neighborhood(cryptdes.main, &config); let accountant_subs = actor_factory.make_and_start_accountant( config.clone(), @@ -370,7 +362,7 @@ pub trait ActorFactory { config: BootstrapperConfig, db_initializer: &dyn DbInitializer, banned_cache_loader: &dyn BannedCacheLoader, - accountant_subs_factory: &dyn AccountantSubsFactory, + subs_factory: &dyn SubsFactory, ) -> AccountantSubs; fn make_and_start_ui_gateway(&self, config: &BootstrapperConfig) -> UiGatewaySubs; fn make_and_start_stream_handler_pool( @@ -378,8 +370,11 @@ pub trait ActorFactory { config: &BootstrapperConfig, ) -> StreamHandlerPoolSubs; fn make_and_start_proxy_client(&self, config: ProxyClientConfig) -> ProxyClientSubs; - fn make_and_start_blockchain_bridge(&self, config: &BootstrapperConfig) - -> BlockchainBridgeSubs; + fn make_and_start_blockchain_bridge( + &self, + config: &BootstrapperConfig, + subs_factory: &dyn SubsFactory, + ) -> BlockchainBridgeSubs; fn make_and_start_configurator(&self, config: &BootstrapperConfig) -> ConfiguratorSubs; } @@ -449,7 +444,7 @@ impl ActorFactory for ActorFactoryReal { config: BootstrapperConfig, db_initializer: &dyn DbInitializer, banned_cache_loader: &dyn BannedCacheLoader, - accountant_subs_factory: &dyn AccountantSubsFactory, + subs_factory: &dyn SubsFactory, ) -> AccountantSubs { let data_directory = config.data_directory.as_path(); let payable_dao_factory = Box::new(Accountant::dao_factory(data_directory)); @@ -469,7 +464,7 @@ impl ActorFactory for ActorFactoryReal { }, ) }); - accountant_subs_factory.make(&addr) + subs_factory.make(&addr) } fn make_and_start_ui_gateway(&self, config: &BootstrapperConfig) -> UiGatewaySubs { @@ -502,6 +497,7 @@ impl ActorFactory for ActorFactoryReal { fn make_and_start_blockchain_bridge( &self, config: &BootstrapperConfig, + subs_factory: &dyn SubsFactory, ) -> BlockchainBridgeSubs { let blockchain_service_url_opt = config .blockchain_bridge_config @@ -510,14 +506,15 @@ impl ActorFactory for ActorFactoryReal { let crashable = is_crashable(config); let wallet_opt = config.consuming_wallet_opt.clone(); let data_directory = config.data_directory.clone(); - let chain_id = config.blockchain_bridge_config.chain; + let chain = config.blockchain_bridge_config.chain; let arbiter = Arbiter::builder().stop_system_on_panic(true); let addr: Addr = arbiter.start(move |_| { - let (blockchain_interface, persistent_config) = BlockchainBridge::make_connections( + let blockchain_interface = BlockchainBridge::initialize_blockchain_interface( blockchain_service_url_opt, - data_directory, - chain_id, + chain, ); + let persistent_config = + BlockchainBridge::initialize_persistent_configuration(&data_directory); BlockchainBridge::new( blockchain_interface, persistent_config, @@ -525,7 +522,7 @@ impl ActorFactory for ActorFactoryReal { wallet_opt, ) }); - BlockchainBridge::make_subs_from(&addr) + subs_factory.make(&addr) } fn make_and_start_configurator(&self, config: &BootstrapperConfig) -> ConfiguratorSubs { @@ -615,18 +612,26 @@ impl LogRecipientSetter for LogRecipientSetterReal { } } +// Test writing easing stuff. If further examination of the actor +// starting methods in ActorFactory is desirable. +// This allows to get the started actor's address and then messages +// can be sent to it, possibly the AssertionMessage. +pub trait SubsFactory +where + Actor: actix::Actor, +{ + fn make(&self, addr: &Addr) -> ActorSubs; +} + #[cfg(test)] mod tests { use super::*; - use crate::accountant::check_sqlite_fns::TestUserDefinedSqliteFnsForNewDelinquencies; - use crate::accountant::test_utils::bc_from_earning_wallet; + use crate::accountant::exportable_test_parts::test_accountant_is_constructed_with_upgraded_db_connection_recognizing_our_extra_sqlite_functions; use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; - use crate::actor_system_factory::tests::ShouldWeRunTheTest::{GoAhead, Skip}; + use crate::blockchain::blockchain_bridge::exportable_test_parts::test_blockchain_bridge_is_constructed_with_correctly_functioning_connections; use crate::bootstrapper::{Bootstrapper, RealUser}; - use crate::database::connection_wrapper::ConnectionWrapper; use crate::node_test_utils::{ - make_stream_handler_pool_subs_from, make_stream_handler_pool_subs_from_recorder, - start_recorder_refcell_opt, + make_stream_handler_pool_subs_from_recorder, start_recorder_refcell_opt, }; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::blockchain_bridge::BlockchainBridgeConfig; @@ -640,20 +645,23 @@ mod tests { use crate::sub_lib::peer_actors::StartMessage; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::ui_gateway::UiGatewayConfig; + use crate::test_utils::actor_system_factory::BannedCacheLoaderMock; use crate::test_utils::automap_mocks::{AutomapControlFactoryMock, AutomapControlMock}; use crate::test_utils::make_wallet; - use crate::test_utils::neighborhood_test_utils::MIN_HOPS_COUNT_FOR_TEST; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::{ - make_accountant_subs_from_recorder, make_blockchain_bridge_subs_from, - make_configurator_subs_from, make_hopper_subs_from, make_neighborhood_subs_from, - make_proxy_client_subs_from, make_proxy_server_subs_from, - make_ui_gateway_subs_from_recorder, Recording, + make_accountant_subs_from_recorder, make_blockchain_bridge_subs_from_recorder, + make_configurator_subs_from_recorder, make_hopper_subs_from_recorder, + make_neighborhood_subs_from_recorder, make_proxy_client_subs_from_recorder, + make_proxy_server_subs_from_recorder, make_ui_gateway_subs_from_recorder, Recording, }; use crate::test_utils::recorder::{make_recorder, Recorder}; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; - use crate::test_utils::unshared_test_utils::assert_on_initialization_with_panic_on_migration; use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; + use crate::test_utils::unshared_test_utils::{ + assert_on_initialization_with_panic_on_migration, SubsFactoryTestAddrLeaker, + }; use crate::test_utils::{alias_cryptde, rate_pack}; use crate::test_utils::{main_cryptde, make_cryptde_pair}; use crate::{hopper, proxy_client, proxy_server, stream_handler_pool, ui_gateway}; @@ -663,27 +671,20 @@ mod tests { use automap_lib::mocks::{ parameterizable_automap_control, TransactorMock, PUBLIC_IP, ROUTER_IP, }; - use crossbeam_channel::{bounded, unbounded, Sender}; + use crossbeam_channel::unbounded; use log::LevelFilter; use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::crash_point::CrashPoint; #[cfg(feature = "log_recipient_test")] use masq_lib::logger::INITIALIZATION_COUNTER; use masq_lib::messages::{ToMessageBody, UiCrashRequest, UiDescriptorRequest}; - use masq_lib::test_utils::utils::{ - check_if_source_code_is_attached, ensure_node_home_directory_exists, ShouldWeRunTheTest, - TEST_DEFAULT_CHAIN, - }; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; use masq_lib::ui_gateway::NodeFromUiMessage; use masq_lib::utils::running_test; use masq_lib::utils::AutomapProtocol::Igdp; - use regex::Regex; use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; - use std::env::current_dir; - use std::fs::File; - use std::io::{BufRead, BufReader}; use std::net::Ipv4Addr; use std::net::{IpAddr, SocketAddr, SocketAddrV4}; use std::path::PathBuf; @@ -797,17 +798,6 @@ mod tests { } } - #[derive(Default)] - struct BannedCacheLoaderMock { - pub load_params: Arc>>>, - } - - impl BannedCacheLoader for BannedCacheLoaderMock { - fn load(&self, conn: Box) { - self.load_params.lock().unwrap().push(conn); - } - } - struct ActorFactoryMock<'a> { dispatcher: RefCell>, proxy_client: RefCell>, @@ -856,7 +846,7 @@ mod tests { .unwrap() .get_or_insert((cryptdes, config.clone())); let addr: Addr = ActorFactoryMock::start_recorder(&self.proxy_server); - make_proxy_server_subs_from(&addr) + make_proxy_server_subs_from_recorder(&addr) } fn make_and_start_hopper(&self, config: HopperConfig) -> HopperSubs { @@ -866,7 +856,7 @@ mod tests { .unwrap() .get_or_insert(config); let addr: Addr = start_recorder_refcell_opt(&self.hopper); - make_hopper_subs_from(&addr) + make_hopper_subs_from_recorder(&addr) } fn make_and_start_neighborhood( @@ -880,7 +870,7 @@ mod tests { .unwrap() .get_or_insert((cryptde, config.clone())); let addr: Addr = start_recorder_refcell_opt(&self.neighborhood); - make_neighborhood_subs_from(&addr) + make_neighborhood_subs_from_recorder(&addr) } fn make_and_start_accountant( @@ -888,7 +878,7 @@ mod tests { config: BootstrapperConfig, _db_initializer: &dyn DbInitializer, _banned_cache_loader: &dyn BannedCacheLoader, - _accountant_subs_factory: &dyn AccountantSubsFactory, + _accountant_subs_factory: &dyn SubsFactory, ) -> AccountantSubs { self.parameters .accountant_params @@ -924,12 +914,13 @@ mod tests { .unwrap() .get_or_insert(config); let addr: Addr = start_recorder_refcell_opt(&self.proxy_client); - make_proxy_client_subs_from(&addr) + make_proxy_client_subs_from_recorder(&addr) } fn make_and_start_blockchain_bridge( &self, config: &BootstrapperConfig, + _subs_factory: &dyn SubsFactory, ) -> BlockchainBridgeSubs { self.parameters .blockchain_bridge_params @@ -937,7 +928,7 @@ mod tests { .unwrap() .get_or_insert(config.clone()); let addr: Addr = start_recorder_refcell_opt(&self.blockchain_bridge); - make_blockchain_bridge_subs_from(&addr) + make_blockchain_bridge_subs_from_recorder(&addr) } fn make_and_start_configurator(&self, config: &BootstrapperConfig) -> ConfiguratorSubs { @@ -947,7 +938,7 @@ mod tests { .unwrap() .get_or_insert(config.clone()); let addr: Addr = start_recorder_refcell_opt(&self.configurator); - make_configurator_subs_from(&addr) + make_configurator_subs_from_recorder(&addr) } } @@ -1050,6 +1041,107 @@ mod tests { } } + #[test] + fn make_and_start_actors_happy_path() { + let validate_database_chain_params_arc = Arc::new(Mutex::new(vec![])); + let prepare_initial_messages_params_arc = Arc::new(Mutex::new(vec![])); + let (stream_handler_pool, _, stream_handler_pool_recording_arc) = make_recorder(); + let main_cryptde = main_cryptde(); + let alias_cryptde = alias_cryptde(); + let cryptde_pair = CryptDEPair { + main: main_cryptde, + alias: alias_cryptde, + }; + let main_cryptde_public_key_expected = pk_from_cryptde_null(main_cryptde); + let alias_cryptde_public_key_expected = pk_from_cryptde_null(alias_cryptde); + let actor_factory = Box::new(ActorFactoryReal {}); + let actor_factory_raw_address_expected = addr_of!(*actor_factory); + let persistent_config_expected_arbitrary_id = ArbitraryIdStamp::new(); + let persistent_config = Box::new( + PersistentConfigurationMock::default() + .set_arbitrary_id_stamp(persistent_config_expected_arbitrary_id), + ); + let stream_holder_pool_subs = + make_stream_handler_pool_subs_from_recorder(&stream_handler_pool.start()); + let actor_system_factor_tools = ActorSystemFactoryToolsMock::default() + .validate_database_chain_params(&validate_database_chain_params_arc) + .cryptdes_result(cryptde_pair) + .prepare_initial_messages_params(&prepare_initial_messages_params_arc) + .prepare_initial_messages_result(stream_holder_pool_subs); + let data_dir = PathBuf::new().join("parent_directory/child_directory"); + let subject = ActorSystemFactoryReal::new(Box::new(actor_system_factor_tools)); + let mut bootstrapper_config = BootstrapperConfig::new(); + bootstrapper_config.blockchain_bridge_config.chain = Chain::PolyMainnet; + bootstrapper_config.data_directory = data_dir.clone(); + bootstrapper_config.db_password_opt = Some("password".to_string()); + + let result = + subject.make_and_start_actors(bootstrapper_config, actor_factory, persistent_config); + + let mut validate_database_chain_params = validate_database_chain_params_arc.lock().unwrap(); + let (persistent_config_actual_arbitrary_id, actual_chain) = + validate_database_chain_params.remove(0); + assert_eq!( + persistent_config_actual_arbitrary_id, + persistent_config_expected_arbitrary_id + ); + assert_eq!(actual_chain, Chain::PolyMainnet); + assert!(validate_database_chain_params.is_empty()); + let mut prepare_initial_messages_params = + prepare_initial_messages_params_arc.lock().unwrap(); + let ( + main_cryptde_actual, + alias_cryptde_actual, + bootstrapper_config_actual, + actor_factory_actual, + persistent_config_actual, + ) = prepare_initial_messages_params.remove(0); + let main_cryptde_public_key_actual = pk_from_cryptde_null(main_cryptde_actual.as_ref()); + assert_eq!( + main_cryptde_public_key_actual, + main_cryptde_public_key_expected + ); + let alias_cryptde_public_key_actual = pk_from_cryptde_null(alias_cryptde_actual.as_ref()); + assert_eq!( + alias_cryptde_public_key_actual, + alias_cryptde_public_key_expected + ); + assert_eq!(bootstrapper_config_actual.data_directory, data_dir); + assert_eq!( + bootstrapper_config_actual.db_password_opt, + Some("password".to_string()) + ); + assert_eq!( + addr_of!(*actor_factory_actual), + actor_factory_raw_address_expected + ); + assert_eq!( + persistent_config_actual.arbitrary_id_stamp(), + persistent_config_expected_arbitrary_id + ); + assert!(prepare_initial_messages_params.is_empty()); + verify_recipient(&result.node_from_ui_sub, &stream_handler_pool_recording_arc) + } + + fn verify_recipient( + recipient: &Recipient, + recording_arc: &Arc>, + ) { + let system = System::new("verifying_recipient_returned_in_test"); + let expected_msg = NodeFromUiMessage { + client_id: 5, + body: UiDescriptorRequest {}.tmb(1), + }; + + recipient.try_send(expected_msg.clone()).unwrap(); + + System::current().stop_with_code(0); + system.run(); + let recording = recording_arc.lock().unwrap(); + let actual_msg = recording.get_record::(0); + assert_eq!(actual_msg, &expected_msg); + } + #[test] fn make_and_start_actors_sends_bind_messages() { let actor_factory = ActorFactoryMock::new(); @@ -1084,13 +1176,14 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, payment_thresholds_opt: Some(PaymentThresholds::default()), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, }; - let persistent_config = - PersistentConfigurationMock::default().chain_name_result("eth-ropsten".to_string()); + let persistent_config = PersistentConfigurationMock::default() + .chain_name_result("eth-ropsten".to_string()) + .set_min_hops_result(Ok(())); Bootstrapper::pub_initialize_cryptdes_for_testing( &Some(main_cryptde()), &Some(alias_cryptde()), @@ -1158,7 +1251,7 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, payment_thresholds_opt: Default::default(), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC @@ -1276,6 +1369,7 @@ mod tests { let mut config = BootstrapperConfig::default(); config.neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![]), + min_hops: MIN_HOPS_FOR_TEST, }; let subject = ActorSystemFactoryToolsReal::new(); let state_before = INITIALIZATION_COUNTER.lock().unwrap().0; @@ -1302,7 +1396,7 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let make_params_arc = Arc::new(Mutex::new(vec![])); let mut subject = make_subject_with_null_setter(); @@ -1456,7 +1550,7 @@ mod tests { real_user: RealUser::null(), neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![]), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, payment_thresholds_opt: Default::default(), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC @@ -1468,7 +1562,7 @@ mod tests { let _ = subject.prepare_initial_messages( make_cryptde_pair(), config.clone(), - Box::new(PersistentConfigurationMock::new()), + Box::new(PersistentConfigurationMock::new().set_min_hops_result(Ok(()))), Box::new(actor_factory), ); @@ -1645,7 +1739,7 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, node_descriptor: Default::default(), payment_thresholds_opt: Default::default(), @@ -1657,7 +1751,7 @@ mod tests { let _ = subject.prepare_initial_messages( make_cryptde_pair(), config.clone(), - Box::new(PersistentConfigurationMock::new()), + Box::new(PersistentConfigurationMock::new().set_min_hops_result(Ok(()))), Box::new(actor_factory), ); @@ -1863,242 +1957,41 @@ mod tests { ); } - struct AccountantSubsFactoryTestOnly { - address_leaker: Sender>, - } - - impl AccountantSubsFactory for AccountantSubsFactoryTestOnly { - fn make(&self, addr: &Addr) -> AccountantSubs { - self.address_leaker.try_send(addr.clone()).unwrap(); - let nonsensical_addr = Recorder::new().start(); - make_accountant_subs_from_recorder(&nonsensical_addr) - } - } - - fn check_ongoing_usage_of_user_defined_fns_within_new_delinquencies_for_receivable_dao( - ) -> ShouldWeRunTheTest { - fn skip_down_to_first_line_saying_new_delinquencies( - previous: impl Iterator, - ) -> impl Iterator { - previous - .skip_while(|line| { - let adjusted_line: String = line - .chars() - .skip_while(|char| char.is_whitespace()) - .collect(); - !adjusted_line.starts_with("fn new_delinquencies(") - }) - .skip(1) - } - fn user_defined_functions_detected(line_undivided_fn_body: &str) -> bool { - line_undivided_fn_body.contains(" slope_drop_high_bytes(") - && line_undivided_fn_body.contains(" slope_drop_low_bytes(") - } - fn assert_is_not_trait_definition(body_lines: impl Iterator) -> String { - fn yield_if_contains_semicolon(line: &str) -> Option { - line.contains(';').then(|| line.to_string()) - } - let mut semicolon_line_opt = None; - let line_undivided_fn_body = body_lines - .map(|line| { - if semicolon_line_opt.is_none() { - if let Some(result) = yield_if_contains_semicolon(&line) { - semicolon_line_opt = Some(result) - } - } - line - }) - .collect::(); - if let Some(line) = semicolon_line_opt { - let regex = Regex::new(r"Vec<\w+>;").unwrap(); - if regex.is_match(&line) { - // The important part of the regex is the ending semicolon. Trait implementations don't use it; - // they just go on with an opening bracket of the function body. Its presence therefore signifies - // we have to do with a trait definition - panic!("the second parsed chunk of code is a trait definition and the implementation lies first") - } - } else { - () //means is a clean function body without semicolon - } - line_undivided_fn_body - } - - let current_dir = current_dir().unwrap(); - let file_path = current_dir.join(PathBuf::from_iter([ - "src", - "accountant", - "database_access_objects", - "receivable_dao.rs", - ])); - let file = match File::open(file_path) { - Ok(file) => file, - Err(_) => match check_if_source_code_is_attached(¤t_dir) { - Skip => return Skip, - _ => panic!( - "if panics, the file receivable_dao.rs probably doesn't exist or \ - has been moved to an unexpected location" - ), - }, - }; - let reader = BufReader::new(file); - let lines_without_fn_trait_definition = - skip_down_to_first_line_saying_new_delinquencies(reader.lines().flatten()); - let function_body_ready_for_final_check = { - let assumed_implemented_function_body = - skip_down_to_first_line_saying_new_delinquencies(lines_without_fn_trait_definition) - .take_while(|line| { - let adjusted_line: String = line - .chars() - .skip_while(|char| char.is_whitespace()) - .collect(); - !adjusted_line.starts_with("fn") - }); - assert_is_not_trait_definition(assumed_implemented_function_body) - }; - if user_defined_functions_detected(&function_body_ready_for_final_check) { - GoAhead - } else { - panic!("was about to test user-defined SQLite functions (slope_drop_high_bytes and slope_drop_low_bytes) - in new_delinquencies() but found out those are absent at the expected place and would leave falsely positive results") - } - } - #[test] - fn our_big_int_sqlite_functions_are_linked_to_receivable_dao_within_accountant() { - //condition: .new_delinquencies() still encompasses our user defined functions (that's why a formal check opens this test) - if let Skip = - check_ongoing_usage_of_user_defined_fns_within_new_delinquencies_for_receivable_dao() - { - eprintln!("skipping test our_big_int_sqlite_functions_are_linked_to_receivable_dao_within_accountant; - was unable to find receivable_dao.rs"); - return; + fn accountant_is_constructed_with_upgraded_db_connection_recognizing_our_extra_sqlite_functions( + ) { + let act = |bootstrapper_config: BootstrapperConfig, + db_initializer: DbInitializerReal, + banned_cache_loader: BannedCacheLoaderMock, + address_leaker: SubsFactoryTestAddrLeaker| { + ActorFactoryReal {}.make_and_start_accountant( + bootstrapper_config, + &db_initializer, + &banned_cache_loader, + &address_leaker, + ) }; - let data_dir = ensure_node_home_directory_exists( - "actor_system_factory", - "our_big_int_sqlite_functions_are_linked_to_receivable_dao_within_accountant", - ); - let _ = DbInitializerReal::default() - .initialize(data_dir.as_ref(), DbInitializationConfig::test_default()) - .unwrap(); - let mut b_config = bc_from_earning_wallet(make_wallet("mine")); - b_config.data_directory = data_dir; - let system = System::new( - "our_big_int_sqlite_functions_are_linked_to_receivable_dao_within_accountant", - ); - let (addr_tx, addr_rv) = bounded(1); - let subject = ActorFactoryReal {}; - - subject.make_and_start_accountant( - b_config, - &DbInitializerReal::default(), - &BannedCacheLoaderMock::default(), - &AccountantSubsFactoryTestOnly { - address_leaker: addr_tx, - }, - ); - let accountant_addr = addr_rv.try_recv().unwrap(); - //this message also stops the system after the check - accountant_addr - .try_send(TestUserDefinedSqliteFnsForNewDelinquencies {}) - .unwrap(); - assert_eq!(system.run(), 0); - //we didn't blow up, it recognized the functions - //this is an example of the error: "no such function: slope_drop_high_bytes" + test_accountant_is_constructed_with_upgraded_db_connection_recognizing_our_extra_sqlite_functions( + "actor_system_factory", + "accountant_is_constructed_with_upgraded_db_connection_recognizing_our_extra_sqlite_functions", + act, + ) } #[test] - fn make_and_start_actors_happy_path() { - let validate_database_chain_params_arc = Arc::new(Mutex::new(vec![])); - let prepare_initial_messages_params_arc = Arc::new(Mutex::new(vec![])); - let (recorder, _, recording_arc) = make_recorder(); - let stream_holder_pool_subs = make_stream_handler_pool_subs_from(Some(recorder)); - let mut bootstrapper_config = BootstrapperConfig::new(); - let irrelevant_data_dir = PathBuf::new().join("big_directory/small_directory"); - bootstrapper_config.blockchain_bridge_config.chain = Chain::PolyMainnet; - bootstrapper_config.data_directory = irrelevant_data_dir.clone(); - bootstrapper_config.db_password_opt = Some("chameleon".to_string()); - let main_cryptde = main_cryptde(); - let main_cryptde_public_key_before = public_key_for_dyn_cryptde_being_null(main_cryptde); - let alias_cryptde = alias_cryptde(); - let alias_cryptde_public_key_before = public_key_for_dyn_cryptde_being_null(alias_cryptde); - let actor_factory = Box::new(ActorFactoryReal {}) as Box; - let actor_factory_before_raw_address = addr_of!(*actor_factory); - let persistent_config_id = ArbitraryIdStamp::new(); - let persistent_config = Box::new( - PersistentConfigurationMock::default().set_arbitrary_id_stamp(persistent_config_id), - ); - let persistent_config_before_raw = addr_of!(*persistent_config); - let tools = ActorSystemFactoryToolsMock::default() - .cryptdes_result(CryptDEPair { - main: main_cryptde, - alias: alias_cryptde, - }) - .validate_database_chain_params(&validate_database_chain_params_arc) - .prepare_initial_messages_params(&prepare_initial_messages_params_arc) - .prepare_initial_messages_result(stream_holder_pool_subs); - let subject = ActorSystemFactoryReal::new(Box::new(tools)); - - let result = - subject.make_and_start_actors(bootstrapper_config, actor_factory, persistent_config); - - let mut validate_database_chain_params = validate_database_chain_params_arc.lock().unwrap(); - let (persistent_config_id_captured, chain) = validate_database_chain_params.remove(0); - assert!(validate_database_chain_params.is_empty()); - assert_eq!(persistent_config_id_captured, persistent_config_id); - assert_eq!(chain, Chain::PolyMainnet); - let mut prepare_initial_messages_params = - prepare_initial_messages_params_arc.lock().unwrap(); - let ( - main_cryptde_after, - alias_cryptde_after, - bootstrapper_config_after, - actor_factory_after, - persistent_config_after, - ) = prepare_initial_messages_params.remove(0); - assert!(prepare_initial_messages_params.is_empty()); - let main_cryptde_public_key_after = - public_key_for_dyn_cryptde_being_null(main_cryptde_after.as_ref()); - assert_eq!( - main_cryptde_public_key_after, - main_cryptde_public_key_before - ); - let alias_cryptde_public_key_after = - public_key_for_dyn_cryptde_being_null(alias_cryptde_after.as_ref()); - assert_eq!( - alias_cryptde_public_key_after, - alias_cryptde_public_key_before - ); - assert_eq!( - bootstrapper_config_after.data_directory, - irrelevant_data_dir - ); - assert_eq!( - bootstrapper_config_after.db_password_opt, - Some("chameleon".to_string()) - ); - assert_eq!( - addr_of!(*actor_factory_after), - actor_factory_before_raw_address - ); - assert_eq!( - addr_of!(*persistent_config_after), - persistent_config_before_raw - ); - let system = System::new("make_and_start_actors_happy_path"); - let msg_of_irrelevant_choice = NodeFromUiMessage { - client_id: 5, - body: UiDescriptorRequest {}.tmb(1), + fn blockchain_bridge_is_constructed_with_correctly_functioning_connections() { + let act = |bootstrapper_config: BootstrapperConfig, + address_leaker: SubsFactoryTestAddrLeaker| { + ActorFactoryReal {} + .make_and_start_blockchain_bridge(&bootstrapper_config, &address_leaker) }; - result - .node_from_ui_sub - .try_send(msg_of_irrelevant_choice.clone()) - .unwrap(); - System::current().stop_with_code(0); - system.run(); - let recording = recording_arc.lock().unwrap(); - let msg = recording.get_record::(0); - assert_eq!(msg, &msg_of_irrelevant_choice); + + test_blockchain_bridge_is_constructed_with_correctly_functioning_connections( + "actor_system_factory", + "blockchain_bridge_is_constructed_with_correctly_functioning_connections", + act, + ) } #[test] @@ -2119,7 +2012,7 @@ mod tests { assert_on_initialization_with_panic_on_migration(&data_dir, &act); } - fn public_key_for_dyn_cryptde_being_null(cryptde: &dyn CryptDE) -> &PublicKey { + fn pk_from_cryptde_null(cryptde: &dyn CryptDE) -> &PublicKey { let null_cryptde = <&CryptDENull>::from(cryptde); null_cryptde.public_key() } diff --git a/node/src/apps.rs b/node/src/apps.rs index 2fa3acd88..9aed6369b 100644 --- a/node/src/apps.rs +++ b/node/src/apps.rs @@ -8,6 +8,7 @@ use masq_lib::shared_schema::{ chain_arg, data_directory_arg, db_password_arg, real_user_arg, shared_app, ui_port_arg, DB_PASSWORD_HELP, }; +use masq_lib::utils::DATA_DIRECTORY_DAEMON_HELP; pub fn app_head() -> App<'static, 'static> { App::new("MASQNode") @@ -23,6 +24,7 @@ pub fn app_head() -> App<'static, 'static> { pub fn app_daemon() -> App<'static, 'static> { app_head() + .arg(data_directory_arg(DATA_DIRECTORY_DAEMON_HELP.as_str())) .arg( Arg::with_name("initialization") .long("initialization") @@ -39,6 +41,7 @@ pub fn app_node() -> App<'static, 'static> { pub fn app_config_dumper() -> App<'static, 'static> { app_head() + .arg(chain_arg()) .arg( Arg::with_name("dump-config") .long("dump-config") @@ -46,10 +49,9 @@ pub fn app_config_dumper() -> App<'static, 'static> { .takes_value(false) .help(DUMP_CONFIG_HELP), ) - .arg(chain_arg()) - .arg(data_directory_arg()) - .arg(real_user_arg()) + .arg(data_directory_arg(DATA_DIRECTORY_DAEMON_HELP.as_str())) .arg(db_password_arg(DB_PASSWORD_HELP)) + .arg(real_user_arg()) } lazy_static! { @@ -95,9 +97,41 @@ const NODE_HELP_TEXT: &str = indoc!( #[cfg(test)] mod tests { use super::*; + use dirs::{data_local_dir, home_dir}; + use std::path::Path; #[test] fn constants_have_correct_values() { + let data_dir = data_local_dir().unwrap(); + let home_dir = home_dir().unwrap(); + let polygon_mainnet_dir = Path::new(&data_dir.to_str().unwrap()) + .join("MASQ") + .join("polygon-mainnet"); + let polygon_mumbai_dir = Path::new(&data_dir.to_str().unwrap()) + .join("MASQ") + .join("polygon-mumbai"); + + assert_eq!( + DATA_DIRECTORY_DAEMON_HELP.as_str(), + format!("Directory in which the Node will store its persistent state, including at least its database \ + and by default its configuration file as well. By default, your data-directory is located in \ + your application directory, under your home directory e.g.: '{}'.\n\n\ + In case you change your chain to a different one, the data-directory path is automatically changed \ + to end with the name of your chain: e.g.: if you choose polygon-mumbai, then data-directory is \ + automatically changed to: '{}'.\n\n\ + You can specify your own data-directory to the Daemon in two different ways: \n\n\ + 1. If you provide a path without the chain name on the end, the Daemon will automatically change \ + your data-directory to correspond with the chain. For example: {}/masq_home will be automatically \ + changed to: '{}/masq_home/polygon-mainnet'.\n\n\ + 2. If you provide your data directory with the corresponding chain name on the end, eg: {}/masq_home/polygon-mainnet, \ + there will be no change until you set the chain parameter to a different value.", + polygon_mainnet_dir.to_string_lossy().to_string().as_str(), + polygon_mumbai_dir.to_string_lossy().to_string().as_str(), + &home_dir.to_string_lossy().to_string().as_str(), + &home_dir.to_string_lossy().to_string().as_str(), + home_dir.to_string_lossy().to_string().as_str() + ) + ); assert_eq!( DUMP_CONFIG_HELP, "Dump the configuration of MASQ Node to stdout in JSON. Used chiefly by UIs." diff --git a/node/src/blockchain/bip32.rs b/node/src/blockchain/bip32.rs index 1234d0d03..6b869d96b 100644 --- a/node/src/blockchain/bip32.rs +++ b/node/src/blockchain/bip32.rs @@ -11,32 +11,32 @@ use std::hash::{Hash, Hasher}; use std::num::NonZeroU32; use tiny_hderive::bip32::ExtendedPrivKey; -#[allow(clippy::upper_case_acronyms)] #[derive(Debug)] -pub struct Bip32ECKeyProvider { +#[allow(clippy::upper_case_acronyms)] +pub struct Bip32EncryptionKeyProvider { secret_raw: Vec, } #[allow(clippy::from_over_into)] -impl Into for &Bip32ECKeyProvider { +impl Into for &Bip32EncryptionKeyProvider { fn into(self) -> Secp256k1SecretKey { secp256k1secrets::key::SecretKey::from_slice(&self.secret_raw).expect("internal error") } } #[allow(clippy::from_over_into)] -impl Into for &Bip32ECKeyProvider { +impl Into for &Bip32EncryptionKeyProvider { fn into(self) -> EthsignSecretKey { EthsignSecretKey::from_raw(self.secret_raw.as_ref()).expect("internal error") } } -impl Bip32ECKeyProvider { +impl Bip32EncryptionKeyProvider { const SECRET_KEY_LENGTH: usize = 32; pub fn from_raw_secret(secret_raw: &[u8]) -> Result { Self::validate_raw_input(secret_raw)?; - Ok(Bip32ECKeyProvider { + Ok(Bip32EncryptionKeyProvider { secret_raw: secret_raw.to_vec(), }) } @@ -83,7 +83,7 @@ impl Bip32ECKeyProvider { } } -impl TryFrom<(&[u8], &str)> for Bip32ECKeyProvider { +impl TryFrom<(&[u8], &str)> for Bip32EncryptionKeyProvider { type Error = String; fn try_from(seed_path: (&[u8], &str)) -> Result { @@ -99,7 +99,7 @@ impl TryFrom<(&[u8], &str)> for Bip32ECKeyProvider { } } -impl<'de> Deserialize<'de> for Bip32ECKeyProvider { +impl<'de> Deserialize<'de> for Bip32EncryptionKeyProvider { fn deserialize(deserializer: D) -> Result>::Error> where D: Deserializer<'de>, @@ -112,7 +112,7 @@ impl<'de> Deserialize<'de> for Bip32ECKeyProvider { } } -impl Serialize for Bip32ECKeyProvider { +impl Serialize for Bip32EncryptionKeyProvider { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, @@ -128,15 +128,15 @@ impl Serialize for Bip32ECKeyProvider { } } -impl PartialEq for Bip32ECKeyProvider { - fn eq(&self, other: &Bip32ECKeyProvider) -> bool { +impl PartialEq for Bip32EncryptionKeyProvider { + fn eq(&self, other: &Bip32EncryptionKeyProvider) -> bool { self.public_key().bytes().as_ref() == other.public_key().bytes().as_ref() } } -impl Eq for Bip32ECKeyProvider {} +impl Eq for Bip32EncryptionKeyProvider {} -impl Hash for Bip32ECKeyProvider { +impl Hash for Bip32EncryptionKeyProvider { fn hash(&self, state: &mut H) { self.public_key().bytes().hash(state); } @@ -153,7 +153,7 @@ mod tests { #[test] fn constants_have_correct_values() { - assert_eq!(Bip32ECKeyProvider::SECRET_KEY_LENGTH, 32); + assert_eq!(Bip32EncryptionKeyProvider::SECRET_KEY_LENGTH, 32); } #[test] @@ -165,7 +165,7 @@ mod tests { ) .unwrap(); let seed = Seed::new(&mnemonic, "Test123!"); - let bip32eckey_provider = Bip32ECKeyProvider::try_from(( + let bip32eckey_provider = Bip32EncryptionKeyProvider::try_from(( seed.as_ref(), DEFAULT_CONSUMING_DERIVATION_PATH.as_str(), )) @@ -186,9 +186,11 @@ mod tests { ) .unwrap(); let seed = Seed::new(&mnemonic, "Test123!"); - let bip32eckey_provider = - Bip32ECKeyProvider::try_from((seed.as_ref(), DEFAULT_EARNING_DERIVATION_PATH.as_str())) - .unwrap(); + let bip32eckey_provider = Bip32EncryptionKeyProvider::try_from(( + seed.as_ref(), + DEFAULT_EARNING_DERIVATION_PATH.as_str(), + )) + .unwrap(); let address: Address = bip32eckey_provider.address(); let expected_address: Address = serde_json::from_str::
("\"0x20eF925bBbFca786bd426BaED8c6Ae45e4284e12\"") @@ -207,7 +209,7 @@ mod tests { let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); let seed = Seed::new(&mnemonic, ""); - let key_provider = Bip32ECKeyProvider::try_from(( + let key_provider = Bip32EncryptionKeyProvider::try_from(( seed.as_bytes(), DEFAULT_CONSUMING_DERIVATION_PATH.as_str(), )) @@ -248,7 +250,7 @@ mod tests { .unwrap(); let seed = Seed::new(&mnemonic, "Test123!"); assert_eq!( - Bip32ECKeyProvider::try_from((seed.as_ref(), "")).unwrap_err(), + Bip32EncryptionKeyProvider::try_from((seed.as_ref(), "")).unwrap_err(), "InvalidDerivationPath".to_string() ); } @@ -256,23 +258,26 @@ mod tests { #[test] fn bip32_try_from_errors_with_empty_seed() { assert_eq!( - Bip32ECKeyProvider::try_from(("".as_ref(), DEFAULT_CONSUMING_DERIVATION_PATH.as_str())) - .unwrap_err(), + Bip32EncryptionKeyProvider::try_from(( + "".as_ref(), + DEFAULT_CONSUMING_DERIVATION_PATH.as_str() + )) + .unwrap_err(), "Invalid Seed Length: 0".to_string() ); } - fn keypair_a() -> Bip32ECKeyProvider { + fn keypair_a() -> Bip32EncryptionKeyProvider { let numbers = (0u8..32u8).collect::>(); - Bip32ECKeyProvider::from_raw_secret(&numbers).unwrap() + Bip32EncryptionKeyProvider::from_raw_secret(&numbers).unwrap() } - fn keypair_b() -> Bip32ECKeyProvider { + fn keypair_b() -> Bip32EncryptionKeyProvider { let numbers = (1u8..33u8).collect::>(); - Bip32ECKeyProvider::from_raw_secret(&numbers).unwrap() + Bip32EncryptionKeyProvider::from_raw_secret(&numbers).unwrap() } - fn hash(keypair: &Bip32ECKeyProvider) -> u64 { + fn hash(keypair: &Bip32EncryptionKeyProvider) -> u64 { let mut hasher = DefaultHasher::new(); keypair.hash(&mut hasher); hasher.finish() @@ -304,7 +309,7 @@ mod tests { fn from_raw_secret_validates_correct_length_happy_path() { let secret_raw: Vec = (0..32u8).collect(); - let result = Bip32ECKeyProvider::from_raw_secret(secret_raw.as_slice()).unwrap(); + let result = Bip32EncryptionKeyProvider::from_raw_secret(secret_raw.as_slice()).unwrap(); assert_eq!(result.secret_raw, secret_raw) } @@ -313,7 +318,7 @@ mod tests { fn from_raw_secret_complains_about_input_too_long() { let secret_raw: Vec = (0..33u8).collect(); - let result = Bip32ECKeyProvider::from_raw_secret(secret_raw.as_slice()); + let result = Bip32EncryptionKeyProvider::from_raw_secret(secret_raw.as_slice()); assert_eq!( result, @@ -325,7 +330,7 @@ mod tests { fn from_raw_secret_complains_about_input_too_short() { let secret_raw: Vec = (0..31u8).collect(); - let result = Bip32ECKeyProvider::from_raw_secret(secret_raw.as_slice()); + let result = Bip32EncryptionKeyProvider::from_raw_secret(secret_raw.as_slice()); assert_eq!( result, diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index f2f68d622..e90c25012 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -1,26 +1,29 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::scanners::payable_scan_setup_msgs::{ - FinancialAndTechDetails, PayablePaymentSetup, StageData, +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ + BlockchainAgentWithContextMessage, QualifiedPayablesMessage, }; use crate::accountant::{ ReceivedPayments, ResponseSkeleton, ScanError, SentPayables, SkeletonOptHolder, }; use crate::accountant::{ReportTransactionReceipts, RequestTransactionReceipts}; -use crate::blockchain::blockchain_interface::{ - BlockchainError, BlockchainInterface, BlockchainInterfaceClandestine, - BlockchainInterfaceNonClandestine, PayableTransactionError, ProcessedPayableFallible, +use crate::actor_system_factory::SubsFactory; +use crate::blockchain::blockchain_interface::blockchain_interface_null::BlockchainInterfaceNull; +use crate::blockchain::blockchain_interface::data_structures::errors::{ + BlockchainError, PayableTransactionError, }; +use crate::blockchain::blockchain_interface::data_structures::ProcessedPayableFallible; +use crate::blockchain::blockchain_interface::BlockchainInterface; +use crate::blockchain::blockchain_interface_initializer::BlockchainInterfaceInitializer; use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; use crate::db_config::config_dao::ConfigDaoReal; use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; -use crate::sub_lib::blockchain_bridge::{ - BlockchainBridgeSubs, ConsumingWalletBalances, OutcomingPaymentsInstructions, -}; +use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, OutboundPaymentsInstructions}; use crate::sub_lib::peer_actors::BindMessage; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::utils::{db_connection_launch_panic, handle_ui_crash_request}; use crate::sub_lib::wallet::Wallet; use actix::Actor; @@ -33,10 +36,11 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::logger::Logger; use masq_lib::messages::ScanType; use masq_lib::ui_gateway::NodeFromUiMessage; -use std::path::PathBuf; +use masq_lib::utils::to_string; +use regex::Regex; +use std::path::Path; use std::time::SystemTime; -use web3::transports::Http; -use web3::types::{TransactionReceipt, H256}; +use web3::types::{BlockNumber, TransactionReceipt, H256}; pub const CRASH_KEY: &str = "BLOCKCHAINBRIDGE"; @@ -45,9 +49,8 @@ pub struct BlockchainBridge { blockchain_interface: Box, logger: Logger, persistent_config: Box, - set_consuming_wallet_subs_opt: Option>>, sent_payable_subs_opt: Option>, - balances_and_payables_sub_opt: Option>, + payable_payments_setup_subs_opt: Option>, received_payments_subs_opt: Option>, scan_error_subs_opt: Option>, crashable: bool, @@ -67,21 +70,14 @@ impl Handler for BlockchainBridge { type Result = (); fn handle(&mut self, msg: BindMessage, _ctx: &mut Self::Context) -> Self::Result { - self.set_consuming_wallet_subs_opt = Some(vec![ - msg.peer_actors.neighborhood.set_consuming_wallet_sub, - msg.peer_actors.proxy_server.set_consuming_wallet_sub, - ]); self.pending_payable_confirmation .new_pp_fingerprints_sub_opt = Some(msg.peer_actors.accountant.init_pending_payable_fingerprints); self.pending_payable_confirmation .report_transaction_receipts_sub_opt = Some(msg.peer_actors.accountant.report_transaction_receipts); - self.balances_and_payables_sub_opt = Some( - msg.peer_actors - .accountant - .report_consuming_wallet_balances_and_qualified_payables, - ); + self.payable_payments_setup_subs_opt = + Some(msg.peer_actors.accountant.report_payable_payments_setup); self.sent_payable_subs_opt = Some(msg.peer_actors.accountant.report_sent_payments); self.received_payments_subs_opt = Some(msg.peer_actors.accountant.report_inbound_payments); self.scan_error_subs_opt = Some(msg.peer_actors.accountant.scan_errors); @@ -138,20 +134,20 @@ impl Handler for BlockchainBridge { } } -impl Handler for BlockchainBridge { +impl Handler for BlockchainBridge { type Result = (); - fn handle(&mut self, msg: PayablePaymentSetup, _ctx: &mut Self::Context) { - self.handle_scan(Self::handle_payable_payment_setup, ScanType::Payables, msg); + fn handle(&mut self, msg: QualifiedPayablesMessage, _ctx: &mut Self::Context) { + self.handle_scan(Self::handle_qualified_payable_msg, ScanType::Payables, msg); } } -impl Handler for BlockchainBridge { +impl Handler for BlockchainBridge { type Result = (); - fn handle(&mut self, msg: OutcomingPaymentsInstructions, _ctx: &mut Self::Context) { + fn handle(&mut self, msg: OutboundPaymentsInstructions, _ctx: &mut Self::Context) { self.handle_scan( - Self::handle_report_accounts_payable, + Self::handle_outbound_payments_instructions, ScanType::Payables, msg, ) @@ -166,11 +162,11 @@ pub struct PendingPayableFingerprintSeeds { #[derive(Debug, PartialEq, Eq, Clone)] pub struct PendingPayableFingerprint { - //Sqlite begins counting from 1 + // Sqlite begins counting from 1 pub rowid: u64, pub timestamp: SystemTime, pub hash: H256, - //Sqlite begins counting from 1 + // We have Sqlite begin counting from 1 pub attempt: u16, pub amount: u128, pub process_error: Option, @@ -195,9 +191,8 @@ impl BlockchainBridge { consuming_wallet_opt, blockchain_interface, persistent_config, - set_consuming_wallet_subs_opt: None, sent_payable_subs_opt: None, - balances_and_payables_sub_opt: None, + payable_payments_setup_subs_opt: None, received_payments_subs_opt: None, scan_error_subs_opt: None, crashable, @@ -209,120 +204,85 @@ impl BlockchainBridge { } } - pub fn make_connections( - blockchain_service_url: Option, - data_directory: PathBuf, - chain: Chain, - ) -> ( - Box, - Box, - ) { - let blockchain_interface: Box = { - match blockchain_service_url { - Some(url) => match Http::new(&url) { - Ok((event_loop_handle, transport)) => Box::new( - BlockchainInterfaceNonClandestine::new(transport, event_loop_handle, chain), - ), - Err(e) => panic!("Invalid blockchain node URL: {:?}", e), - }, - None => Box::new(BlockchainInterfaceClandestine::new(chain)), - } - }; + pub fn initialize_persistent_configuration( + data_directory: &Path, + ) -> Box { let config_dao = Box::new(ConfigDaoReal::new( DbInitializerReal::default() - .initialize( - &data_directory, - DbInitializationConfig::panic_on_migration(), - ) - .unwrap_or_else(|err| db_connection_launch_panic(err, &data_directory)), + .initialize(data_directory, DbInitializationConfig::panic_on_migration()) + .unwrap_or_else(|err| db_connection_launch_panic(err, data_directory)), )); - ( - blockchain_interface, - Box::new(PersistentConfigurationReal::new(config_dao)), - ) + Box::new(PersistentConfigurationReal::new(config_dao)) + } + + pub fn initialize_blockchain_interface( + blockchain_service_url_opt: Option, + chain: Chain, + ) -> Box { + match blockchain_service_url_opt { + Some(url) => { + // TODO if we decided to have interchangeably runtime switchable or simultaneously usable interfaces we will + // probably want to make BlockchainInterfaceInitializer a collaborator that's a part of the actor + BlockchainInterfaceInitializer {}.initialize_interface(&url, chain) + } + None => Box::new(BlockchainInterfaceNull::default()), + } } pub fn make_subs_from(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), - pps_for_blockchain_bridge: recipient!(addr, PayablePaymentSetup), + outbound_payments_instructions: recipient!(addr, OutboundPaymentsInstructions), + qualified_payables: recipient!(addr, QualifiedPayablesMessage), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), request_transaction_receipts: recipient!(addr, RequestTransactionReceipts), } } - fn handle_payable_payment_setup(&mut self, msg: PayablePaymentSetup) -> Result<(), String> { - let consuming_wallet = match self.consuming_wallet_opt.as_ref() { - Some(wallet) => wallet, - None => { - return Err( - "Cannot inspect available balances for payables while consuming wallet \ - is missing" - .to_string(), - ) - } - }; - // TODO rewrite this into a batch call as soon as GH-629 gets into master - // New card GH-707 will address this - let gas_balance = match self.blockchain_interface.get_gas_balance(consuming_wallet) { - Ok(gas_balance) => u128::try_from(gas_balance).expect("unexpected wealth"), - Err(e) => { - return Err(format!( - "Did not find out gas balance of the consuming wallet: {:?}", - e - )) - } + fn handle_qualified_payable_msg( + &mut self, + incoming_message: QualifiedPayablesMessage, + ) -> Result<(), String> { + let consuming_wallet = if let Some(wallet) = self.consuming_wallet_opt.as_ref() { + wallet + } else { + return Err( + "Cannot inspect available balances for payables while consuming wallet is missing" + .to_string(), + ); }; - let token_balance = match self + + let agent = self .blockchain_interface - .get_token_balance(consuming_wallet) - { - Ok(token_balance) => u128::try_from(token_balance).expect("unexpected wealth"), - Err(e) => { - return Err(format!( - "Did not find out token balance of the consuming wallet: {:?}", - e - )) - } - }; - let consuming_wallet_balances = { - ConsumingWalletBalances { - transaction_fee_minor: gas_balance, - masq_tokens_minor: token_balance, - } - }; - let desired_gas_price_gwei = self - .persistent_config - .gas_price() - .map_err(|e| format!("Couldn't query the gas price: {:?}", e))?; - let estimated_gas_limit_per_transaction = - self.blockchain_interface.estimated_gas_limit_per_payable(); - let this_stage_data = StageData::FinancialAndTechDetails(FinancialAndTechDetails { - consuming_wallet_balances, - estimated_gas_limit_per_transaction, - agreed_transaction_fee_per_computed_unit_major: desired_gas_price_gwei, - }); - let msg = PayablePaymentSetup::from((msg, this_stage_data)); + .build_blockchain_agent(consuming_wallet, &*self.persistent_config) + .map_err(to_string)?; + + let outgoing_message = BlockchainAgentWithContextMessage::new( + incoming_message.protected_qualified_payables, + agent, + incoming_message.response_skeleton_opt, + ); - self.balances_and_payables_sub_opt + self.payable_payments_setup_subs_opt .as_ref() .expect("Accountant is unbound") - .try_send(msg) + .try_send(outgoing_message) .expect("Accountant is dead"); Ok(()) } - fn handle_report_accounts_payable( + fn handle_outbound_payments_instructions( &mut self, - msg: OutcomingPaymentsInstructions, + msg: OutboundPaymentsInstructions, ) -> Result<(), String> { let skeleton_opt = msg.response_skeleton_opt; - let result = self.process_payments(&msg); + let agent = msg.agent; + let checked_accounts = msg.affordable_accounts; + let result = self.process_payments(agent, checked_accounts); - let local_processing_result = match &result { + let locally_produced_result = match &result { Err(e) => Err(format!("ReportAccountsPayable: {}", e)), Ok(_) => Ok(()), }; @@ -336,25 +296,57 @@ impl BlockchainBridge { }) .expect("Accountant is dead"); - local_processing_result + locally_produced_result } fn handle_retrieve_transactions(&mut self, msg: RetrieveTransactions) -> Result<(), String> { - let start_block = match self.persistent_config.start_block() { + let start_block_nbr = match self.persistent_config.start_block() { Ok (sb) => sb, Err (e) => panic! ("Cannot retrieve start block from database; payments to you may not be processed: {:?}", e) }; - let retrieved_transactions = self + let max_block_count = match self.persistent_config.max_block_count() { + Ok(Some(mbc)) => mbc, + _ => u64::MAX, + }; + let end_block = match self .blockchain_interface - .retrieve_transactions(start_block, &msg.recipient); - + .lower_interface() + .get_block_number() + { + Ok(eb) => { + if u64::MAX == max_block_count { + BlockNumber::Number(eb) + } else { + BlockNumber::Number(eb.as_u64().min(start_block_nbr + max_block_count).into()) + } + } + Err(e) => { + info!( + self.logger, + "Using 'latest' block number instead of a literal number. {:?}", e + ); + if max_block_count == u64::MAX { + BlockNumber::Latest + } else { + BlockNumber::Number((start_block_nbr + max_block_count).into()) + } + } + }; + let start_block = BlockNumber::Number(start_block_nbr.into()); + let retrieved_transactions = + self.blockchain_interface + .retrieve_transactions(start_block, end_block, &msg.recipient); match retrieved_transactions { Ok(transactions) => { + debug!( + self.logger, + "Write new start block: {}", transactions.new_start_block + ); if let Err(e) = self .persistent_config .set_start_block(transactions.new_start_block) { - panic! ("Cannot set start block in database; payments to you may not be processed: {:?}", e) + panic! ("Cannot set start block {} in database; payments to you may not be processed: {:?}", transactions.new_start_block, e) }; if transactions.transactions.is_empty() { debug!(self.logger, "No new receivable detected"); @@ -370,10 +362,33 @@ impl BlockchainBridge { .expect("Accountant is dead."); Ok(()) } - Err(e) => Err(format!( - "Tried to retrieve received payments but failed: {:?}", - e - )), + Err(e) => { + if let Some(max_block_count) = self.extract_max_block_count(e.clone()) { + debug!(self.logger, "Writing max_block_count({})", max_block_count); + self.persistent_config + .set_max_block_count(Some(max_block_count)) + .map_or_else( + |_| { + warning!(self.logger, "{} update max_block_count to {}. Scheduling next scan with that limit.", e, max_block_count); + Err(format!("{} updated max_block_count to {}. Scheduling next scan with that limit.", e, max_block_count)) + }, + |e| { + warning!(self.logger, "Writing max_block_count failed: {:?}", e); + Err(format!("Writing max_block_count failed: {:?}", e)) + }, + ) + } else { + warning!( + self.logger, + "Attempted to retrieve received payments but failed: {:?}", + e + ); + Err(format!( + "Attempted to retrieve received payments but failed: {:?}", + e + )) + } + } } } @@ -449,43 +464,47 @@ impl BlockchainBridge { fn process_payments( &self, - msg: &OutcomingPaymentsInstructions, + agent: Box, + affordable_accounts: Vec, ) -> Result, PayableTransactionError> { - let (consuming_wallet, gas_price) = match self.consuming_wallet_opt.as_ref() { - Some(consuming_wallet) => match self.persistent_config.gas_price() { - Ok(gas_price) => (consuming_wallet, gas_price), - Err(e) => { - return Err(PayableTransactionError::GasPriceQueryFailed(format!( - "{:?}", - e - ))) - } - }, - None => return Err(PayableTransactionError::MissingConsumingWallet), - }; - - let pending_nonce = self - .blockchain_interface - .get_transaction_count(consuming_wallet) - .map_err(PayableTransactionError::TransactionCount)?; - - let new_fingerprints_recipient = self.get_new_fingerprints_recipient(); + let new_fingerprints_recipient = self.new_fingerprints_recipient(); self.blockchain_interface.send_batch_of_payables( - consuming_wallet, - gas_price, - pending_nonce, + agent, new_fingerprints_recipient, - &msg.accounts, + &affordable_accounts, ) } - fn get_new_fingerprints_recipient(&self) -> &Recipient { + fn new_fingerprints_recipient(&self) -> &Recipient { self.pending_payable_confirmation .new_pp_fingerprints_sub_opt .as_ref() .expect("Accountant unbound") } + + pub fn extract_max_block_count(&self, error: BlockchainError) -> Option { + let regex_result = + Regex::new(r".* (max: |allowed for your plan: |is limited to |block range limit \()(?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") { + Some(m) => match m.as_str().parse::() { + Ok(value) => Some(value), + Err(_) => None, + }, + _ => None, + }, + None => match msg.as_str() { + "Got invalid response: Expected batch, got single." => Some(1000), + _ => None, + }, + }, + _ => None, + }; + max_block_count + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -494,47 +513,72 @@ struct PendingTxInfo { when_sent: SystemTime, } +pub struct BlockchainBridgeSubsFactoryReal {} + +impl SubsFactory for BlockchainBridgeSubsFactoryReal { + fn make(&self, addr: &Addr) -> BlockchainBridgeSubs { + BlockchainBridge::make_subs_from(addr) + } +} + #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; - use crate::accountant::database_access_objects::utils::from_time_t; - use crate::accountant::test_utils::{ - make_initial_payable_payment_setup_message, make_pending_payable_fingerprint, + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; + use crate::accountant::db_access_objects::utils::from_time_t; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::scanners::test_utils::protect_payables_in_test; + use crate::accountant::test_utils::make_pending_payable_fingerprint; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; + use crate::blockchain::blockchain_interface::blockchain_interface_null::BlockchainInterfaceNull; + use crate::blockchain::blockchain_interface::data_structures::errors::{ + BlockchainAgentBuildError, PayableTransactionError, }; - use crate::blockchain::bip32::Bip32ECKeyProvider; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::Correct; - use crate::blockchain::blockchain_interface::{ - BlockchainError, BlockchainTransaction, RetrievedBlockchainTransactions, + use crate::blockchain::blockchain_interface::data_structures::{ + BlockchainTransaction, RetrievedBlockchainTransactions, }; + use crate::blockchain::blockchain_interface::lower_level_interface::LatestBlockNumber; + use crate::blockchain::blockchain_interface::test_utils::LowerBCIMock; use crate::blockchain::test_utils::{make_tx_hash, BlockchainInterfaceMock}; use crate::db_config::persistent_configuration::PersistentConfigError; use crate::match_every_type_id; use crate::node_test_utils::check_timestamp; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::{make_recorder, peer_actors_builder}; use crate::test_utils::recorder_stop_conditions::StopCondition; use crate::test_utils::recorder_stop_conditions::StopConditions; + use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::test_utils::unshared_test_utils::{ assert_on_initialization_with_panic_on_migration, configure_default_persistent_config, - prove_that_crash_request_handler_is_hooked_up, ZERO, + prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, ZERO, }; use crate::test_utils::{make_paying_wallet, make_wallet}; use actix::System; use ethereum_types::U64; use ethsign_crypto::Keccak256; - use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::messages::ScanType; use masq_lib::test_utils::logging::init_test_logging; use masq_lib::test_utils::logging::TestLogHandler; - use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; use rustc_hex::FromHex; use std::any::TypeId; use std::path::Path; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; - use web3::types::{TransactionReceipt, H160, H256, U256}; + use web3::types::{TransactionReceipt, H160, H256}; + + impl Handler> for BlockchainBridge { + type Result = (); + + fn handle( + &mut self, + msg: AssertionsMessage, + _ctx: &mut Self::Context, + ) -> Self::Result { + (msg.assertions)(self) + } + } #[test] fn constants_have_correct_values() { @@ -551,7 +595,8 @@ mod tests { let secret: Vec = "cc46befe8d169b89db447bd725fc2368b12542113555302598430cb5d5c74ea9" .from_hex() .unwrap(); - let consuming_wallet = Wallet::from(Bip32ECKeyProvider::from_raw_secret(&secret).unwrap()); + let consuming_wallet = + Wallet::from(Bip32EncryptionKeyProvider::from_raw_secret(&secret).unwrap()); let subject = BlockchainBridge::new( stub_bi(), Box::new(configure_default_persistent_config(ZERO)), @@ -574,6 +619,16 @@ mod tests { )); } + #[test] + fn blockchain_interface_null_as_result_of_missing_blockchain_service_url() { + let result = BlockchainBridge::initialize_blockchain_interface(None, TEST_DEFAULT_CHAIN); + + result + .as_any() + .downcast_ref::() + .unwrap(); + } + #[test] fn blockchain_bridge_receives_bind_message_without_consuming_private_key() { init_test_logging(); @@ -599,85 +654,22 @@ mod tests { } #[test] - #[should_panic(expected = "Invalid blockchain node URL")] - fn invalid_blockchain_url_produces_panic() { - let data_directory = PathBuf::new(); //never reached - let blockchain_service_url = Some("http://λ:8545".to_string()); - let _ = BlockchainBridge::make_connections( - blockchain_service_url, - data_directory, - DEFAULT_CHAIN, - ); - } - - #[test] - fn report_accounts_payable_returns_error_when_there_is_no_consuming_wallet_configured() { - let blockchain_interface_mock = BlockchainInterfaceMock::default(); - let persistent_configuration_mock = PersistentConfigurationMock::default(); - let (accountant, _, accountant_recording_arc) = make_recorder(); - let recipient = accountant.start().recipient(); - let mut subject = BlockchainBridge::new( - Box::new(blockchain_interface_mock), - Box::new(persistent_configuration_mock), - false, - None, - ); - subject.sent_payable_subs_opt = Some(recipient); - let request = OutcomingPaymentsInstructions { - accounts: vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 42, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }], - response_skeleton_opt: None, - }; - let system = System::new("test"); - - let result = subject.handle_report_accounts_payable(request); - - System::current().stop(); - assert_eq!(system.run(), 0); - assert_eq!( - result, - Err("ReportAccountsPayable: Missing consuming wallet to pay payable from".to_string()) - ); - let accountant_recording = accountant_recording_arc.lock().unwrap(); - let sent_payables_msg = accountant_recording.get_record::(0); - assert_eq!( - sent_payables_msg, - &SentPayables { - payment_procedure_result: Err(PayableTransactionError::MissingConsumingWallet), - response_skeleton_opt: None - } - ); - assert_eq!(accountant_recording.len(), 1) - } - - #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant( + fn qualified_payables_msg_is_handled_and_new_msg_with_an_added_blockchain_agent_returns_to_accountant( ) { let system = System::new( - "handle_payable_payment_setup_for_blockchain_bridge_reports_balances_and_payables_back_to_accountant", + "qualified_payables_msg_is_handled_and_new_msg_with_an_added_blockchain_agent_returns_to_accountant", ); - let get_gas_balance_params_arc = Arc::new(Mutex::new(vec![])); - let get_token_balance_params_arc = Arc::new(Mutex::new(vec![])); + let build_blockchain_agent_params_arc = Arc::new(Mutex::new(vec![])); let (accountant, _, accountant_recording_arc) = make_recorder(); - let gas_balance = 4455; - let token_balance = 112233; - let wallet_balances_found = ConsumingWalletBalances { - transaction_fee_minor: gas_balance, - masq_tokens_minor: token_balance, - }; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp); let blockchain_interface = BlockchainInterfaceMock::default() - .get_gas_balance_params(&get_gas_balance_params_arc) - .get_gas_balance_result(Ok(U256::from(gas_balance))) - .get_token_balance_params(&get_token_balance_params_arc) - .get_token_balance_result(Ok(U256::from(token_balance))) - .estimated_gas_limit_per_payable_result(51_546); + .build_blockchain_agent_params(&build_blockchain_agent_params_arc) + .build_blockchain_agent_result(Ok(Box::new(agent))); let consuming_wallet = make_paying_wallet(b"somewallet"); - let persistent_configuration = - PersistentConfigurationMock::default().gas_price_result(Ok(146)); + let persistent_config_id_stamp = ArbitraryIdStamp::new(); + let persistent_configuration = PersistentConfigurationMock::default() + .set_arbitrary_id_stamp(persistent_config_id_stamp); let wallet_1 = make_wallet("booga"); let wallet_2 = make_wallet("gulp"); let qualified_payables = vec![ @@ -707,45 +699,47 @@ mod tests { let addr = subject.start(); let subject_subs = BlockchainBridge::make_subs_from(&addr); let peer_actors = peer_actors_builder().accountant(accountant).build(); - let msg = make_initial_payable_payment_setup_message( - qualified_payables.clone(), - Some(ResponseSkeleton { + let qualified_payables = protect_payables_in_test(qualified_payables.clone()); + let qualified_payables_msg = QualifiedPayablesMessage { + protected_qualified_payables: qualified_payables.clone(), + response_skeleton_opt: Some(ResponseSkeleton { client_id: 11122, context_id: 444, }), - ); + }; send_bind_message!(subject_subs, peer_actors); - addr.try_send(msg.clone()).unwrap(); + addr.try_send(qualified_payables_msg).unwrap(); System::current().stop(); system.run(); - let get_gas_balance_params = get_gas_balance_params_arc.lock().unwrap(); - assert_eq!(*get_gas_balance_params, vec![consuming_wallet.clone()]); - let get_token_balance_params = get_token_balance_params_arc.lock().unwrap(); - assert_eq!(*get_token_balance_params, vec![consuming_wallet]); + + let build_blockchain_agent_params = build_blockchain_agent_params_arc.lock().unwrap(); + assert_eq!( + *build_blockchain_agent_params, + vec![(consuming_wallet.clone(), persistent_config_id_stamp)] + ); let accountant_received_payment = accountant_recording_arc.lock().unwrap(); - assert_eq!(accountant_received_payment.len(), 1); - let reported_balances_and_qualified_accounts: &PayablePaymentSetup = + let blockchain_agent_with_context_msg_actual: &BlockchainAgentWithContextMessage = accountant_received_payment.get_record(0); - let expected_msg: PayablePaymentSetup = ( - msg, - StageData::FinancialAndTechDetails(FinancialAndTechDetails { - consuming_wallet_balances: wallet_balances_found, - estimated_gas_limit_per_transaction: 51_546, - agreed_transaction_fee_per_computed_unit_major: 146, - }), - ) - .into(); - assert_eq!(reported_balances_and_qualified_accounts, &expected_msg); + assert_eq!( + blockchain_agent_with_context_msg_actual.protected_qualified_payables, + qualified_payables + ); + assert_eq!( + blockchain_agent_with_context_msg_actual + .agent + .arbitrary_id_stamp(), + agent_id_stamp + ); + assert_eq!(accountant_received_payment.len(), 1); } - fn assert_failure_during_balance_inspection( - test_name: &str, - blockchain_interface: BlockchainInterfaceMock, - error_msg: &str, - ) { + #[test] + fn build_of_blockchain_agent_throws_err_out_and_ends_handling_qualified_payables_message() { init_test_logging(); + let test_name = + "build_of_blockchain_agent_throws_err_out_and_ends_handling_qualified_payables_message"; let (accountant, _, accountant_recording_arc) = make_recorder(); let scan_error_recipient: Recipient = accountant .system_stop_conditions(match_every_type_id!(ScanError)) @@ -753,6 +747,10 @@ mod tests { .recipient(); let persistent_configuration = PersistentConfigurationMock::default(); let consuming_wallet = make_wallet(test_name); + let blockchain_interface = BlockchainInterfaceMock::default() + .build_blockchain_agent_result(Err(BlockchainAgentBuildError::GasPrice( + PersistentConfigError::NotPresent, + ))); let mut subject = BlockchainBridge::new( Box::new(blockchain_interface), Box::new(persistent_configuration), @@ -761,18 +759,18 @@ mod tests { ); subject.logger = Logger::new(test_name); subject.scan_error_subs_opt = Some(scan_error_recipient); - let request = make_initial_payable_payment_setup_message( - vec![PayableAccount { + let request = QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 42, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, - }], - Some(ResponseSkeleton { + }]), + response_skeleton_opt: Some(ResponseSkeleton { client_id: 11, context_id: 2323, }), - ); + }; let subject_addr = subject.start(); let system = System::new(test_name); @@ -784,6 +782,8 @@ mod tests { let recording = accountant_recording_arc.lock().unwrap(); let message = recording.get_record::(0); assert_eq!(recording.len(), 1); + let expected_error_msg = "Blockchain agent construction failed at fetching gas \ + price from the database: NotPresent"; assert_eq!( message, &ScanError { @@ -792,42 +792,15 @@ mod tests { client_id: 11, context_id: 2323 }), - msg: error_msg.to_string() + msg: expected_error_msg.to_string() } ); - TestLogHandler::new().exists_log_containing(&format!("WARN: {}: {}", test_name, error_msg)); - } - - #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_gas_balance() { - let test_name = - "handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_gas_balance"; - let blockchain_interface = BlockchainInterfaceMock::default().get_gas_balance_result(Err( - BlockchainError::QueryFailed("Lazy and yet you're asking for balances?".to_string()), - )); - let error_msg = "Did not find out gas balance of the consuming wallet: \ - QueryFailed(\"Lazy and yet you're asking for balances?\")"; - - assert_failure_during_balance_inspection(test_name, blockchain_interface, error_msg) - } - - #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_token_balance() { - let test_name = - "handle_payable_payment_setup_for_blockchain_bridge_fails_on_inspection_of_token_balance"; - let blockchain_interface = BlockchainInterfaceMock::default() - .get_gas_balance_result(Ok(U256::from(45678))) - .get_token_balance_result(Err(BlockchainError::QueryFailed( - "Go get you a job. This balance must be deserved".to_string(), - ))); - let error_msg = "Did not find out token balance of the consuming wallet: QueryFailed(\ - \"Go get you a job. This balance must be deserved\")"; - - assert_failure_during_balance_inspection(test_name, blockchain_interface, error_msg) + TestLogHandler::new() + .exists_log_containing(&format!("WARN: {test_name}: {expected_error_msg}")); } #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_fails_at_missing_consuming_wallet() { + fn handle_qualified_payable_msg_fails_at_missing_consuming_wallet() { let blockchain_interface = BlockchainInterfaceMock::default(); let persistent_configuration = PersistentConfigurationMock::default(); let mut subject = BlockchainBridge::new( @@ -836,17 +809,17 @@ mod tests { false, None, ); - let request = make_initial_payable_payment_setup_message( - vec![PayableAccount { + let request = QualifiedPayablesMessage { + protected_qualified_payables: protect_payables_in_test(vec![PayableAccount { wallet: make_wallet("blah"), balance_wei: 4254, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, - }], - None, - ); + }]), + response_skeleton_opt: None, + }; - let result = subject.handle_payable_payment_setup(request); + let result = subject.handle_qualified_payable_msg(request); assert_eq!( result, @@ -858,73 +831,34 @@ mod tests { } #[test] - fn handle_payable_payment_setup_for_blockchain_bridge_fails_on_gas_price_query() { - let blockchain_interface = BlockchainInterfaceMock::default() - .get_gas_balance_result(Ok(U256::from(456789))) - .get_token_balance_result(Ok(U256::from(7890123456_u64))); - let persistent_configuration = PersistentConfigurationMock::default().gas_price_result( - Err(PersistentConfigError::DatabaseError("siesta".to_string())), - ); - let consuming_wallet = make_wallet("our wallet"); - let mut subject = BlockchainBridge::new( - Box::new(blockchain_interface), - Box::new(persistent_configuration), - false, - Some(consuming_wallet), - ); - let request = make_initial_payable_payment_setup_message( - vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 123456, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }], - Some(ResponseSkeleton { - client_id: 123, - context_id: 222, - }), - ); - - let result = subject.handle_payable_payment_setup(request); - - assert_eq!( - result, - Err("Couldn't query the gas price: DatabaseError(\"siesta\")".to_string()) - ) - } - - #[test] - fn handle_report_accounts_payable_transacts_and_sends_finished_payments_back_to_accountant() { + fn handle_outbound_payments_instructions_sees_payments_happen_and_sends_payment_results_back_to_accountant( + ) { let system = - System::new("handle_report_accounts_payable_transacts_and_sends_finished_payments_back_to_accountant"); - let get_transaction_count_params_arc = Arc::new(Mutex::new(vec![])); + System::new("handle_outbound_payments_instructions_sees_payments_happen_and_sends_payment_results_back_to_accountant"); let send_batch_of_payables_params_arc = Arc::new(Mutex::new(vec![])); let (accountant, _, accountant_recording_arc) = make_recorder(); let accountant = accountant.system_stop_conditions(match_every_type_id!(PendingPayableFingerprintSeeds)); let wallet_account_1 = make_wallet("blah"); let wallet_account_2 = make_wallet("foo"); + let blockchain_interface_id_stamp = ArbitraryIdStamp::new(); let blockchain_interface_mock = BlockchainInterfaceMock::default() - .get_transaction_count_params(&get_transaction_count_params_arc) - .get_transaction_count_result(Ok(U256::from(1u64))) + .set_arbitrary_id_stamp(blockchain_interface_id_stamp) .send_batch_of_payables_params(&send_batch_of_payables_params_arc) .send_batch_of_payables_result(Ok(vec![ - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_1.clone(), hash: H256::from("sometransactionhash".keccak256()), }), - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_2.clone(), hash: H256::from("someothertransactionhash".keccak256()), }), ])); - let expected_gas_price = 145u64; - let persistent_configuration_mock = - PersistentConfigurationMock::default().gas_price_result(Ok(expected_gas_price)); let consuming_wallet = make_paying_wallet(b"somewallet"); let subject = BlockchainBridge::new( Box::new(blockchain_interface_mock), - Box::new(persistent_configuration_mock), + Box::new(PersistentConfigurationMock::default()), false, Some(consuming_wallet.clone()), ); @@ -945,11 +879,14 @@ mod tests { pending_payable_opt: None, }, ]; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp); send_bind_message!(subject_subs, peer_actors); let _ = addr - .try_send(OutcomingPaymentsInstructions { - accounts: accounts.clone(), + .try_send(OutboundPaymentsInstructions { + affordable_accounts: accounts.clone(), + agent: Box::new(agent), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -961,31 +898,22 @@ mod tests { system.run(); let mut send_batch_of_payables_params = send_batch_of_payables_params_arc.lock().unwrap(); //cannot assert on the captured recipient as its actor is gone after the System stops spinning - let ( - consuming_wallet_actual, - gas_price_actual, - nonce_actual, - _recipient_actual, - accounts_actual, - ) = send_batch_of_payables_params.remove(0); + let (actual_agent_id_stamp, _recipient_actual, accounts_actual) = + send_batch_of_payables_params.remove(0); assert!(send_batch_of_payables_params.is_empty()); - assert_eq!(consuming_wallet_actual, consuming_wallet.clone()); - assert_eq!(gas_price_actual, expected_gas_price); - assert_eq!(nonce_actual, U256::from(1u64)); + assert_eq!(actual_agent_id_stamp, agent_id_stamp); assert_eq!(accounts_actual, accounts); - let get_transaction_count_params = get_transaction_count_params_arc.lock().unwrap(); - assert_eq!(*get_transaction_count_params, vec![consuming_wallet]); let accountant_recording = accountant_recording_arc.lock().unwrap(); let sent_payments_msg = accountant_recording.get_record::(0); assert_eq!( *sent_payments_msg, SentPayables { payment_procedure_result: Ok(vec![ - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_1, hash: H256::from("sometransactionhash".keccak256()) }), - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: wallet_account_2, hash: H256::from("someothertransactionhash".keccak256()) }) @@ -1000,9 +928,9 @@ mod tests { } #[test] - fn handle_report_accounts_payable_transmits_eleventh_hour_error_back_to_accountant() { + fn handle_outbound_payments_instructions_sends_eleventh_hour_error_back_to_accountant() { let system = System::new( - "handle_report_accounts_payable_transmits_eleventh_hour_error_back_to_accountant", + "handle_outbound_payments_instructions_sends_eleventh_hour_error_back_to_accountant", ); let (accountant, _, accountant_recording_arc) = make_recorder(); let hash = make_tx_hash(0xde); @@ -1014,10 +942,8 @@ mod tests { hashes: vec![hash], }); let blockchain_interface_mock = BlockchainInterfaceMock::default() - .get_transaction_count_result(Ok(U256::from(1u64))) .send_batch_of_payables_result(expected_error.clone()); - let persistent_configuration_mock = - PersistentConfigurationMock::default().gas_price_result(Ok(123)); + let persistent_configuration_mock = PersistentConfigurationMock::default(); let consuming_wallet = make_paying_wallet(b"somewallet"); let subject = BlockchainBridge::new( Box::new(blockchain_interface_mock), @@ -1034,11 +960,13 @@ mod tests { last_paid_timestamp: from_time_t(150_000_000), pending_payable_opt: None, }]; + let agent = BlockchainAgentMock::default(); send_bind_message!(subject_subs, peer_actors); let _ = addr - .try_send(OutcomingPaymentsInstructions { - accounts, + .try_send(OutboundPaymentsInstructions { + affordable_accounts: accounts, + agent: Box::new(agent), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -1079,149 +1007,46 @@ mod tests { assert_eq!(accountant_recording.len(), 2) } - #[test] - fn report_accounts_payable_returns_error_fetching_pending_nonce() { - let blockchain_interface_mock = BlockchainInterfaceMock::default() - .get_transaction_count_result(Err(BlockchainError::QueryFailed( - "What the hack...??".to_string(), - ))); - let consuming_wallet = make_wallet("somewallet"); - let persistent_configuration_mock = - PersistentConfigurationMock::new().gas_price_result(Ok(3u64)); - let subject = BlockchainBridge::new( - Box::new(blockchain_interface_mock), - Box::new(persistent_configuration_mock), - false, - Some(consuming_wallet), - ); - let request = OutcomingPaymentsInstructions { - accounts: vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 123_456, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }], - response_skeleton_opt: None, - }; - - let result = subject.process_payments(&request); - - assert_eq!( - result, - Err(PayableTransactionError::TransactionCount( - BlockchainError::QueryFailed("What the hack...??".to_string()) - )) - ); - } - #[test] fn process_payments_returns_error_from_sending_batch() { let transaction_hash = make_tx_hash(789); let blockchain_interface_mock = BlockchainInterfaceMock::default() - .get_transaction_count_result(Ok(web3::types::U256::from(1))) .send_batch_of_payables_result(Err(PayableTransactionError::Sending { - msg: "failure from exhaustion".to_string(), + msg: "failure from chronic exhaustion".to_string(), hashes: vec![transaction_hash], })); let consuming_wallet = make_wallet("somewallet"); - let persistent_configuration_mock = - PersistentConfigurationMock::new().gas_price_result(Ok(3u64)); + let persistent_configuration_mock = PersistentConfigurationMock::new(); let mut subject = BlockchainBridge::new( Box::new(blockchain_interface_mock), Box::new(persistent_configuration_mock), false, Some(consuming_wallet.clone()), ); - let request = OutcomingPaymentsInstructions { - accounts: vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 424_454, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }], - response_skeleton_opt: None, - }; + let checked_accounts = vec![PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 424_454, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }]; + let agent = Box::new(BlockchainAgentMock::default()); let (accountant, _, _) = make_recorder(); let fingerprint_recipient = accountant.start().recipient(); subject .pending_payable_confirmation .new_pp_fingerprints_sub_opt = Some(fingerprint_recipient); - let result = subject.process_payments(&request); + let result = subject.process_payments(agent, checked_accounts); assert_eq!( result, Err(PayableTransactionError::Sending { - msg: "failure from exhaustion".to_string(), + msg: "failure from chronic exhaustion".to_string(), hashes: vec![transaction_hash] }) ); } - #[test] - fn handle_report_accounts_payable_manages_gas_price_error() { - init_test_logging(); - let (accountant, _, accountant_recording_arc) = make_recorder(); - let accountant_addr = accountant - .system_stop_conditions(match_every_type_id!(ScanError)) - .start(); - let sent_payables_recipient = accountant_addr.clone().recipient(); - let scan_error_recipient = accountant_addr.recipient(); - let blockchain_interface_mock = BlockchainInterfaceMock::default() - .get_transaction_count_result(Ok(web3::types::U256::from(1))); - let persistent_configuration_mock = PersistentConfigurationMock::new() - .gas_price_result(Err(PersistentConfigError::TransactionError)); - let consuming_wallet = make_wallet("somewallet"); - let mut subject = BlockchainBridge::new( - Box::new(blockchain_interface_mock), - Box::new(persistent_configuration_mock), - false, - Some(consuming_wallet), - ); - subject.sent_payable_subs_opt = Some(sent_payables_recipient); - subject.scan_error_subs_opt = Some(scan_error_recipient); - let request = OutcomingPaymentsInstructions { - accounts: vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 42, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }], - response_skeleton_opt: None, - }; - let subject_addr = subject.start(); - let system = System::new("test"); - - subject_addr.try_send(request).unwrap(); - - system.run(); - let recording = accountant_recording_arc.lock().unwrap(); - let actual_sent_payable_msg = recording.get_record::(0); - assert_eq!( - actual_sent_payable_msg, - &SentPayables { - payment_procedure_result: Err(PayableTransactionError::GasPriceQueryFailed( - "TransactionError".to_string() - )), - response_skeleton_opt: None - } - ); - let actual_scan_err_msg = recording.get_record::(1); - assert_eq!( - actual_scan_err_msg, - &ScanError { - scan_type: ScanType::Payables, - response_skeleton_opt: None, - msg: "ReportAccountsPayable: Unsuccessful gas price query: \"TransactionError\"" - .to_string() - } - ); - assert_eq!(recording.len(), 2); - TestLogHandler::new().exists_log_containing( - "WARN: BlockchainBridge: ReportAccountsPayable: Unsuccessful gas price query: \"TransactionError\"", - ); - } - #[test] fn blockchain_bridge_processes_requests_for_transaction_receipts_when_all_were_ok() { let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); @@ -1298,10 +1123,16 @@ mod tests { .system_stop_conditions(match_every_type_id!(ScanError)) .start() .recipient(); - let blockchain_interface = BlockchainInterfaceMock::default().retrieve_transactions_result( - Err(BlockchainError::QueryFailed("we have no luck".to_string())), - ); - let persistent_config = PersistentConfigurationMock::new().start_block_result(Ok(5)); // no set_start_block_result: set_start_block() must not be called + let lower_interface = LowerBCIMock::default() + .get_block_number_result(LatestBlockNumber::Ok(U64::from(1234u64))); + let blockchain_interface = BlockchainInterfaceMock::default() + .retrieve_transactions_result(Err(BlockchainError::QueryFailed( + "we have no luck".to_string(), + ))) + .helpers_results(Box::new(lower_interface)); + let persistent_config = PersistentConfigurationMock::new() + .max_block_count_result(Ok(Some(100_000))) + .start_block_result(Ok(5)); // no set_start_block_result: set_start_block() must not be called let mut subject = BlockchainBridge::new( Box::new(blockchain_interface), Box::new(persistent_config), @@ -1326,12 +1157,12 @@ mod tests { &ScanError { scan_type: ScanType::Receivables, response_skeleton_opt: None, - msg: "Tried to retrieve received payments but failed: QueryFailed(\"we have no luck\")".to_string() + msg: "Attempted to retrieve received payments but failed: QueryFailed(\"we have no luck\")".to_string() } ); assert_eq!(recording.len(), 1); TestLogHandler::new().exists_log_containing( - "WARN: BlockchainBridge: Tried to retrieve \ + "WARN: BlockchainBridge: Attempted to retrieve \ received payments but failed: QueryFailed(\"we have no luck\")", ); } @@ -1453,7 +1284,7 @@ mod tests { let (accountant, _, accountant_recording) = make_recorder(); let recipient = accountant.start().recipient(); let mut subject = BlockchainBridge::new( - Box::new(BlockchainInterfaceClandestine::new(Chain::Dev)), + Box::new(BlockchainInterfaceMock::default()), Box::new(PersistentConfigurationMock::default()), false, Some(Wallet::new("mine")), @@ -1561,6 +1392,100 @@ mod tests { receipt for '0x000000000000000000000000000000000000000000000000000000000001b2e6' failed due to 'QueryFailed(\"booga\")'"); } + #[test] + fn handle_retrieve_transactions_uses_latest_block_number_upon_get_block_number_error() { + init_test_logging(); + let retrieve_transactions_params_arc = Arc::new(Mutex::new(vec![])); + let system = System::new( + "handle_retrieve_transactions_uses_latest_block_number_upon_get_block_number_error", + ); + let (accountant, _, accountant_recording_arc) = make_recorder(); + let earning_wallet = make_wallet("somewallet"); + let amount = 42; + let amount2 = 55; + let expected_transactions = RetrievedBlockchainTransactions { + new_start_block: 8675309u64, + transactions: vec![ + BlockchainTransaction { + block_number: 7, + from: earning_wallet.clone(), + wei_amount: amount, + }, + BlockchainTransaction { + block_number: 9, + from: earning_wallet.clone(), + wei_amount: amount2, + }, + ], + }; + let rpc_helpers = LowerBCIMock::default().get_block_number_result(LatestBlockNumber::Err( + BlockchainError::QueryFailed("Failed to read the latest block number".to_string()), + )); + let blockchain_interface_mock = BlockchainInterfaceMock::default() + .retrieve_transactions_params(&retrieve_transactions_params_arc) + .retrieve_transactions_result(Ok(expected_transactions.clone())) + .helpers_results(Box::new(rpc_helpers)); + let set_start_block_params_arc = Arc::new(Mutex::new(vec![])); + let persistent_config = PersistentConfigurationMock::new() + .max_block_count_result(Ok(Some(10000u64))) + .start_block_result(Ok(6)) + .set_start_block_params(&set_start_block_params_arc) + .set_start_block_result(Ok(())); + let subject = BlockchainBridge::new( + Box::new(blockchain_interface_mock), + Box::new(persistent_config), + false, + Some(make_wallet("consuming")), + ); + + let addr = subject.start(); + let subject_subs = BlockchainBridge::make_subs_from(&addr); + let peer_actors = peer_actors_builder().accountant(accountant).build(); + send_bind_message!(subject_subs, peer_actors); + let retrieve_transactions = RetrieveTransactions { + recipient: earning_wallet.clone(), + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321, + }), + }; + let before = SystemTime::now(); + + let _ = addr.try_send(retrieve_transactions).unwrap(); + + System::current().stop(); + system.run(); + let after = SystemTime::now(); + let set_start_block_params = set_start_block_params_arc.lock().unwrap(); + assert_eq!(*set_start_block_params, vec![8675309u64]); + let retrieve_transactions_params = retrieve_transactions_params_arc.lock().unwrap(); + assert_eq!( + *retrieve_transactions_params, + vec![( + BlockNumber::Number(6u64.into()), + BlockNumber::Number(10006u64.into()), + earning_wallet + )] + ); + let accountant_received_payment = accountant_recording_arc.lock().unwrap(); + assert_eq!(accountant_received_payment.len(), 1); + let received_payments = accountant_received_payment.get_record::(0); + check_timestamp(before, received_payments.timestamp, after); + assert_eq!( + received_payments, + &ReceivedPayments { + timestamp: received_payments.timestamp, + payments: expected_transactions.transactions, + response_skeleton_opt: Some(ResponseSkeleton { + client_id: 1234, + context_id: 4321 + }), + } + ); + + TestLogHandler::new().exists_log_containing("INFO: BlockchainBridge: Using 'latest' block number instead of a literal number. QueryFailed(\"Failed to read the latest block number\")"); + } + #[test] fn handle_retrieve_transactions_sends_received_payments_back_to_accountant() { let retrieve_transactions_params_arc = Arc::new(Mutex::new(vec![])); @@ -1585,11 +1510,15 @@ mod tests { }, ], }; + let latest_block_number = LatestBlockNumber::Ok(1024u64.into()); + let rpc_helpers = LowerBCIMock::default().get_block_number_result(latest_block_number); let blockchain_interface_mock = BlockchainInterfaceMock::default() .retrieve_transactions_params(&retrieve_transactions_params_arc) - .retrieve_transactions_result(Ok(expected_transactions.clone())); + .retrieve_transactions_result(Ok(expected_transactions.clone())) + .helpers_results(Box::new(rpc_helpers)); let set_start_block_params_arc = Arc::new(Mutex::new(vec![])); let persistent_config = PersistentConfigurationMock::new() + .max_block_count_result(Ok(Some(10000u64))) .start_block_result(Ok(6)) .set_start_block_params(&set_start_block_params_arc) .set_start_block_result(Ok(())); @@ -1618,9 +1547,16 @@ mod tests { system.run(); let after = SystemTime::now(); let set_start_block_params = set_start_block_params_arc.lock().unwrap(); - assert_eq!(*set_start_block_params, vec![1234]); + assert_eq!(*set_start_block_params, vec![1234u64]); let retrieve_transactions_params = retrieve_transactions_params_arc.lock().unwrap(); - assert_eq!(*retrieve_transactions_params, vec![(6, earning_wallet)]); + assert_eq!( + *retrieve_transactions_params, + vec![( + BlockNumber::Number(6u64.into()), + BlockNumber::Number(1024u64.into()), + earning_wallet + )] + ); let accountant_received_payment = accountant_recording_arc.lock().unwrap(); assert_eq!(accountant_received_payment.len(), 1); let received_payments = accountant_received_payment.get_record::(0); @@ -1641,13 +1577,16 @@ mod tests { #[test] fn processing_of_received_payments_continues_even_if_no_payments_are_detected() { init_test_logging(); + let rpc_helpers = LowerBCIMock::default().get_block_number_result(Ok(0u64.into())); let blockchain_interface_mock = BlockchainInterfaceMock::default() .retrieve_transactions_result(Ok(RetrievedBlockchainTransactions { new_start_block: 7, transactions: vec![], - })); + })) + .helpers_results(Box::new(rpc_helpers)); let set_start_block_params_arc = Arc::new(Mutex::new(vec![])); let persistent_config = PersistentConfigurationMock::new() + .max_block_count_result(Ok(Some(10000u64))) .start_block_result(Ok(6)) .set_start_block_params(&set_start_block_params_arc) .set_start_block_result(Ok(())); @@ -1704,10 +1643,13 @@ mod tests { expected = "Cannot retrieve start block from database; payments to you may not be processed: TransactionError" )] fn handle_retrieve_transactions_panics_if_start_block_cannot_be_read() { + let rpc_helpers = LowerBCIMock::default().get_block_number_result(Ok(0u64.into())); + let blockchain_interface = + BlockchainInterfaceMock::default().helpers_results(Box::new(rpc_helpers)); let persistent_config = PersistentConfigurationMock::new() .start_block_result(Err(PersistentConfigError::TransactionError)); let mut subject = BlockchainBridge::new( - Box::new(BlockchainInterfaceMock::default()), + Box::new(blockchain_interface), Box::new(persistent_config), false, None, //not needed in this test @@ -1722,22 +1664,24 @@ mod tests { #[test] #[should_panic( - expected = "Cannot set start block in database; payments to you may not be processed: TransactionError" + expected = "Cannot set start block 1234 in database; payments to you may not be processed: TransactionError" )] fn handle_retrieve_transactions_panics_if_start_block_cannot_be_written() { let persistent_config = PersistentConfigurationMock::new() .start_block_result(Ok(1234)) - .set_start_block_result(Err(PersistentConfigError::TransactionError)); - let blockchain_interface = BlockchainInterfaceMock::default().retrieve_transactions_result( - Ok(RetrievedBlockchainTransactions { + .set_start_block_result(Err(PersistentConfigError::TransactionError)) + .max_block_count_result(Ok(Some(10000u64))); + let rpc_helpers = LowerBCIMock::default().get_block_number_result(Ok(0u64.into())); + let blockchain_interface = BlockchainInterfaceMock::default() + .retrieve_transactions_result(Ok(RetrievedBlockchainTransactions { new_start_block: 1234, transactions: vec![BlockchainTransaction { block_number: 1000, from: make_wallet("somewallet"), wei_amount: 2345, }], - }), - ); + })) + .helpers_results(Box::new(rpc_helpers)); let mut subject = BlockchainBridge::new( Box::new(blockchain_interface), Box::new(persistent_config), @@ -1895,6 +1839,127 @@ mod tests { prove_that_crash_request_handler_is_hooked_up(subject, CRASH_KEY); } + #[test] + fn extract_max_block_range_from_error_response() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32005), message: \"eth_getLogs block range too large, range: 33636, max: 3500\", data: None }".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(Some(3500u64), max_block_count); + } + + #[test] + fn extract_max_block_range_from_pokt_error_response() { + let result = BlockchainError::QueryFailed("Rpc(Error { code: ServerError(-32001), message: \"Relay request failed validation: invalid relay request: eth_getLogs block range limit (100000 blocks) exceeded\", data: None })".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(Some(100000u64), max_block_count); + } + /* + POKT (Polygon mainnet and mumbai) + {"jsonrpc":"2.0","id":7,"error":{"message":"You cannot query logs for more than 100000 blocks at once.","code":-32064}} + */ + /* + Ankr + {"jsonrpc":"2.0","error":{"code":-32600,"message":"block range is too wide"},"id":null}% + */ + #[test] + fn extract_max_block_range_for_ankr_error_response() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32600), message: \"block range is too wide\", data: None }".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(None, max_block_count); + } + + /* + MaticVigil + [{"error":{"message":"Blockheight too far in the past. Check params passed to eth_getLogs or eth_call requests.Range of blocks allowed for your plan: 1000","code":-32005},"jsonrpc":"2.0","id":7},{"error":{"message":"Blockheight too far in the past. Check params passed to eth_getLogs or eth_call requests.Range of blocks allowed for your plan: 1000","code":-32005},"jsonrpc":"2.0","id":8}]% + */ + #[test] + fn extract_max_block_range_for_matic_vigil_error_response() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32005), message: \"Blockheight too far in the past. Check params passed to eth_getLogs or eth_call requests.Range of blocks allowed for your plan: 1000\", data: None }".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(Some(1000), max_block_count); + } + + /* + Blockpi + [{"jsonrpc":"2.0","id":7,"result":"0x21db466"},{"jsonrpc":"2.0","id":8,"error":{"code":-32602,"message":"eth_getLogs is limited to 1024 block range. Please check the parameter requirements at https://docs.blockpi.io/documentations/api-reference"}}] + */ + #[test] + fn extract_max_block_range_for_blockpi_error_response() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32005), message: \"eth_getLogs is limited to 1024 block range. Please check the parameter requirements at https://docs.blockpi.io/documentations/api-reference\", data: None }".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(Some(1024), max_block_count); + } + + /* + blastapi - completely rejected call on Public endpoint as won't handle eth_getLogs method on public API + [{"jsonrpc":"2.0","id":2,"error":{"code":-32601,"message":"Method not found","data":{"method":""}}},{"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"Invalid Request","data":{"message":"Cancelled due to validation errors in batch request"}}}] (edited) + [8:50 AM] + */ + + #[test] + fn extract_max_block_range_for_blastapi_error_response() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32601), message: \"Method not found\", data: \"'eth_getLogs' is not available on our public API. Head over to https://docs.blastapi.io/blast-documentation/tutorials-and-guides/using-blast-to-get-a-blockchain-endpoint for more information\" }".to_string()); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(None, max_block_count); + } + + #[test] + fn extract_max_block_range_for_expected_batch_got_single_error_response() { + let result = BlockchainError::QueryFailed( + "Got invalid response: Expected batch, got single.".to_string(), + ); + let subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceMock::default()), + Box::new(PersistentConfigurationMock::default()), + false, + None, + ); + let max_block_count = subject.extract_max_block_count(result); + + assert_eq!(Some(1000), max_block_count); + } + #[test] fn make_connections_implements_panic_on_migration() { let data_dir = ensure_node_home_directory_exists( @@ -1903,13 +1968,150 @@ mod tests { ); let act = |data_dir: &Path| { - BlockchainBridge::make_connections( - Some("http://127.0.0.1".to_string()), - data_dir.to_path_buf(), - Chain::PolyMumbai, - ); + BlockchainBridge::initialize_persistent_configuration(data_dir); }; assert_on_initialization_with_panic_on_migration(&data_dir, &act); } } + +#[cfg(test)] +pub mod exportable_test_parts { + use super::*; + use crate::bootstrapper::BootstrapperConfig; + use crate::test_utils::http_test_server::TestServer; + use crate::test_utils::make_wallet; + use crate::test_utils::recorder::make_blockchain_bridge_subs_from_recorder; + use crate::test_utils::unshared_test_utils::{AssertionsMessage, SubsFactoryTestAddrLeaker}; + use actix::System; + use crossbeam_channel::{bounded, Receiver}; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; + use masq_lib::utils::find_free_port; + use serde_json::Value::Object; + use serde_json::{Map, Value}; + use std::net::Ipv4Addr; + + impl SubsFactory + for SubsFactoryTestAddrLeaker + { + fn make(&self, addr: &Addr) -> BlockchainBridgeSubs { + self.send_leaker_msg_and_return_meaningless_subs( + addr, + make_blockchain_bridge_subs_from_recorder, + ) + } + } + + pub fn test_blockchain_bridge_is_constructed_with_correctly_functioning_connections( + test_module: &str, + test_name: &str, + act: A, + ) where + A: FnOnce( + BootstrapperConfig, + SubsFactoryTestAddrLeaker, + ) -> BlockchainBridgeSubs, + { + fn prepare_db_with_unique_value(data_dir: &Path, gas_price: u64) { + let mut persistent_config = { + let conn = DbInitializerReal::default() + .initialize(data_dir, DbInitializationConfig::test_default()) + .unwrap(); + PersistentConfigurationReal::from(conn) + }; + persistent_config.set_gas_price(gas_price).unwrap() + } + fn launch_prepared_test_server() -> (TestServer, String) { + let port = find_free_port(); + let server_url = format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port); + ( + TestServer::start( + port, + vec![br#"{"jsonrpc":"2.0","id":0,"result":someGarbage}"#.to_vec()], + ), + server_url, + ) + } + fn send_rpc_request_to_assert_on_later_and_assert_db_connection( + actor_addr_rx: Receiver>, + wallet: Wallet, + expected_gas_price: u64, + ) { + let blockchain_bridge_addr = actor_addr_rx.try_recv().unwrap(); + let msg = AssertionsMessage { + assertions: Box::new(move |bb: &mut BlockchainBridge| { + // We will assert on soundness of the connection by checking the receipt of + // this request + let _result = bb + .blockchain_interface + .lower_interface() + .get_service_fee_balance(&wallet); + + // Asserting that we can look into the expected db from here, meaning the + // PersistentConfiguration was set up correctly + assert_eq!( + bb.persistent_config.gas_price().unwrap(), + expected_gas_price + ); + // I don't know why exactly but the standard position + // of this call doesn't work + System::current().stop(); + }), + }; + blockchain_bridge_addr.try_send(msg).unwrap(); + } + fn assert_blockchain_interface_connection(test_server: &TestServer, wallet: Wallet) { + let requests = test_server.requests_so_far(); + let bodies: Vec = requests + .into_iter() + .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .collect(); + let params = &bodies[0]["params"]; + let expected_params = { + let mut map = Map::new(); + let hashed_data = format!( + "0x70a08231000000000000000000000000{}", + &wallet.to_string()[2..] + ); + map.insert("data".to_string(), Value::String(hashed_data)); + map.insert( + "to".to_string(), + Value::String(format!("{:?}", TEST_DEFAULT_CHAIN.rec().contract)), + ); + map + }; + assert_eq!( + params, + &Value::Array(vec![ + Object(expected_params), + Value::String("latest".to_string()) + ]) + ); + } + + let data_dir = ensure_node_home_directory_exists(test_module, test_name); + let gas_price = 444; + prepare_db_with_unique_value(&data_dir, gas_price); + let (test_server, server_url) = launch_prepared_test_server(); + let wallet = make_wallet("abc"); + let mut bootstrapper_config = BootstrapperConfig::new(); + bootstrapper_config + .blockchain_bridge_config + .blockchain_service_url_opt = Some(server_url); + bootstrapper_config.blockchain_bridge_config.chain = TEST_DEFAULT_CHAIN; + bootstrapper_config.data_directory = data_dir; + let (tx, blockchain_bridge_addr_rx) = bounded(1); + let address_leaker = SubsFactoryTestAddrLeaker { address_leaker: tx }; + let system = System::new(test_name); + + act(bootstrapper_config, address_leaker); + + send_rpc_request_to_assert_on_later_and_assert_db_connection( + blockchain_bridge_addr_rx, + wallet.clone(), + gas_price, + ); + assert_eq!(system.run(), 0); + assert_blockchain_interface_connection(&test_server, wallet) + } +} diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_null/lower_level_interface_null.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_null/lower_level_interface_null.rs new file mode 100644 index 000000000..5d97264b7 --- /dev/null +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_null/lower_level_interface_null.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::blockchain::blockchain_interface::data_structures::errors::BlockchainError; +use crate::blockchain::blockchain_interface::lower_level_interface::{ + LatestBlockNumber, LowerBCI, ResultForBalance, ResultForNonce, +}; +use crate::sub_lib::wallet::Wallet; +use masq_lib::logger::Logger; + +pub struct LowerBCINull { + logger: Logger, +} + +impl LowerBCI for LowerBCINull { + fn get_transaction_fee_balance(&self, _wallet: &Wallet) -> ResultForBalance { + Err(self.handle_null_call("transaction fee balance")) + } + + fn get_service_fee_balance(&self, _wallet: &Wallet) -> ResultForBalance { + Err(self.handle_null_call("masq balance")) + } + + fn get_block_number(&self) -> LatestBlockNumber { + Err(self.handle_null_call("block number")) + } + + fn get_transaction_id(&self, _wallet: &Wallet) -> ResultForNonce { + Err(self.handle_null_call("transaction id")) + } +} + +impl LowerBCINull { + pub fn new(logger: &Logger) -> Self { + Self { + logger: logger.clone(), + } + } + + fn handle_null_call(&self, operation: &str) -> BlockchainError { + error!(self.logger, "Null version can't fetch {operation}"); + BlockchainError::UninitializedBlockchainInterface + } +} + +#[cfg(test)] +mod tests { + use crate::blockchain::blockchain_interface::BlockchainError; + use crate::blockchain::blockchain_interface::blockchain_interface_null::lower_level_interface_null::LowerBCINull; + use crate::blockchain::blockchain_interface::lower_level_interface::LowerBCI; + use crate::sub_lib::wallet::Wallet; + use crate::test_utils::make_wallet; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use std::fmt::Debug; + + #[test] + fn lower_bci_null_gets_no_transaction_fee_balance() { + let test_name = "lower_bci_null_gets_no_transaction_fee_balance"; + let act = + |subject: &LowerBCINull, wallet: &Wallet| subject.get_transaction_fee_balance(wallet); + + test_null_method(test_name, act, "transaction fee balance"); + } + + #[test] + fn lower_bci_null_gets_no_masq_balance() { + let test_name = "lower_bci_null_gets_no_masq_balance"; + let act = |subject: &LowerBCINull, wallet: &Wallet| subject.get_service_fee_balance(wallet); + + test_null_method(test_name, act, "masq balance"); + } + + #[test] + fn lower_bci_null_gets_no_block_number() { + let test_name = "lower_bci_null_gets_no_block_number"; + let act = |subject: &LowerBCINull, _wallet: &Wallet| subject.get_block_number(); + + test_null_method(test_name, act, "block number"); + } + + #[test] + fn lower_bci_null_gets_no_transaction_id() { + let test_name = "lower_bci_null_gets_no_transaction_id"; + let act = |subject: &LowerBCINull, wallet: &Wallet| subject.get_transaction_id(wallet); + + test_null_method(test_name, act, "transaction id"); + } + + fn test_null_method( + test_name: &str, + act: fn(&LowerBCINull, &Wallet) -> Result, + expected_method_name: &str, + ) { + init_test_logging(); + let wallet = make_wallet("blah"); + let subject = LowerBCINull::new(&Logger::new(test_name)); + + let result = act(&subject, &wallet); + + assert_eq!( + result, + Err(BlockchainError::UninitializedBlockchainInterface) + ); + let _expected_log_msg = TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Null version can't fetch {expected_method_name}" + )); + } +} diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_null/mod.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_null/mod.rs new file mode 100644 index 000000000..ed86f701d --- /dev/null +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_null/mod.rs @@ -0,0 +1,281 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod lower_level_interface_null; + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; +use crate::blockchain::blockchain_interface::blockchain_interface_null::lower_level_interface_null::LowerBCINull; +use crate::blockchain::blockchain_interface::lower_level_interface::LowerBCI; +use crate::db_config::persistent_configuration::PersistentConfiguration; +use crate::sub_lib::wallet::Wallet; +use actix::Recipient; +use masq_lib::logger::Logger; +use web3::types::{Address, BlockNumber, H160, H256}; +use crate::blockchain::blockchain_interface::BlockchainInterface; +use crate::blockchain::blockchain_interface::data_structures::errors::{BlockchainAgentBuildError, BlockchainError, PayableTransactionError, ResultForReceipt}; +use crate::blockchain::blockchain_interface::data_structures::{ProcessedPayableFallible, RetrievedBlockchainTransactions}; + +pub struct BlockchainInterfaceNull { + logger: Logger, + helper: Box, +} + +impl BlockchainInterface for BlockchainInterfaceNull { + fn contract_address(&self) -> Address { + self.log_uninitialized_for_operation("get contract address"); + H160::zero() + } + + fn retrieve_transactions( + &self, + _start_block: BlockNumber, + _end_block: BlockNumber, + _wallet: &Wallet, + ) -> Result { + self.handle_uninitialized_interface("retrieve transactions") + } + + fn build_blockchain_agent( + &self, + _consuming_wallet: &Wallet, + _persistent_config: &dyn PersistentConfiguration, + ) -> Result, BlockchainAgentBuildError> { + self.handle_uninitialized_interface("build blockchain agent") + } + + fn send_batch_of_payables( + &self, + _agent: Box, + _new_fingerprints_recipient: &Recipient, + _accounts: &[PayableAccount], + ) -> Result, PayableTransactionError> { + self.handle_uninitialized_interface("pay for payables") + } + + fn get_transaction_receipt(&self, _hash: H256) -> ResultForReceipt { + self.handle_uninitialized_interface("get transaction receipt") + } + + fn lower_interface(&self) -> &dyn LowerBCI { + error!(self.logger, "Provides null RPC helpers only"); + &*self.helper + } + + as_any_in_trait_impl!(); +} + +impl Default for BlockchainInterfaceNull { + fn default() -> Self { + Self::new() + } +} + +trait BlockchainInterfaceUninitializedError { + fn error() -> Self; +} + +macro_rules! impl_bci_uninitialized { + ($($error_type: ty),+) => { + $( + impl BlockchainInterfaceUninitializedError for $error_type { + fn error() -> Self { + Self::UninitializedBlockchainInterface + } + } + )+ + } +} + +impl_bci_uninitialized!( + PayableTransactionError, + BlockchainError, + BlockchainAgentBuildError +); + +impl BlockchainInterfaceNull { + pub fn new() -> Self { + let logger = Logger::new("BlockchainInterface"); + let helper = Box::new(LowerBCINull::new(&logger)); + BlockchainInterfaceNull { logger, helper } + } + + fn handle_uninitialized_interface( + &self, + operation: &str, + ) -> Result + where + E: BlockchainInterfaceUninitializedError, + { + self.log_uninitialized_for_operation(operation); + let err = E::error(); + Err(err) + } + + fn log_uninitialized_for_operation(&self, operation: &str) { + error!( + self.logger, + "Failed to {} with uninitialized blockchain \ + interface. Parameter blockchain-service-url is missing.", + operation + ) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_null::BlockchainAgentNull; + use crate::accountant::test_utils::make_payable_account; + use crate::blockchain::blockchain_interface::blockchain_interface_null::lower_level_interface_null::LowerBCINull; + use crate::blockchain::blockchain_interface::blockchain_interface_null::{ + BlockchainInterfaceNull, BlockchainInterfaceUninitializedError, + }; + use crate::blockchain::test_utils::make_tx_hash; + use crate::test_utils::make_wallet; + use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; + use crate::test_utils::recorder::make_recorder; + use actix::Actor; + use ethereum_types::U64; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use web3::types::{BlockNumber, H160}; + use crate::blockchain::blockchain_interface::BlockchainInterface; + use crate::blockchain::blockchain_interface::data_structures::errors::{BlockchainAgentBuildError, BlockchainError, PayableTransactionError}; + + fn make_subject(test_name: &str) -> BlockchainInterfaceNull { + let logger = Logger::new(test_name); + let helper = Box::new(LowerBCINull::new(&logger)); + BlockchainInterfaceNull { logger, helper } + } + + #[test] + fn blockchain_interface_null_returns_contract_address() { + let result = make_subject("irrelevant").contract_address(); + + assert_eq!(result, H160::zero()) + } + + #[test] + fn blockchain_interface_null_retrieves_no_transactions() { + init_test_logging(); + let test_name = "blockchain_interface_null_retrieves_no_transactions"; + let wallet = make_wallet("blah"); + + let result = make_subject(test_name).retrieve_transactions( + BlockNumber::Number(U64::zero()), + BlockNumber::Latest, + &wallet, + ); + + assert_eq!( + result, + Err(BlockchainError::UninitializedBlockchainInterface) + ); + let expected_msg = "Failed to retrieve transactions with uninitialized blockchain \ + interface. Parameter blockchain-service-url is missing."; + let expected_log_msg = format!("ERROR: {test_name}: {}", expected_msg); + TestLogHandler::new().exists_log_containing(expected_log_msg.as_str()); + } + + #[test] + fn blockchain_interface_null_builds_null_agent() { + init_test_logging(); + let test_name = "blockchain_interface_null_builds_null_agent"; + let wallet = make_wallet("blah"); + let persistent_config = PersistentConfigurationMock::new(); + let subject = make_subject(test_name); + + let result = subject.build_blockchain_agent(&wallet, &persistent_config); + + let err = match result { + Ok(_) => panic!("we expected an error but got ok"), + Err(e) => e, + }; + assert_eq!( + err, + BlockchainAgentBuildError::UninitializedBlockchainInterface + ); + let expected_log_msg = format!( + "ERROR: {test_name}: Failed to build blockchain agent with uninitialized blockchain \ + interface. Parameter blockchain-service-url is missing." + ); + TestLogHandler::new().exists_log_containing(expected_log_msg.as_str()); + } + + #[test] + fn blockchain_interface_null_cannot_send_batch_of_payables() { + init_test_logging(); + let test_name = "blockchain_interface_null_cannot_send_batch_of_payables"; + let (recorder, _, _) = make_recorder(); + let recipient = recorder.start().recipient(); + let accounts = vec![make_payable_account(111)]; + let agent = Box::new(BlockchainAgentNull::new()); + + let result = make_subject(test_name).send_batch_of_payables(agent, &recipient, &accounts); + + assert_eq!( + result, + Err(PayableTransactionError::UninitializedBlockchainInterface) + ); + let expected_log_msg = format!( + "ERROR: {test_name}: Failed to pay for payables with uninitialized blockchain \ + interface. Parameter blockchain-service-url is missing." + ); + TestLogHandler::new().exists_log_containing(expected_log_msg.as_str()); + } + + #[test] + fn blockchain_interface_null_gets_no_transaction_receipt() { + init_test_logging(); + let test_name = "blockchain_interface_null_gets_no_transaction_receipt"; + let tx_hash = make_tx_hash(123); + + let result = make_subject(test_name).get_transaction_receipt(tx_hash); + + assert_eq!( + result, + Err(BlockchainError::UninitializedBlockchainInterface) + ); + let expected_log_msg = format!( + "ERROR: {test_name}: Failed to get transaction receipt with uninitialized \ + blockchain interface. Parameter blockchain-service-url is missing." + ); + TestLogHandler::new().exists_log_containing(expected_log_msg.as_str()); + } + + #[test] + fn blockchain_interface_null_gives_null_helper() { + init_test_logging(); + let test_name = "blockchain_interface_null_gives_null_helper"; + let wallet = make_wallet("abc"); + + let _ = make_subject(test_name) + .lower_interface() + .get_transaction_id(&wallet); + + let expected_log_msg_from_helpers_call = + format!("ERROR: {test_name}: Provides null RPC helpers only"); + let expected_log_msg_from_rcp_call = + format!("ERROR: {test_name}: Null version can't fetch transaction id"); + TestLogHandler::new().assert_logs_contain_in_order(vec![ + expected_log_msg_from_helpers_call.as_str(), + expected_log_msg_from_rcp_call.as_str(), + ]); + } + + #[test] + fn blockchain_interface_null_error_is_implemented_for_blockchain_error() { + assert_eq!( + BlockchainError::error(), + BlockchainError::UninitializedBlockchainInterface + ) + } + + #[test] + fn blockchain_interface_null_error_is_implemented_for_payable_transaction_error() { + assert_eq!( + PayableTransactionError::error(), + PayableTransactionError::UninitializedBlockchainInterface + ) + } +} diff --git a/node/src/blockchain/batch_payable_tools.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/batch_payable_tools.rs similarity index 96% rename from node/src/blockchain/batch_payable_tools.rs rename to node/src/blockchain/blockchain_interface/blockchain_interface_web3/batch_payable_tools.rs index f57a3f12c..ed0e21d15 100644 --- a/node/src/blockchain/batch_payable_tools.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/batch_payable_tools.rs @@ -92,8 +92,10 @@ impl BatchPayableTools for BatchPayableToolsReal +where + T: BatchTransport, +{ + web3: Rc>, + // TODO waiting for GH-707 (note: consider to query the balances together with the id) + _batch_web3: Rc>>, + contract: Contract, +} + +impl LowerBCI for LowerBCIWeb3 +where + T: BatchTransport, +{ + fn get_transaction_fee_balance(&self, wallet: &Wallet) -> ResultForBalance { + self.web3 + .eth() + .balance(wallet.address(), None) + .map_err(|e| BlockchainError::QueryFailed(format!("{} for wallet {}", e, wallet))) + .wait() + } + + fn get_service_fee_balance(&self, wallet: &Wallet) -> ResultForBalance { + self.contract + .query( + "balanceOf", + wallet.address(), + None, + Options::default(), + None, + ) + .map_err(|e| BlockchainError::QueryFailed(format!("{} for wallet {}", e, wallet))) + .wait() + } + + fn get_block_number(&self) -> LatestBlockNumber { + self.web3 + .eth() + .block_number() + .map_err(|e| BlockchainError::QueryFailed(e.to_string())) + .wait() + } + + fn get_transaction_id(&self, wallet: &Wallet) -> ResultForNonce { + self.web3 + .eth() + .transaction_count(wallet.address(), Some(BlockNumber::Pending)) + .map_err(|e| BlockchainError::QueryFailed(format!("{} for wallet {}", e, wallet))) + .wait() + } +} + +impl LowerBCIWeb3 +where + T: BatchTransport, +{ + pub fn new(web3: Rc>, batch_web3: Rc>>, contract: Contract) -> Self { + Self { + web3, + _batch_web3: batch_web3, + contract, + } + } +} + +#[cfg(test)] +mod tests { + use crate::blockchain::blockchain_interface::blockchain_interface_web3::lower_level_interface_web3::LowerBCIWeb3; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ + CONTRACT_ABI, REQUESTS_IN_PARALLEL, + }; + use crate::blockchain::blockchain_interface::lower_level_interface::{LowerBCI, ResultForBalance}; + use crate::blockchain::blockchain_interface::BlockchainError; + use crate::sub_lib::wallet::Wallet; + use crate::test_utils::http_test_server::TestServer; + use crate::test_utils::make_paying_wallet; + use ethereum_types::U64; + use masq_lib::blockchains::chains::Chain; + use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; + use masq_lib::utils::find_free_port; + use serde_json::{json, Value}; + use std::net::Ipv4Addr; + use std::rc::Rc; + use std::str::FromStr; + use std::sync::{Arc, Mutex}; + use web3::contract::Contract; + use web3::transports::{Batch, Http}; + use web3::types::U256; + use web3::{BatchTransport, Web3}; + use crate::blockchain::test_utils::TestTransport; + + #[test] + fn web3_helper_transaction_fee_balance_works() { + let port = find_free_port(); + let test_server = TestServer::start( + port, + vec![br#"{"jsonrpc":"2.0","id":0,"result":"0xDEADBEEF"}"#.to_vec()], + ); + let (_event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let subject = make_subject(transport, chain); + + let result = subject + .get_transaction_fee_balance( + &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), + ) + .unwrap(); + + assert_eq!(result, U256::from(0xDEADBEEF_u64)); + let requests = test_server.requests_so_far(); + let bodies: Vec = requests + .into_iter() + .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .collect(); + assert_eq!(bodies[0]["method"].to_string(), "\"eth_getBalance\"",); + assert_eq!( + bodies[0]["params"][0].to_string(), + "\"0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc\"", + ); + assert_eq!(bodies.len(), 1) + } + + #[test] + #[should_panic(expected = "No address for an uninitialized wallet!")] + fn web3_helper_get_transaction_fee_balance_returns_err_for_an_invalid_wallet() { + let port = 8545; + let (_event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let subject = make_subject(transport, chain); + + let result = subject.get_transaction_fee_balance(&Wallet::new("0x_invalid_wallet_address")); + + assert_eq!(result, Err(BlockchainError::InvalidAddress)); + } + + #[test] + fn web3_helper_get_transaction_fee_balance_returns_err_for_unintelligible_response() { + let act = |subject: &LowerBCIWeb3, wallet: &Wallet| { + subject.get_transaction_fee_balance(wallet) + }; + + assert_error_from_unintelligible_response(act, "invalid hex character"); + } + + #[test] + fn web3_helper_get_masq_balance_works() { + let port = find_free_port(); + let test_server = TestServer::start (port, vec![ + br#"{"jsonrpc":"2.0","id":0,"result":"0x00000000000000000000000000000000000000000000000000000000DEADBEEF"}"#.to_vec() + ]); + let (_event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let subject = make_subject(transport, chain); + + let result = subject + .get_service_fee_balance( + &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), + ) + .unwrap(); + + assert_eq!(result, U256::from(0xDEADBEEF_u64)); + let requests = test_server.requests_so_far(); + let bodies: Vec = requests + .into_iter() + .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .collect(); + assert_eq!(bodies[0]["method"].to_string(), "\"eth_call\"",); + let contract_address = chain.rec().contract; + assert_eq!( + bodies[0]["params"][0]["to"].to_string(), + format!("\"{:?}\"", contract_address), + ); + assert_eq!(bodies.len(), 1) + } + + #[test] + #[should_panic(expected = "No address for an uninitialized wallet!")] + fn web3_helper_get_masq_balance_returns_err_for_an_invalid_wallet() { + let port = 8545; + let (_event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let subject = make_subject(transport, chain); + + let result = subject.get_service_fee_balance(&Wallet::new("0x_invalid_wallet_address")); + + assert_eq!(result, Err(BlockchainError::InvalidAddress)); + } + + #[test] + fn web3_helper_get_masq_balance_returns_err_for_unintelligible_response() { + let act = + |subject: &LowerBCIWeb3, wallet: &Wallet| subject.get_service_fee_balance(wallet); + + assert_error_from_unintelligible_response(act, "Invalid hex"); + } + + #[test] + fn web3_helper_can_fetch_latest_block_number_successfully() { + let prepare_params_arc = Arc::new(Mutex::new(vec![])); + let transport = TestTransport::default() + .prepare_params(&prepare_params_arc) + .send_result(json!("0x1e37066")); + let subject = make_subject(transport, TEST_DEFAULT_CHAIN); + + let latest_block_number = subject.get_block_number().unwrap(); + + assert_eq!(latest_block_number, U64::from(0x1e37066u64)); + let mut prepare_params = prepare_params_arc.lock().unwrap(); + let (method_name, actual_arguments) = prepare_params.remove(0); + assert!(prepare_params.is_empty()); + assert_eq!(method_name, "eth_blockNumber".to_string()); + let expected_arguments: Vec = vec![]; + assert_eq!(actual_arguments, expected_arguments); + } + + #[test] + fn web3_helper_handles_latest_null_block_number_error() { + let prepare_params_arc = Arc::new(Mutex::new(vec![])); + let transport = TestTransport::default() + .prepare_params(&prepare_params_arc) + .send_result(Value::Null); + let subject = make_subject(transport, TEST_DEFAULT_CHAIN); + + let expected_error = subject.get_block_number().unwrap_err(); + + assert_eq!( + expected_error, + BlockchainError::QueryFailed( + "Decoder error: Error(\"invalid type: null, expected \ + a 0x-prefixed hex string with length between (0; 16]\", line: 0, column: 0)" + .to_string() + ) + ); + let mut prepare_params = prepare_params_arc.lock().unwrap(); + let (method_name, actual_arguments) = prepare_params.remove(0); + assert!(prepare_params.is_empty()); + assert_eq!(method_name, "eth_blockNumber".to_string()); + let expected_arguments: Vec = vec![]; + assert_eq!(actual_arguments, expected_arguments); + } + + #[test] + fn web3_helper_can_handle_latest_string_block_number_error() { + let prepare_params_arc: Arc)>>> = + Arc::new(Mutex::new(vec![])); + let transport = TestTransport::default() + .prepare_params(&prepare_params_arc) + .send_result(Value::String("this is an invalid block number".to_string())); + let subject = make_subject(transport, TEST_DEFAULT_CHAIN); + + let expected_error = subject.get_block_number().unwrap_err(); + + assert_eq!( + expected_error, + BlockchainError::QueryFailed( + "Decoder error: Error(\"0x prefix is missing\", line: 0, column: 0)".to_string() + ) + ); + let mut prepare_params = prepare_params_arc.lock().unwrap(); + let (method_name, actual_arguments) = prepare_params.remove(0); + assert!(prepare_params.is_empty()); + assert_eq!(method_name, "eth_blockNumber".to_string()); + let expected_arguments: Vec = vec![]; + assert_eq!(actual_arguments, expected_arguments); + } + + #[test] + fn web3_helper_get_transaction_id_works() { + let prepare_params_arc = Arc::new(Mutex::new(vec![])); + let send_params_arc = Arc::new(Mutex::new(vec![])); + let transport = TestTransport::default() + .prepare_params(&prepare_params_arc) + .send_params(&send_params_arc) + .send_result(json!( + "0x0000000000000000000000000000000000000000000000000000000000000001" + )); + let chain = TEST_DEFAULT_CHAIN; + let subject = make_subject(transport, chain); + + let result = subject.get_transaction_id(&make_paying_wallet(b"gdasgsa")); + + assert_eq!(result, Ok(U256::from(1))); + let mut prepare_params = prepare_params_arc.lock().unwrap(); + let (method_name, actual_arguments) = prepare_params.remove(0); + assert!(prepare_params.is_empty()); + let actual_arguments: Vec = actual_arguments + .into_iter() + .map(|arg| serde_json::to_string(&arg).unwrap()) + .collect(); + assert_eq!(method_name, "eth_getTransactionCount".to_string()); + assert_eq!( + actual_arguments, + vec![ + String::from(r#""0x5c361ba8d82fcf0e5538b2a823e9d457a2296725""#), + String::from(r#""pending""#), + ] + ); + let send_params = send_params_arc.lock().unwrap(); + let rpc_call_params = vec![ + Value::String(String::from("0x5c361ba8d82fcf0e5538b2a823e9d457a2296725")), + Value::String(String::from("pending")), + ]; + let expected_request = + web3::helpers::build_request(1, "eth_getTransactionCount", rpc_call_params); + assert_eq!(*send_params, vec![(1, expected_request)]) + } + + #[test] + fn web3_helper_get_transaction_id_handles_err() { + let act = + |subject: &LowerBCIWeb3, wallet: &Wallet| subject.get_transaction_id(wallet); + + assert_error_from_unintelligible_response(act, "invalid hex character") + } + + fn assert_error_from_unintelligible_response(act: F, expected_err_fragment: &str) + where + F: FnOnce(&LowerBCIWeb3, &Wallet) -> ResultForBalance, + { + let port = find_free_port(); + let _test_server = TestServer::start (port, vec![ + br#"{"jsonrpc":"2.0","id":0,"result":"0x000000000000000000000000000000000000000000000000000000000000FFFQ"}"#.to_vec() + ]); + let (_event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let wallet = Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(); + let subject = make_subject(transport, chain); + + let result = act(&subject, &wallet); + + let err_msg = match result { + Err(BlockchainError::QueryFailed(msg)) => msg, + x => panic!("Expected BlockchainError::QueryFailed, but got {:?}", x), + }; + assert!( + err_msg.contains(expected_err_fragment), + "Expected this fragment \"{}\" in this err msg: {}", + expected_err_fragment, + err_msg + ); + assert!( + err_msg.contains("for wallet 0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc"), + "Expected the wallet to be cited in the err msg like \" for wallet {}\" but wasn't", + wallet + ) + } + + fn make_subject(transport: T, chain: Chain) -> LowerBCIWeb3 + where + T: BatchTransport, + { + let web3 = Web3::new(transport.clone()); + let web3_batch = Web3::new(Batch::new(transport.clone())); + let contract = + Contract::from_json(web3.eth(), chain.rec().contract, CONTRACT_ABI.as_bytes()) + .expect("Unable to initialize contract."); + LowerBCIWeb3::new(Rc::new(web3), Rc::new(web3_batch), contract) + } +} diff --git a/node/src/blockchain/blockchain_interface.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs similarity index 55% rename from node/src/blockchain/blockchain_interface.rs rename to node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs index 0c5f6eeed..d4b0f8ea1 100644 --- a/node/src/blockchain/blockchain_interface.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -1,38 +1,63 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::{PayableAccount, PendingPayable}; -use crate::accountant::{comma_joined_stringifiable, gwei_to_wei}; -use crate::blockchain::batch_payable_tools::{BatchPayableTools, BatchPayableToolsReal}; +mod batch_payable_tools; +pub mod lower_level_interface_web3; +mod test_utils; + +use crate::accountant::db_access_objects::payable_dao::{PayableAccount}; +use crate::accountant::{gwei_to_wei}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_web3::BlockchainAgentWeb3; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; -use crate::blockchain::blockchain_interface::blockchain_interface_common::TRANSACTION_DATA_GAS_MARGIN_MAX; -use crate::blockchain::blockchain_interface::BlockchainError::{ - InvalidAddress, InvalidResponse, InvalidUrl, QueryFailed, +use crate::blockchain::blockchain_interface::blockchain_interface_web3::batch_payable_tools::{ + BatchPayableTools, BatchPayableToolsReal, }; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::lower_level_interface_web3::LowerBCIWeb3; +use crate::blockchain::blockchain_interface::lower_level_interface::LowerBCI; +use crate::blockchain::blockchain_interface::{BlockchainAgentBuildError, BlockchainError, BlockchainInterface, PayableTransactionError, ResultForReceipt, RetrievedBlockchainTransactions}; +use crate::db_config::persistent_configuration::PersistentConfiguration; +use crate::masq_lib::utils::ExpectValue; +use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; -use actix::{Message, Recipient}; -use futures::{future, Future}; -use itertools::Either::{Left, Right}; +use actix::Recipient; +use futures::Future; +use indoc::indoc; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::logger::Logger; -use masq_lib::utils::ExpectValue; use serde_json::Value; -use std::convert::{From, TryFrom, TryInto}; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::Debug; use std::iter::once; +use std::rc::Rc; use thousands::Separable; -use web3::contract::{Contract, Options}; +use web3::contract::Contract; use web3::transports::{Batch, EventLoopHandle}; use web3::types::{ Address, BlockNumber, Bytes, FilterBuilder, Log, SignedTransaction, TransactionParameters, - TransactionReceipt, H160, H256, U256, + H160, H256, U256, }; use web3::{BatchTransport, Error, Web3}; - -pub const REQUESTS_IN_PARALLEL: usize = 1; - -pub const CONTRACT_ABI: &str = r#"[{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]"#; +use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; +use crate::blockchain::blockchain_interface::data_structures::{BlockchainTransaction, ProcessedPayableFallible, RpcPayablesFailure}; + +const CONTRACT_ABI: &str = indoc!( + r#"[{ + "constant":true, + "inputs":[{"name":"owner","type":"address"}], + "name":"balanceOf", + "outputs":[{"name":"","type":"uint256"}], + "payable":false, + "stateMutability":"view", + "type":"function" + },{ + "constant":false, + "inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}], + "name":"transfer", + "outputs":[{"name":"","type":"bool"}], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }]"# +); const TRANSACTION_LITERAL: H256 = H256([ 0xdd, 0xf2, 0x52, 0xad, 0x1b, 0xe2, 0xc8, 0x9b, 0x69, 0xc2, 0xb0, 0x68, 0xfc, 0x37, 0x8d, 0xaa, @@ -41,227 +66,26 @@ const TRANSACTION_LITERAL: H256 = H256([ const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; -#[derive(Clone, Debug, Eq, Message, PartialEq)] -pub struct BlockchainTransaction { - pub block_number: u64, - pub from: Wallet, - pub wei_amount: u128, -} - -impl fmt::Display for BlockchainTransaction { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}gw from {} ({})", - self.wei_amount, self.from, self.block_number - ) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum BlockchainError { - InvalidUrl, - InvalidAddress, - InvalidResponse, - QueryFailed(String), -} - -impl Display for BlockchainError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let err_spec = match self { - InvalidUrl => Left("Invalid url"), - InvalidAddress => Left("Invalid address"), - InvalidResponse => Left("Invalid response"), - QueryFailed(msg) => Right(format!("Query failed: {}", msg)), - }; - write!(f, "Blockchain error: {}", err_spec) - } -} - -pub type BlockchainResult = Result; -pub type ResultForBalance = BlockchainResult; -pub type ResultForBothBalances = BlockchainResult<(web3::types::U256, web3::types::U256)>; -pub type ResultForNonce = BlockchainResult; -pub type ResultForReceipt = BlockchainResult>; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PayableTransactionError { - MissingConsumingWallet, - GasPriceQueryFailed(String), - TransactionCount(BlockchainError), - UnusableWallet(String), - Signing(String), - Sending { msg: String, hashes: Vec }, -} - -impl Display for PayableTransactionError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::MissingConsumingWallet => { - write!(f, "Missing consuming wallet to pay payable from") - } - Self::GasPriceQueryFailed(msg) => { - write!(f, "Unsuccessful gas price query: \"{}\"", msg) - } - Self::TransactionCount(blockchain_err) => write!( - f, - "Transaction count fetching failed for: {}", - blockchain_err - ), - Self::UnusableWallet(msg) => write!( - f, - "Unusable wallet for signing payable transactions: \"{}\"", - msg - ), - Self::Signing(msg) => write!(f, "Signing phase: \"{}\"", msg), - Self::Sending { msg, hashes } => write!( - f, - "Sending phase: \"{}\". Signed and hashed transactions: {}", - msg, - comma_joined_stringifiable(hashes, |hash| format!("{:?}", hash)) - ), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RetrievedBlockchainTransactions { - pub new_start_block: u64, - pub transactions: Vec, -} - -pub trait BlockchainInterface { - fn contract_address(&self) -> Address; - - fn retrieve_transactions( - &self, - start_block: u64, - recipient: &Wallet, - ) -> Result; - - fn estimated_gas_limit_per_payable(&self) -> u64; - - fn send_batch_of_payables( - &self, - consuming_wallet: &Wallet, - gas_price: u64, - pending_nonce: U256, - new_fingerprints_recipient: &Recipient, - accounts: &[PayableAccount], - ) -> Result, PayableTransactionError>; - - fn get_gas_balance(&self, address: &Wallet) -> ResultForBalance; - - fn get_token_balance(&self, address: &Wallet) -> ResultForBalance; - - fn get_transaction_count(&self, address: &Wallet) -> ResultForNonce; - - fn get_transaction_receipt(&self, hash: H256) -> ResultForReceipt; -} - -// TODO: This probably should go away -pub struct BlockchainInterfaceClandestine { - logger: Logger, - chain: Chain, -} - -impl BlockchainInterfaceClandestine { - pub fn new(chain: Chain) -> Self { - BlockchainInterfaceClandestine { - logger: Logger::new("BlockchainInterface"), - chain, - } - } -} - -impl Default for BlockchainInterfaceClandestine { - fn default() -> Self { - Self::new(DEFAULT_CHAIN) - } -} - -impl BlockchainInterface for BlockchainInterfaceClandestine { - fn contract_address(&self) -> Address { - self.chain.rec().contract - } - - fn retrieve_transactions( - &self, - _start_block: u64, - _recipient: &Wallet, - ) -> Result { - let msg = "Can't retrieve transactions clandestinely yet".to_string(); - error!(self.logger, "{}", &msg); - Err(BlockchainError::QueryFailed(msg)) - } - - fn estimated_gas_limit_per_payable(&self) -> u64 { - blockchain_interface_common::base_gas_limit(self.chain) + TRANSACTION_DATA_GAS_MARGIN_MAX - } - - fn send_batch_of_payables( - &self, - _consuming_wallet: &Wallet, - _gas_price: u64, - _last_nonce: U256, - _new_fingerprints_recipient: &Recipient, - _accounts: &[PayableAccount], - ) -> Result, PayableTransactionError> { - error!(self.logger, "Can't send transactions out clandestinely yet",); - Err(PayableTransactionError::Sending { - msg: "invalid attempt to send txs clandestinely".to_string(), - hashes: vec![], - }) - } - - fn get_gas_balance(&self, _address: &Wallet) -> ResultForBalance { - error!(self.logger, "Can't get gas balance clandestinely yet",); - Ok(0.into()) - } - - fn get_token_balance(&self, _address: &Wallet) -> ResultForBalance { - error!( - self.logger, - "Can't get masq token balance clandestinely yet", - ); - Ok(0.into()) - } - - fn get_transaction_count(&self, _address: &Wallet) -> ResultForNonce { - error!(self.logger, "Can't get transaction count clandestinely yet",); - Ok(0.into()) - } - - fn get_transaction_receipt(&self, _hash: H256) -> ResultForReceipt { - error!( - self.logger, - "Can't get transaction receipt clandestinely yet", - ); - Ok(None) - } -} +pub const REQUESTS_IN_PARALLEL: usize = 1; -pub struct BlockchainInterfaceNonClandestine { +pub struct BlockchainInterfaceWeb3 +where + T: 'static + BatchTransport + Debug, +{ logger: Logger, chain: Chain, + gas_limit_const_part: u64, // This must not be dropped for Web3 requests to be completed _event_loop_handle: EventLoopHandle, - web3: Web3, - batch_web3: Web3>, + web3: Rc>, + web3_batch: Rc>>, batch_payable_tools: Box>, - contract: Contract, + lower_interface: Box, } -const GWEI: U256 = U256([1_000_000_000u64, 0, 0, 0]); - -pub fn to_wei(gwub: u64) -> U256 { - let subgwei = U256::from(gwub); - subgwei.full_mul(GWEI).try_into().expect("Internal Error") -} - -impl BlockchainInterface for BlockchainInterfaceNonClandestine +impl BlockchainInterface for BlockchainInterfaceWeb3 where - T: BatchTransport + Debug + 'static, + T: 'static + BatchTransport + Debug, { fn contract_address(&self) -> Address { self.chain.rec().contract @@ -269,21 +93,23 @@ where fn retrieve_transactions( &self, - start_block: u64, + start_block: BlockNumber, + end_block: BlockNumber, recipient: &Wallet, ) -> Result { debug!( self.logger, - "Retrieving transactions from start block: {} for: {} chain_id: {} contract: {:#x}", + "Retrieving transactions from start block: {:?} to end block: {:?} for: {} chain_id: {} contract: {:#x}", start_block, + end_block, recipient, self.chain.rec().num_chain_id, self.contract_address() ); let filter = FilterBuilder::default() .address(vec![self.contract_address()]) - .from_block(BlockNumber::Number(ethereum_types::U64::from(start_block))) - .to_block(BlockNumber::Latest) + .from_block(start_block) + .to_block(end_block) .topics( Some(vec![TRANSACTION_LITERAL]), None, @@ -292,13 +118,39 @@ where ) .build(); - let log_request = self.web3.eth().logs(filter); + let fallback_start_block_number = match end_block { + BlockNumber::Number(eb) => eb.as_u64(), + _ => { + if let BlockNumber::Number(start_block_number) = start_block { + start_block_number.as_u64() + 1u64 + } else { + panic!("start_block of Latest, Earliest, and Pending are not supported"); + } + } + }; + let block_request = self.web3_batch.eth().block_number(); + let log_request = self.web3_batch.eth().logs(filter); + let logger = self.logger.clone(); - log_request - .then(|logs| { - debug!(logger, "Transaction retrieval completed: {:?}", logs); - future::result::(match logs { + match self.web3_batch.transport().submit_batch().wait() { + Ok(_) => { + let response_block_number = match block_request.wait() { + Ok(block_nbr) => { + debug!(logger, "Latest block number: {}", block_nbr.as_u64()); + block_nbr.as_u64() + } + Err(_) => { + debug!( + logger, + "Using fallback block number: {}", fallback_start_block_number + ); + fallback_start_block_number + } + }; + + match log_request.wait() { Ok(logs) => { + let logs_len = logs.len(); if logs .iter() .any(|log| log.topics.len() < 2 || log.data.0.len() > 32) @@ -310,59 +162,119 @@ where ); Err(BlockchainError::InvalidResponse) } else { - let transactions: Vec = logs - .iter() - .filter_map(|log: &Log| match log.block_number { - Some(block_number) => { - let amount: U256 = U256::from(log.data.0.as_slice()); - let wei_amount_result = u128::try_from(amount); - wei_amount_result.ok().map(|wei_amount| { - BlockchainTransaction { - block_number: u64::try_from(block_number) - .expect("Internal Error"), - from: Wallet::from(log.topics[1]), - wei_amount, - } - }) - } - None => None, - }) - .collect(); + let transactions: Vec = + self.extract_transactions_from_logs(logs); debug!(logger, "Retrieved transactions: {:?}", transactions); + if transactions.is_empty() && logs_len != transactions.len() { + warning!( + logger, + "Retrieving transactions: logs: {}, transactions: {}", + logs_len, + transactions.len() + ) + } // Get the largest transaction block number, unless there are no - // transactions, in which case use start_block. - let last_transaction_block = - transactions.iter().fold(start_block, |so_far, elem| { - if elem.block_number > so_far { - elem.block_number - } else { - so_far - } - }); + // transactions, in which case use end_block, unless get_latest_block() + // was not successful. + let transaction_max_block_number = self + .find_largest_transaction_block_number( + response_block_number, + &transactions, + ); + debug!( + logger, + "Discovered transaction max block nbr: {}", + transaction_max_block_number + ); Ok(RetrievedBlockchainTransactions { - new_start_block: last_transaction_block + 1, + new_start_block: 1u64 + transaction_max_block_number, transactions, }) } } - Err(e) => Err(BlockchainError::QueryFailed(e.to_string())), - }) - }) - .wait() + Err(e) => { + error!(self.logger, "Retrieving transactions: {:?}", e); + Err(BlockchainError::QueryFailed(e.to_string())) + } + } + } + Err(e) => Err(BlockchainError::QueryFailed(e.to_string())), + } } - fn estimated_gas_limit_per_payable(&self) -> u64 { - blockchain_interface_common::base_gas_limit(self.chain) + TRANSACTION_DATA_GAS_MARGIN_MAX + fn build_blockchain_agent( + &self, + consuming_wallet: &Wallet, + persistent_config: &dyn PersistentConfiguration, + ) -> Result, BlockchainAgentBuildError> { + let gas_price_gwei = match persistent_config.gas_price() { + Ok(price) => price, + Err(e) => return Err(BlockchainAgentBuildError::GasPrice(e)), + }; + + let transaction_fee_balance = match self + .lower_interface + .get_transaction_fee_balance(consuming_wallet) + { + Ok(balance) => balance, + Err(e) => { + return Err(BlockchainAgentBuildError::TransactionFeeBalance( + consuming_wallet.clone(), + e, + )) + } + }; + + let masq_token_balance = match self + .lower_interface + .get_service_fee_balance(consuming_wallet) + { + Ok(balance) => balance, + Err(e) => { + return Err(BlockchainAgentBuildError::ServiceFeeBalance( + consuming_wallet.clone(), + e, + )) + } + }; + + let pending_transaction_id = match self.lower_interface.get_transaction_id(consuming_wallet) + { + Ok(id) => id, + Err(e) => { + return Err(BlockchainAgentBuildError::TransactionID( + consuming_wallet.clone(), + e, + )) + } + }; + + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: transaction_fee_balance, + service_fee_balance_in_minor_units: u128::try_from(masq_token_balance) + .expect("larger amount than the official supply"), + }; + let consuming_wallet = consuming_wallet.clone(); + + Ok(Box::new(BlockchainAgentWeb3::new( + gas_price_gwei, + self.gas_limit_const_part, + consuming_wallet, + consuming_wallet_balances, + pending_transaction_id, + ))) } fn send_batch_of_payables( &self, - consuming_wallet: &Wallet, - gas_price: u64, - pending_nonce: U256, + agent: Box, new_fingerprints_recipient: &Recipient, accounts: &[PayableAccount], ) -> Result, PayableTransactionError> { + let consuming_wallet = agent.consuming_wallet(); + let gas_price = agent.agreed_fee_per_computation_unit(); + let pending_nonce = agent.pending_transaction_id(); + debug!( self.logger, "Common attributes of payables to be transacted: sender wallet: {}, contract: {:?}, chain_id: {}, gas_price: {}", @@ -392,7 +304,7 @@ where self.transmission_log(accounts, gas_price) ); - match self.batch_payable_tools.submit_batch(&self.batch_web3) { + match self.batch_payable_tools.submit_batch(&self.web3_batch) { Ok(responses) => Ok(Self::merged_output_data( responses, hashes_and_paid_amounts, @@ -402,35 +314,6 @@ where } } - fn get_gas_balance(&self, wallet: &Wallet) -> ResultForBalance { - self.web3 - .eth() - .balance(wallet.address(), None) - .map_err(|e| BlockchainError::QueryFailed(e.to_string())) - .wait() - } - - fn get_token_balance(&self, wallet: &Wallet) -> ResultForBalance { - self.contract - .query( - "balanceOf", - wallet.address(), - None, - Options::default(), - None, - ) - .map_err(|e| BlockchainError::QueryFailed(e.to_string())) - .wait() - } - - fn get_transaction_count(&self, wallet: &Wallet) -> ResultForNonce { - self.web3 - .eth() - .transaction_count(wallet.address(), Some(BlockNumber::Pending)) - .map_err(|e| BlockchainError::QueryFailed(e.to_string())) - .wait() - } - fn get_transaction_receipt(&self, hash: H256) -> ResultForReceipt { self.web3 .eth() @@ -438,43 +321,39 @@ where .map_err(|e| BlockchainError::QueryFailed(e.to_string())) .wait() } -} - -#[derive(Debug, PartialEq, Clone)] -pub enum ProcessedPayableFallible { - Correct(PendingPayable), - Failed(RpcPayableFailure), -} -#[derive(Debug, PartialEq, Clone)] -pub struct RpcPayableFailure { - pub rpc_error: Error, - pub recipient_wallet: Wallet, - pub hash: H256, + fn lower_interface(&self) -> &dyn LowerBCI { + &*self.lower_interface + } } -type HashAndAmountResult = Result, PayableTransactionError>; - -impl BlockchainInterfaceNonClandestine +impl BlockchainInterfaceWeb3 where - T: BatchTransport + Debug + 'static, + T: 'static + BatchTransport + Debug, { pub fn new(transport: T, event_loop_handle: EventLoopHandle, chain: Chain) -> Self { - let web3 = Web3::new(transport.clone()); - let batch_web3 = Web3::new(Batch::new(transport)); + let web3 = Rc::new(Web3::new(transport.clone())); + let web3_batch = Rc::new(Web3::new(Batch::new(transport))); let batch_payable_tools = Box::new(BatchPayableToolsReal::::default()); let contract = Contract::from_json(web3.eth(), chain.rec().contract, CONTRACT_ABI.as_bytes()) .expect("Unable to initialize contract."); + let lower_level_blockchain_interface = Box::new(LowerBCIWeb3::new( + Rc::clone(&web3), + Rc::clone(&web3_batch), + contract, + )); + let gas_limit_const_part = Self::web3_gas_limit_const_part(chain); Self { logger: Logger::new("BlockchainInterface"), chain, + gas_limit_const_part, _event_loop_handle: event_loop_handle, web3, - batch_web3, + web3_batch, + lower_interface: lower_level_blockchain_interface, batch_payable_tools, - contract, } } @@ -578,11 +457,11 @@ where .zip(accounts.iter()); iterator_with_all_data .map(|((rpc_result, (hash, _)), account)| match rpc_result { - Ok(_) => ProcessedPayableFallible::Correct(PendingPayable { + Ok(_) => Ok(PendingPayable { recipient_wallet: account.wallet.clone(), hash, }), - Err(rpc_error) => ProcessedPayableFallible::Failed(RpcPayableFailure { + Err(rpc_error) => Err(RpcPayablesFailure { rpc_error, recipient_wallet: account.wallet.clone(), hash, @@ -616,7 +495,7 @@ where let signed_tx = self.sign_transaction(recipient, consuming_wallet, amount, nonce, gas_price)?; self.batch_payable_tools - .append_transaction_to_batch(signed_tx.raw_transaction, &self.batch_web3); + .append_transaction_to_batch(signed_tx.raw_transaction, &self.web3_batch); Ok(signed_tx.transaction_hash) } @@ -629,7 +508,7 @@ where gas_price: u64, ) -> Result { let data = Self::transaction_data(recipient, amount); - let gas_limit = Self::compute_gas_limit(data.as_slice(), self.chain); + let gas_limit = self.compute_gas_limit(data.as_slice()); let gas_price = gwei_to_wei::(gas_price); let transaction_parameters = TransactionParameters { nonce: Some(nonce), @@ -647,7 +526,7 @@ where }; self.batch_payable_tools - .sign_transaction(transaction_parameters, &self.batch_web3, &key) + .sign_transaction(transaction_parameters, &self.web3_batch, &key) .map_err(|e| PayableTransactionError::Signing(e.to_string())) } @@ -691,87 +570,144 @@ where data } - fn compute_gas_limit(data: &[u8], chain: Chain) -> U256 { - let base_gas_limit = blockchain_interface_common::base_gas_limit(chain); - ethereum_types::U256::try_from(data.iter().fold(base_gas_limit, |acc, v| { + fn compute_gas_limit(&self, data: &[u8]) -> U256 { + ethereum_types::U256::try_from(data.iter().fold(self.gas_limit_const_part, |acc, v| { acc + if v == &0u8 { 4 } else { 68 } })) .expect("Internal error") } - #[cfg(test)] - fn web3(&self) -> &Web3 { - &self.web3 + fn web3_gas_limit_const_part(chain: Chain) -> u64 { + match chain { + Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => 55_000, + Chain::PolyMainnet | Chain::PolyMumbai => 70_000, + } } -} -mod blockchain_interface_common { - use masq_lib::blockchains::chains::{Chain, ChainFamily}; - - pub const TRANSACTION_DATA_GAS_MARGIN_MAX: u64 = transaction_data_margin(); - - pub fn base_gas_limit(chain: Chain) -> u64 { - match chain.rec().chain_family { - ChainFamily::Polygon => 70_000, - ChainFamily::Eth => 55_000, - ChainFamily::Dev => 55_000, - } + fn extract_transactions_from_logs(&self, logs: Vec) -> Vec { + logs.iter() + .filter_map(|log: &Log| match log.block_number { + None => None, + Some(block_number) => { + let wei_amount = U256::from(log.data.0.as_slice()).as_u128(); + Some(BlockchainTransaction { + block_number: block_number.as_u64(), + from: Wallet::from(log.topics[1]), + wei_amount, + }) + } + }) + .collect() } - pub const fn transaction_data_margin() -> u64 { - // 68 bytes * 68 per non zero byte according to the Ethereum docs, - // deliberately maximized - 68 * 68 + fn find_largest_transaction_block_number( + &self, + response_block_number: u64, + transactions: &[BlockchainTransaction], + ) -> u64 { + if transactions.is_empty() { + response_block_number + } else { + transactions + .iter() + .fold(response_block_number, |a, b| a.max(b.block_number)) + } } } +type HashAndAmountResult = Result, PayableTransactionError>; + #[cfg(test)] mod tests { - use super::*; - use crate::accountant::database_access_objects::utils::from_time_t; + use crate::accountant::db_access_objects::utils::from_time_t; use crate::accountant::gwei_to_wei; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_web3::WEB3_MAXIMAL_GAS_LIMIT_MARGIN; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::test_utils::{ make_payable_account, make_payable_account_with_wallet_and_balance_and_timestamp_opt, }; - use crate::blockchain::bip32::Bip32ECKeyProvider; - use crate::blockchain::blockchain_interface::ProcessedPayableFallible::{Correct, Failed}; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; + use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; + + use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ + BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, + TRANSFER_METHOD_ID, + }; + use crate::blockchain::blockchain_interface::test_utils::{ + test_blockchain_interface_is_connected_and_functioning, LowerBCIMock, + }; + use crate::blockchain::blockchain_interface::{ + BlockchainAgentBuildError, BlockchainError, BlockchainInterface, PayableTransactionError, + RetrievedBlockchainTransactions, + }; use crate::blockchain::test_utils::{ - make_default_signed_transaction, make_fake_event_loop_handle, make_tx_hash, - BatchPayableToolsMock, TestTransport, + all_chains, make_fake_event_loop_handle, make_tx_hash, TestTransport, }; + use crate::db_config::persistent_configuration::PersistentConfigError; + use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; - use crate::test_utils::make_paying_wallet; + use crate::test_utils::assert_string_contains; + use crate::test_utils::http_test_server::TestServer; + use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::{make_recorder, Recorder}; use crate::test_utils::unshared_test_utils::decode_hex; - use crate::test_utils::{make_wallet, TestRawTransaction}; + use crate::test_utils::{make_paying_wallet, make_wallet, TestRawTransaction}; use actix::{Actor, System}; - use crossbeam_channel::{unbounded, Receiver}; use ethereum_types::U64; use ethsign_crypto::Keccak256; + use futures::Future; use jsonrpc_core::Version::V2; - use jsonrpc_core::{Call, Error, ErrorCode, Id, MethodCall, Params}; - use masq_lib::blockchains::chains::ChainFamily; + use jsonrpc_core::{Call, Error as RPCError, ErrorCode, Id, MethodCall, Params}; + use masq_lib::blockchains::chains::Chain; + use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; - use masq_lib::utils::{find_free_port, slice_of_strs_to_vec_of_strings}; + use masq_lib::utils::find_free_port; use serde_derive::Deserialize; - use serde_json::json; - use serde_json::Value; - use simple_server::{Request, Server}; - use std::io::Write; - use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}; - use std::ops::Add; + use serde_json::{json, Value}; + use std::net::Ipv4Addr; + + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::test_utils::{ + make_default_signed_transaction, BatchPayableToolsMock, + }; + use crate::blockchain::blockchain_interface::data_structures::{ + BlockchainTransaction, RpcPayablesFailure, + }; + use indoc::indoc; use std::str::FromStr; use std::sync::{Arc, Mutex}; - use std::thread; - use std::time::{Duration, Instant, SystemTime}; - use web3::transports::Http; - use web3::types::H2048; + use std::time::SystemTime; + use web3::transports::{Batch, Http}; + use web3::types::{ + Address, BlockNumber, Bytes, TransactionParameters, TransactionReceipt, H2048, H256, U256, + }; use web3::Error as Web3Error; + use web3::Web3; #[test] - fn constants_have_correct_values() { - let contract_abi_expected: &str = r#"[{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]"#; + fn constants_are_correct() { + let contract_abi_expected: &str = indoc!( + r#"[{ + "constant":true, + "inputs":[{"name":"owner","type":"address"}], + "name":"balanceOf", + "outputs":[{"name":"","type":"uint256"}], + "payable":false, + "stateMutability":"view", + "type":"function" + },{ + "constant":false, + "inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}], + "name":"transfer", + "outputs":[{"name":"","type":"bool"}], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }]"# + ); let transaction_literal_expected: H256 = H256 { 0: [ 0xdd, 0xf2, 0x52, 0xad, 0x1b, 0xe2, 0xc8, 0x9b, 0x69, 0xc2, 0xb0, 0x68, 0xfc, 0x37, @@ -779,128 +715,51 @@ mod tests { 0xf5, 0x23, 0xb3, 0xef, ], }; - assert_eq!(REQUESTS_IN_PARALLEL, 1); assert_eq!(CONTRACT_ABI, contract_abi_expected); assert_eq!(TRANSACTION_LITERAL, transaction_literal_expected); assert_eq!(TRANSFER_METHOD_ID, [0xa9, 0x05, 0x9c, 0xbb]); - assert_eq!(GWEI, U256([1_000_000_000u64, 0, 0, 0])); - } - - struct TestServer { - port: u16, - rx: Receiver>>, - } - - impl Drop for TestServer { - fn drop(&mut self) { - self.stop(); - } + assert_eq!(REQUESTS_IN_PARALLEL, 1); } - impl TestServer { - fn start(port: u16, bodies: Vec>) -> Self { - std::env::set_var("SIMPLESERVER_THREADS", "1"); - let (tx, rx) = unbounded(); - let _ = thread::spawn(move || { - let bodies_arc = Arc::new(Mutex::new(bodies)); - Server::new(move |req, mut rsp| { - if req.headers().get("X-Quit").is_some() { - panic!("Server stop requested"); - } - tx.send(req).unwrap(); - let body = bodies_arc.lock().unwrap().remove(0); - Ok(rsp.body(body)?) - }) - .listen(&Ipv4Addr::LOCALHOST.to_string(), &format!("{}", port)); - }); - let deadline = Instant::now().add(Duration::from_secs(5)); - loop { - thread::sleep(Duration::from_millis(10)); - match TcpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) { - Ok(_) => break, - Err(e) => eprintln!("No: {:?}", e), - } - if Instant::now().gt(&deadline) { - panic!("TestServer still not started after 5sec"); - } - } - TestServer { port, rx } - } - - fn requests_so_far(&self) -> Vec>> { - let mut requests = vec![]; - while let Ok(request) = self.rx.try_recv() { - requests.push(request); - } - return requests; - } + #[test] + fn blockchain_interface_web3_can_return_contract() { + all_chains().iter().for_each(|chain| { + let subject = BlockchainInterfaceWeb3::new( + TestTransport::default(), + make_fake_event_loop_handle(), + *chain, + ); - fn stop(&mut self) { - let mut stream = match TcpStream::connect(SocketAddr::new( - IpAddr::V4(Ipv4Addr::LOCALHOST), - self.port, - )) { - Ok(s) => s, - Err(_) => return, - }; - stream - .write(b"DELETE /irrelevant.htm HTTP/1.1\r\nX-Quit: Yes") - .unwrap(); - } + assert_eq!(subject.contract_address(), chain.rec().contract) + }) } #[test] - fn blockchain_interface_non_clandestine_handles_no_retrieved_transactions() { - let to = "0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc"; - let port = find_free_port(); - let test_server = TestServer::start( - port, - vec![br#"{"jsonrpc":"2.0","id":3,"result":[]}"#.to_vec()], - ); - - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); - - let result = subject - .retrieve_transactions( - 42, - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), + fn blockchain_interface_web3_provides_plain_rp_calls_correctly() { + let subject_factory = |port: u16, _chain: Chain| { + let chain = Chain::PolyMainnet; + let (event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + REQUESTS_IN_PARALLEL, ) .unwrap(); + Box::new(BlockchainInterfaceWeb3::new( + transport, + event_loop_handle, + chain, + )) as Box + }; - let requests = test_server.requests_so_far(); - let bodies: Vec = requests - .into_iter() - .map(|request| serde_json::from_slice(&request.body()).unwrap()) - .collect(); - assert_eq!( - format!("\"0x000000000000000000000000{}\"", &to[2..]), - bodies[0]["params"][0]["topics"][2].to_string(), - ); - assert_eq!( - result, - RetrievedBlockchainTransactions { - new_start_block: 42 + 1, - transactions: vec![] - } - ) + test_blockchain_interface_is_connected_and_functioning(subject_factory) } #[test] - fn blockchain_interface_non_clandestine_retrieves_transactions() { + fn blockchain_interface_web3_retrieves_transactions() { let to = "0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc"; let port = find_free_port(); #[rustfmt::skip] let test_server = TestServer::start (port, vec![ - br#"{ + br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{ "jsonrpc":"2.0", "id":3, "result":[ @@ -935,76 +794,129 @@ mod tests { "transactionIndex":"0x0" } ] - }"#.to_vec(), + }]"#.to_vec(), ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); + let end_block_nbr = 1024u64; let result = subject .retrieve_transactions( - 42, - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), + BlockNumber::Number(42u64.into()), + BlockNumber::Number(end_block_nbr.into()), + &Wallet::from_str(&to).unwrap(), ) .unwrap(); let requests = test_server.requests_so_far(); - let bodies: Vec = requests + let bodies: Vec = requests .into_iter() .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .map(|b: Value| serde_json::to_string(&b).unwrap()) .collect(); - assert_eq!( - format!("\"0x000000000000000000000000{}\"", &to[2..]), - bodies[0]["params"][0]["topics"][2].to_string(), - ); + let expected_body_prefix = r#"[{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber","params":[]},{"id":1,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"address":"0x384dec25e03f94931767ce4c3556168468ba24c3","fromBlock":"0x2a","toBlock":"0x400","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",null,"0x000000000000000000000000"#; + let expected_body_suffix = r#""]}]}]"#; + let expected_body = format!( + "{}{}{}", + expected_body_prefix, + &to[2..], + expected_body_suffix + ); + assert_eq!(bodies, vec!(expected_body)); assert_eq!( result, RetrievedBlockchainTransactions { - new_start_block: 0x4be663 + 1, + new_start_block: 0x4be664, transactions: vec![ BlockchainTransaction { block_number: 0x4be663, from: Wallet::from_str("0x3ab28ecedea6cdb6feed398e93ae8c7b316b1182") .unwrap(), - wei_amount: 4_503_599_627_370_496, + wei_amount: 4_503_599_627_370_496u128, }, BlockchainTransaction { block_number: 0x4be662, from: Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc") .unwrap(), - wei_amount: 4_503_599_627_370_496, + wei_amount: 4_503_599_627_370_496u128, }, ] } ) } + #[test] + fn blockchain_interface_web3_handles_no_retrieved_transactions() { + let to = "0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc"; + let port = find_free_port(); + let test_server = TestServer::start( + port, + vec![br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[]}]"#.to_vec()], + ); + let (event_loop_handle, transport) = Http::with_max_parallel( + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), + REQUESTS_IN_PARALLEL, + ) + .unwrap(); + let subject = + BlockchainInterfaceWeb3::new(transport, event_loop_handle, TEST_DEFAULT_CHAIN); + let end_block_nbr = 1024u64; + + let result = subject + .retrieve_transactions( + BlockNumber::Number(42u64.into()), + BlockNumber::Number(end_block_nbr.into()), + &Wallet::from_str(&to).unwrap(), + ) + .unwrap(); + + let requests = test_server.requests_so_far(); + let bodies: Vec = requests + .into_iter() + .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .map(|b: Value| serde_json::to_string(&b).unwrap()) + .collect(); + let expected_body_prefix = r#"[{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber","params":[]},{"id":1,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"address":"0x384dec25e03f94931767ce4c3556168468ba24c3","fromBlock":"0x2a","toBlock":"0x400","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",null,"0x000000000000000000000000"#; + let expected_body_suffix = r#""]}]}]"#; + let expected_body = format!( + "{}{}{}", + expected_body_prefix, + &to[2..], + expected_body_suffix + ); + assert_eq!(bodies, vec!(expected_body)); + assert_eq!( + result, + RetrievedBlockchainTransactions { + new_start_block: 1 + end_block_nbr, + transactions: vec![] + } + ); + } + #[test] #[should_panic(expected = "No address for an uninitialized wallet!")] - fn blockchain_interface_non_clandestine_retrieve_transactions_returns_an_error_if_the_to_address_is_invalid( + fn blockchain_interface_web3_retrieve_transactions_returns_an_error_if_the_to_address_is_invalid( ) { - let port = 8545; + let port = find_free_port(); let (event_loop_handle, transport) = Http::with_max_parallel( &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); - let result = subject - .retrieve_transactions(42, &Wallet::new("0x3f69f9efd4f2592fd70beecd9dce71c472fc")); + let result = subject.retrieve_transactions( + BlockNumber::Number(42u64.into()), + BlockNumber::Latest, + &Wallet::new("0x3f69f9efd4f2592fd70beecd9dce71c472fc"), + ); assert_eq!( result.expect_err("Expected an Err, got Ok"), @@ -1013,25 +925,23 @@ mod tests { } #[test] - fn blockchain_interface_non_clandestine_retrieve_transactions_returns_an_error_if_a_response_with_too_few_topics_is_returned( + fn blockchain_interface_web3_retrieve_transactions_returns_an_error_if_a_response_with_too_few_topics_is_returned( ) { let port = find_free_port(); let _test_server = TestServer::start (port, vec![ - br#"{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d63100000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}"#.to_vec() + br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d63100000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let result = subject.retrieve_transactions( - 42, + BlockNumber::Number(42u64.into()), + BlockNumber::Latest, &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), ); @@ -1042,27 +952,23 @@ mod tests { } #[test] - fn blockchain_interface_non_clandestine_retrieve_transactions_returns_an_error_if_a_response_with_data_that_is_too_long_is_returned( + fn blockchain_interface_web3_retrieve_transactions_returns_an_error_if_a_response_with_data_that_is_too_long_is_returned( ) { let port = find_free_port(); let _test_server = TestServer::start(port, vec![ - br#"{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d6310000001","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}"#.to_vec() + br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d6310000001","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let result = subject.retrieve_transactions( - 42, + BlockNumber::Number(42u64.into()), + BlockNumber::Latest, &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), ); @@ -1070,257 +976,246 @@ mod tests { } #[test] - fn blockchain_interface_non_clandestine_retrieve_transactions_ignores_transaction_logs_that_have_no_block_number( + fn blockchain_interface_web3_retrieve_transactions_ignores_transaction_logs_that_have_no_block_number( ) { let port = find_free_port(); let _test_server = TestServer::start (port, vec![ - br#"{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","data":"0x0000000000000000000000000000000000000000000000000010000000000000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}"#.to_vec() + br#"[{"jsonrpc":"2.0","id":1,"result":"0x400"},{"jsonrpc":"2.0","id":2,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","data":"0x0000000000000000000000000000000000000000000000000010000000000000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); - + init_test_logging(); let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let end_block_nbr = 1024u64; + let subject = + BlockchainInterfaceWeb3::new(transport, event_loop_handle, TEST_DEFAULT_CHAIN); let result = subject.retrieve_transactions( - 42, + BlockNumber::Number(42u64.into()), + BlockNumber::Number(end_block_nbr.into()), &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), ); assert_eq!( result, Ok(RetrievedBlockchainTransactions { - new_start_block: 43, + new_start_block: 1 + end_block_nbr, transactions: vec![] }) ); + let test_log_handler = TestLogHandler::new(); + test_log_handler.exists_log_containing( + "WARN: BlockchainInterface: Retrieving transactions: logs: 1, transactions: 0", + ); } #[test] - fn blockchain_interface_non_clandestine_can_retrieve_eth_balance_of_a_wallet() { + fn blockchain_interface_non_clandestine_retrieve_transactions_uses_block_number_latest_as_fallback_start_block_plus_one( + ) { let port = find_free_port(); - let _test_server = TestServer::start( - port, - vec![br#"{"jsonrpc":"2.0","id":0,"result":"0xFFFF"}"#.to_vec()], - ); - + let _test_server = TestServer::start (port, vec![ + br#"[{"jsonrpc":"2.0","id":1,"result":"error"},{"jsonrpc":"2.0","id":2,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","data":"0x0000000000000000000000000000000000000000000000000010000000000000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() + ]); let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, + let start_block = BlockNumber::Number(42u64.into()); + let result = subject.retrieve_transactions( + start_block, + BlockNumber::Latest, + &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), ); - let result = subject - .get_gas_balance( - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), - ) - .unwrap(); - - assert_eq!(result, U256::from(65_535)); - } + let expected_fallback_start_block = + if let BlockNumber::Number(start_block_nbr) = start_block { + start_block_nbr.as_u64() + 1u64 + } else { + panic!("start_block of Latest, Earliest, and Pending are not supported!") + }; - #[test] - #[should_panic(expected = "No address for an uninitialized wallet!")] - fn blockchain_interface_non_clandestine_returns_an_error_when_requesting_eth_balance_of_an_invalid_wallet( - ) { - let port = 8545; - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, + assert_eq!( + result, + Ok(RetrievedBlockchainTransactions { + new_start_block: 1 + expected_fallback_start_block, + transactions: vec![] + }) ); - - let result = - subject.get_gas_balance(&Wallet::new("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fQ")); - - assert_eq!(result, Err(BlockchainError::InvalidAddress)); } #[test] - fn blockchain_interface_non_clandestine_returns_an_error_for_unintelligible_response_to_requesting_eth_balance( - ) { - let port = find_free_port(); - let _test_server = TestServer::start( - port, - vec![br#"{"jsonrpc":"2.0","id":0,"result":"0xFFFQ"}"#.to_vec()], - ); - - let (event_loop_handle, transport) = Http::new(&format!( - "http://{}:{}", - &Ipv4Addr::LOCALHOST.to_string(), - port - )) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, + fn blockchain_interface_web3_can_build_blockchain_agent() { + let get_transaction_fee_balance_params_arc = Arc::new(Mutex::new(vec![])); + let get_masq_balance_params_arc = Arc::new(Mutex::new(vec![])); + let get_transactions_id_params_arc = Arc::new(Mutex::new(vec![])); + let chain = Chain::PolyMainnet; + let wallet = make_wallet("abc"); + let persistent_config = PersistentConfigurationMock::new().gas_price_result(Ok(50)); + let mut subject = BlockchainInterfaceWeb3::new( + TestTransport::default(), + make_fake_event_loop_handle(), + chain, ); + let transaction_fee_balance = U256::from(123_456_789); + let masq_balance = U256::from(444_444_444); + let transaction_id = U256::from(23); + let blockchain_interface_helper = LowerBCIMock::default() + .get_transaction_fee_balance_params(&get_transaction_fee_balance_params_arc) + .get_transaction_fee_balance_result(Ok(transaction_fee_balance)) + .get_masq_balance_params(&get_masq_balance_params_arc) + .get_masq_balance_result(Ok(masq_balance)) + .get_transaction_id_params(&get_transactions_id_params_arc) + .get_transaction_id_result(Ok(transaction_id)); + subject.lower_interface = Box::new(blockchain_interface_helper); - let result = subject.get_gas_balance( - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), - ); + let result = subject + .build_blockchain_agent(&wallet, &persistent_config) + .unwrap(); - match result { - Err(BlockchainError::QueryFailed(msg)) if msg.contains("invalid hex character: Q") => { - () + let get_transaction_fee_balance_params = + get_transaction_fee_balance_params_arc.lock().unwrap(); + assert_eq!(*get_transaction_fee_balance_params, vec![wallet.clone()]); + let get_masq_balance_params = get_masq_balance_params_arc.lock().unwrap(); + assert_eq!(*get_masq_balance_params, vec![wallet.clone()]); + let get_transaction_id_params = get_transactions_id_params_arc.lock().unwrap(); + assert_eq!(*get_transaction_id_params, vec![wallet.clone()]); + assert_eq!(result.consuming_wallet(), &wallet); + assert_eq!(result.pending_transaction_id(), transaction_id); + assert_eq!( + result.consuming_wallet_balances(), + ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: transaction_fee_balance, + service_fee_balance_in_minor_units: masq_balance.as_u128() } - x => panic!("Expected complaint about hex character, but got {:?}", x), - }; - } - - #[test] - fn blockchain_interface_non_clandestine_returns_error_for_unintelligible_response_to_gas_balance( - ) { - let act = |subject: &BlockchainInterfaceNonClandestine, wallet: &Wallet| { - subject.get_gas_balance(wallet) - }; - - assert_error_during_requesting_balance(act, "invalid hex character"); + ); + assert_eq!(result.agreed_fee_per_computation_unit(), 50); + let expected_fee_estimation = + ((BlockchainInterfaceWeb3::::web3_gas_limit_const_part(chain) + + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) + * 50) as u128; + assert_eq!( + result.estimated_transaction_fee_per_transaction(), + expected_fee_estimation + ) } #[test] - fn blockchain_interface_non_clandestine_can_retrieve_token_balance_of_a_wallet() { - let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ - br#"{"jsonrpc":"2.0","id":0,"result":"0x000000000000000000000000000000000000000000000000000000000000FFFF"}"#.to_vec() - ]); - - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, + fn build_of_the_blockchain_agent_fails_on_fetching_gas_price() { + let chain = Chain::PolyMumbai; + let wallet = make_wallet("abc"); + let persistent_config = PersistentConfigurationMock::new().gas_price_result(Err( + PersistentConfigError::UninterpretableValue("booga".to_string()), + )); + let subject = BlockchainInterfaceWeb3::new( + TestTransport::default(), + make_fake_event_loop_handle(), + chain, ); - let result = subject - .get_token_balance( - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), - ) - .unwrap(); + let result = subject.build_blockchain_agent(&wallet, &persistent_config); - assert_eq!(result, U256::from(65_535)); + let err = match result { + Err(e) => e, + _ => panic!("we expected Err() but got Ok()"), + }; + let expected_err = BlockchainAgentBuildError::GasPrice( + PersistentConfigError::UninterpretableValue("booga".to_string()), + ); + assert_eq!(err, expected_err) } - #[test] - #[should_panic(expected = "No address for an uninitialized wallet!")] - fn blockchain_interface_non_clandestine_returns_an_error_when_requesting_token_balance_of_an_invalid_wallet( - ) { - let port = 8545; - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, + fn build_of_the_blockchain_agent_fails_on_blockchain_interface_error( + blockchain_interface_helper: LowerBCIMock, + expected_err_factory: F, + ) where + F: FnOnce(&Wallet) -> BlockchainAgentBuildError, + { + let chain = Chain::EthMainnet; + let wallet = make_wallet("bcd"); + let persistent_config = PersistentConfigurationMock::new().gas_price_result(Ok(30)); + let mut subject = BlockchainInterfaceWeb3::new( + TestTransport::default(), + make_fake_event_loop_handle(), + chain, ); + subject.lower_interface = Box::new(blockchain_interface_helper); - let result = - subject.get_token_balance(&Wallet::new("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fQ")); + let result = subject.build_blockchain_agent(&wallet, &persistent_config); - assert_eq!(result, Err(BlockchainError::InvalidAddress)); + let err = match result { + Err(e) => e, + _ => panic!("we expected Err() but got Ok()"), + }; + let expected_err = expected_err_factory(&wallet); + assert_eq!(err, expected_err) } #[test] - fn blockchain_interface_non_clandestine_returns_error_for_unintelligible_response_to_token_balance( - ) { - let act = |subject: &BlockchainInterfaceNonClandestine, wallet: &Wallet| { - subject.get_token_balance(wallet) + fn build_of_the_blockchain_agent_fails_on_transaction_fee_balance() { + let lower_interface = LowerBCIMock::default() + .get_transaction_fee_balance_result(Err(BlockchainError::InvalidAddress)); + let expected_err_factory = |wallet: &Wallet| { + BlockchainAgentBuildError::TransactionFeeBalance( + wallet.clone(), + BlockchainError::InvalidAddress, + ) }; - assert_error_during_requesting_balance(act, "Invalid hex"); - } - - fn assert_error_during_requesting_balance(act: F, expected_err_msg_fragment: &str) - where - F: FnOnce(&BlockchainInterfaceNonClandestine, &Wallet) -> ResultForBalance, - { - let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ - br#"{"jsonrpc":"2.0","id":0,"result":"0x000000000000000000000000000000000000000000000000000000000000FFFQ"}"#.to_vec() - ]); - - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, + build_of_the_blockchain_agent_fails_on_blockchain_interface_error( + lower_interface, + expected_err_factory, ) - .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); - - let result = act( - &subject, - &Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(), - ); + } - let err_msg = match result { - Err(BlockchainError::QueryFailed(msg)) => msg, - x => panic!("Expected BlockchainError::QueryFailed, but got {:?}", x), + #[test] + fn build_of_the_blockchain_agent_fails_on_masq_balance() { + let transaction_fee_balance = U256::from(123_456_789); + let lower_interface = LowerBCIMock::default() + .get_transaction_fee_balance_result(Ok(transaction_fee_balance)) + .get_masq_balance_result(Err(BlockchainError::InvalidResponse)); + let expected_err_factory = |wallet: &Wallet| { + BlockchainAgentBuildError::ServiceFeeBalance( + wallet.clone(), + BlockchainError::InvalidResponse, + ) }; - assert!( - err_msg.contains(expected_err_msg_fragment), - "Expected this fragment {} in this err msg: {}", - expected_err_msg_fragment, - err_msg + + build_of_the_blockchain_agent_fails_on_blockchain_interface_error( + lower_interface, + expected_err_factory, ) } #[test] - fn blockchain_interface_non_clandestine_gives_estimates_for_gas_limits() { - let subject = |chain: Chain| { - BlockchainInterfaceNonClandestine::new( - TestTransport::default(), - make_fake_event_loop_handle(), - chain, + fn build_of_the_blockchain_agent_fails_on_transaction_id() { + let transaction_fee_balance = U256::from(123_456_789); + let masq_balance = U256::from(500_000_000); + let lower_interface = LowerBCIMock::default() + .get_transaction_fee_balance_result(Ok(transaction_fee_balance)) + .get_masq_balance_result(Ok(masq_balance)) + .get_transaction_id_result(Err(BlockchainError::InvalidResponse)); + let expected_err_factory = |wallet: &Wallet| { + BlockchainAgentBuildError::TransactionID( + wallet.clone(), + BlockchainError::InvalidResponse, ) }; - [ - Chain::EthMainnet, - Chain::PolyMainnet, - Chain::PolyMumbai, - Chain::Dev, - ] - .into_iter() - .for_each(|chain| { - let subject = subject.clone(); - assert_eq!( - subject(chain).estimated_gas_limit_per_payable(), - blockchain_interface_common::base_gas_limit(chain) + (68 * 68) //number of bytes and gas required per non-zero bytes = maximal margin - ) - }); + build_of_the_blockchain_agent_fails_on_blockchain_interface_error( + lower_interface, + expected_err_factory, + ); } #[test] - fn blockchain_interface_non_clandestine_can_transfer_tokens_in_batch() { + fn blockchain_interface_web3_can_transfer_tokens_in_batch() { //exercising also the layer of web3 functions, but the transport layer is mocked init_test_logging(); let send_batch_params_arc = Arc::new(Mutex::new(vec![])); @@ -1329,7 +1224,7 @@ mod tests { //Any eventual rpc errors brought back are processed as well... let expected_batch_responses = vec![ Ok(json!("...unnecessarily important hash...")), - Err(web3::Error::Rpc(Error { + Err(web3::Error::Rpc(RPCError { code: ErrorCode::ServerError(114), message: "server being busy".to_string(), data: None, @@ -1343,13 +1238,10 @@ mod tests { let actor_addr = accountant.start(); let fingerprint_recipient = recipient!(actor_addr, PendingPayableFingerprintSeeds); let logger = Logger::new("sending_batch_payments"); - let mut subject = BlockchainInterfaceNonClandestine::new( - transport.clone(), - make_fake_event_loop_handle(), - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let mut subject = + BlockchainInterfaceWeb3::new(transport.clone(), make_fake_event_loop_handle(), chain); subject.logger = logger; - let gas_price = 120; let amount_1 = gwei_to_wei(900_000_000_u64); let account_1 = make_payable_account_with_wallet_and_balance_and_timestamp_opt( make_wallet("w123"), @@ -1368,19 +1260,13 @@ mod tests { amount_3, None, ); - let pending_nonce = U256::from(6); let accounts_to_process = vec![account_1, account_2, account_3]; let consuming_wallet = make_paying_wallet(b"gdasgsa"); + let agent = make_initialized_agent(120, consuming_wallet, U256::from(6)); let test_timestamp_before = SystemTime::now(); let result = subject - .send_batch_of_payables( - &consuming_wallet, - gas_price, - pending_nonce, - &fingerprint_recipient, - &accounts_to_process, - ) + .send_batch_of_payables(agent, &fingerprint_recipient, &accounts_to_process) .unwrap(); let test_timestamp_after = SystemTime::now(); @@ -1428,10 +1314,10 @@ mod tests { ); let check_expected_successful_request = |expected_hash: H256, idx: usize| { let pending_payable = match &result[idx]{ - Correct(pp) => pp, - Failed(RpcPayableFailure{ rpc_error, recipient_wallet: recipient, hash }) => panic!( - "we expected correct pending payable but got one with rpc_error: {:?} and hash: {} for recipient: {}", - rpc_error, hash, recipient + Ok(pp) => pp, + Err(RpcPayablesFailure { rpc_error, recipient_wallet: recipient, hash }) => panic!( + "we expected correct pending payable but got one with rpc_error: {:?} and hash: {} for recipient: {}", + rpc_error, hash, recipient ), }; let hash = pending_payable.hash; @@ -1445,11 +1331,11 @@ mod tests { //failing request let pending_payable_fallible_2 = &result[1]; let (rpc_error, recipient_2, hash_2) = match pending_payable_fallible_2 { - Correct(pp) => panic!( + Ok(pp) => panic!( "we expected failing pending payable but got a good one: {:?}", pp ), - Failed(RpcPayableFailure { + Err(RpcPayablesFailure { rpc_error, recipient_wallet: recipient, hash, @@ -1457,7 +1343,7 @@ mod tests { }; assert_eq!( rpc_error, - &web3::Error::Rpc(Error { + &web3::Error::Rpc(RPCError { code: ErrorCode::ServerError(114), message: "server being busy".to_string(), data: None @@ -1524,7 +1410,7 @@ mod tests { } #[test] - fn non_clandestine_interface_send_batch_of_payables_components_are_used_together_properly() { + fn send_payables_within_batch_components_are_used_together_properly() { let sign_transaction_params_arc = Arc::new(Mutex::new(vec![])); let append_transaction_to_batch_params_arc = Arc::new(Mutex::new(vec![])); let new_payable_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); @@ -1535,13 +1421,13 @@ mod tests { let initiate_fingerprints_recipient = accountant.start().recipient(); let consuming_wallet_secret = b"consuming_wallet_0123456789abcde"; let secret_key = - (&Bip32ECKeyProvider::from_raw_secret(consuming_wallet_secret).unwrap()).into(); + (&Bip32EncryptionKeyProvider::from_raw_secret(consuming_wallet_secret).unwrap()).into(); let batch_wide_timestamp_expected = SystemTime::now(); let transport = TestTransport::default().initiate_reference_counter(&reference_counter_arc); let chain = Chain::EthMainnet; let mut subject = - BlockchainInterfaceNonClandestine::new(transport, make_fake_event_loop_handle(), chain); - let first_tx_parameters = TransactionParameters { + BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); + let first_transaction_params_expected = TransactionParameters { nonce: Some(U256::from(4)), to: Some(subject.contract_address()), gas: U256::from(56_552), @@ -1557,10 +1443,10 @@ mod tests { let first_signed_transaction = subject .web3 .accounts() - .sign_transaction(first_tx_parameters.clone(), &secret_key) + .sign_transaction(first_transaction_params_expected.clone(), &secret_key) .wait() .unwrap(); - let second_tx_parameters = TransactionParameters { + let second_transaction_params_expected = TransactionParameters { nonce: Some(U256::from(5)), to: Some(subject.contract_address()), gas: U256::from(56_552), @@ -1576,12 +1462,11 @@ mod tests { let second_signed_transaction = subject .web3 .accounts() - .sign_transaction(second_tx_parameters.clone(), &secret_key) + .sign_transaction(second_transaction_params_expected.clone(), &secret_key) .wait() .unwrap(); let first_hash = first_signed_transaction.transaction_hash; let second_hash = second_signed_transaction.transaction_hash; - let pending_nonce = U256::from(4); //technically, the JSON values in the correct responses don't matter, we only check for errors if any came back let rpc_responses = vec![ Ok(Value::String((&first_hash.to_string()[2..]).to_string())), @@ -1598,7 +1483,6 @@ mod tests { .submit_batch_result(Ok(rpc_responses)); subject.batch_payable_tools = Box::new(batch_payables_tools); let consuming_wallet = make_paying_wallet(consuming_wallet_secret); - let gas_price = 123; let first_payment_amount = 333_222_111_000; let first_creditor_wallet = make_wallet("creditor321"); let first_account = make_payable_account_with_wallet_and_balance_and_timestamp_opt( @@ -1613,11 +1497,10 @@ mod tests { second_payment_amount, None, ); + let agent = make_initialized_agent(123, consuming_wallet, U256::from(4)); let result = subject.send_batch_of_payables( - &consuming_wallet, - gas_price, - pending_nonce, + agent, &initiate_fingerprints_recipient, &vec![first_account, second_account], ); @@ -1633,13 +1516,16 @@ mod tests { assert_eq!( result, Ok(vec![ - Correct(first_resulting_pending_payable), - Correct(second_resulting_pending_payable) + Ok(first_resulting_pending_payable), + Ok(second_resulting_pending_payable) ]) ); let mut sign_transaction_params = sign_transaction_params_arc.lock().unwrap(); - let (first_transaction_params, web3, secret) = sign_transaction_params.remove(0); - assert_eq!(first_transaction_params, first_tx_parameters); + let (first_transaction_params_actual, web3, secret) = sign_transaction_params.remove(0); + assert_eq!( + first_transaction_params_actual, + first_transaction_params_expected + ); let check_web3_origin = |web3: &Web3>| { let ref_count_before_clone = Arc::strong_count(&reference_counter_arc); let _new_ref = web3.clone(); @@ -1649,16 +1535,21 @@ mod tests { check_web3_origin(&web3); assert_eq!( secret, - (&Bip32ECKeyProvider::from_raw_secret(&consuming_wallet_secret.keccak256()).unwrap()) + (&Bip32EncryptionKeyProvider::from_raw_secret(&consuming_wallet_secret.keccak256()) + .unwrap()) .into() ); - let (second_transaction_params, web3_from_st_call, secret) = + let (second_transaction_params_actual, web3_from_st_call, secret) = sign_transaction_params.remove(0); - assert_eq!(second_transaction_params, second_tx_parameters); + assert_eq!( + second_transaction_params_actual, + second_transaction_params_expected + ); check_web3_origin(&web3_from_st_call); assert_eq!( secret, - (&Bip32ECKeyProvider::from_raw_secret(&consuming_wallet_secret.keccak256()).unwrap()) + (&Bip32EncryptionKeyProvider::from_raw_secret(&consuming_wallet_secret.keccak256()) + .unwrap()) .into() ); assert!(sign_transaction_params.is_empty()); @@ -1695,7 +1586,8 @@ mod tests { assert_eq!(submit_batch_params.len(), 1); check_web3_origin(&web3_from_sb_call); assert!(accountant_recording_arc.lock().unwrap().is_empty()); - let system = System::new("non_clandestine_interface_send_payables_in_batch_components_are_used_together_properly"); + let system = + System::new("send_payables_within_batch_components_are_used_together_properly"); let probe_message = PendingPayableFingerprintSeeds { batch_wide_timestamp: SystemTime::now(), hashes_and_balances: vec![], @@ -1708,73 +1600,47 @@ mod tests { } #[test] - fn non_clandestine_base_gas_limit_is_properly_set() { - assert_eq!( - blockchain_interface_common::base_gas_limit(Chain::PolyMainnet), - 70_000 - ); - assert_eq!( - blockchain_interface_common::base_gas_limit(Chain::PolyMumbai), - 70_000 - ); + fn web3_gas_limit_const_part_returns_reasonable_values() { + type Subject = BlockchainInterfaceWeb3; assert_eq!( - blockchain_interface_common::base_gas_limit(Chain::EthMainnet), + Subject::web3_gas_limit_const_part(Chain::EthMainnet), 55_000 ); assert_eq!( - blockchain_interface_common::base_gas_limit(Chain::EthRopsten), + Subject::web3_gas_limit_const_part(Chain::EthRopsten), 55_000 ); assert_eq!( - blockchain_interface_common::base_gas_limit(Chain::Dev), - 55_000 + Subject::web3_gas_limit_const_part(Chain::PolyMainnet), + 70_000 ); - } - - #[test] - fn non_clandestine_gas_limit_for_polygon_mainnet_starts_on_70000_as_the_base() { - let transport = TestTransport::default(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::PolyMainnet, + assert_eq!( + Subject::web3_gas_limit_const_part(Chain::PolyMumbai), + 70_000 ); - - assert_gas_limit_is_between(subject, 70000, u64::MAX) + assert_eq!(Subject::web3_gas_limit_const_part(Chain::Dev), 55_000); } #[test] - fn non_clandestine_gas_limit_for_dev_lies_within_limits() { - let transport = TestTransport::default(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::Dev, - ); - - assert_gas_limit_is_between(subject, 55000, 65000) + fn gas_limit_for_polygon_mainnet_lies_within_limits_for_raw_transaction() { + test_gas_limit_is_between_limits(Chain::PolyMainnet); } #[test] - fn non_clandestine_gas_limit_for_eth_mainnet_lies_within_limits() { - let transport = TestTransport::default(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::EthMainnet, - ); - - assert_gas_limit_is_between(subject, 55000, 65000) + fn gas_limit_for_eth_mainnet_lies_within_limits_for_raw_transaction() { + test_gas_limit_is_between_limits(Chain::EthMainnet) } - fn assert_gas_limit_is_between( - mut subject: BlockchainInterfaceNonClandestine, - not_under_this_value: u64, - not_above_this_value: u64, - ) { + fn test_gas_limit_is_between_limits(chain: Chain) { let sign_transaction_params_arc = Arc::new(Mutex::new(vec![])); + let transport = TestTransport::default(); + let mut subject = + BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); + let not_under_this_value = + BlockchainInterfaceWeb3::::web3_gas_limit_const_part(chain); + let not_above_this_value = not_under_this_value + WEB3_MAXIMAL_GAS_LIMIT_MARGIN; let consuming_wallet_secret_raw_bytes = b"my-wallet"; - let batch_payable_tools = BatchPayableToolsMock::::default() + let batch_payable_tools = BatchPayableToolsMock::::default() .sign_transaction_params(&sign_transaction_params_arc) .sign_transaction_result(Ok(make_default_signed_transaction())); subject.batch_payable_tools = Box::new(batch_payable_tools); @@ -1793,39 +1659,47 @@ mod tests { let mut sign_transaction_params = sign_transaction_params_arc.lock().unwrap(); let (transaction_params, _, secret) = sign_transaction_params.remove(0); assert!(sign_transaction_params.is_empty()); - assert!(transaction_params.gas >= U256::from(not_under_this_value)); - assert!(transaction_params.gas <= U256::from(not_above_this_value)); + assert!( + transaction_params.gas >= U256::from(not_under_this_value), + "actual gas limit {} isn't above or equal {}", + transaction_params.gas, + not_under_this_value + ); + assert!( + transaction_params.gas <= U256::from(not_above_this_value), + "actual gas limit {} isn't below or equal {}", + transaction_params.gas, + not_above_this_value + ); assert_eq!( secret, - (&Bip32ECKeyProvider::from_raw_secret(&consuming_wallet_secret_raw_bytes.keccak256()) - .unwrap()) + (&Bip32EncryptionKeyProvider::from_raw_secret( + &consuming_wallet_secret_raw_bytes.keccak256() + ) + .unwrap()) .into() ); } #[test] - fn signing_error_ends_iteration_over_accounts_after_detecting_first_error_which_is_then_propagated_all_way_up_and_out( - ) { + fn signing_error_terminates_iteration_over_accounts_and_propagates_it_all_way_up_and_out() { let transport = TestTransport::default(); + let chain = Chain::PolyMumbai; let batch_payable_tools = BatchPayableToolsMock::::default() .sign_transaction_result(Err(Web3Error::Signing( secp256k1secrets::Error::InvalidSecretKey, ))) //we return after meeting the first result .sign_transaction_result(Err(Web3Error::Internal)); - let mut subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::PolyMumbai, - ); + let mut subject = + BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); subject.batch_payable_tools = Box::new(batch_payable_tools); let recipient = Recorder::new().start().recipient(); let consuming_wallet = make_paying_wallet(&b"consume, you greedy fool!"[..]); - let nonce = U256::from(123); let accounts = vec![make_payable_account(5555), make_payable_account(6666)]; + let agent = make_initialized_agent(123, consuming_wallet, U256::from(4)); - let result = - subject.send_batch_of_payables(&consuming_wallet, 111, nonce, &recipient, &accounts); + let result = subject.send_batch_of_payables(agent, &recipient, &accounts); assert_eq!( result, @@ -1842,12 +1716,8 @@ mod tests { let transport = TestTransport::default(); let incomplete_consuming_wallet = Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - TEST_DEFAULT_CHAIN, - ); - + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); let system = System::new("test"); let (accountant, _, accountant_recording_arc) = make_recorder(); let recipient = accountant.start().recipient(); @@ -1856,16 +1726,9 @@ mod tests { 9000, None, ); - let gas_price = 123; - let nonce = U256::from(1); + let agent = make_initialized_agent(123, incomplete_consuming_wallet, U256::from(1)); - let result = subject.send_batch_of_payables( - &incomplete_consuming_wallet, - gas_price, - nonce, - &recipient, - &vec![account], - ); + let result = subject.send_batch_of_payables(agent, &recipient, &vec![account]); System::current().stop(); system.run(); @@ -1887,11 +1750,9 @@ mod tests { .batch_wide_timestamp_result(SystemTime::now()) .submit_batch_result(Err(Web3Error::Transport("Transaction crashed".to_string()))); let consuming_wallet_secret_raw_bytes = b"okay-wallet"; - let mut subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::PolyMumbai, - ); + let chain = Chain::PolyMumbai; + let mut subject = + BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); subject.batch_payable_tools = Box::new(batch_payable_tools); let unimportant_recipient = Recorder::new().start().recipient(); let account = make_payable_account_with_wallet_and_balance_and_timestamp_opt( @@ -1900,16 +1761,9 @@ mod tests { None, ); let consuming_wallet = make_paying_wallet(consuming_wallet_secret_raw_bytes); - let gas_price = 123; - let nonce = U256::from(1); + let agent = make_initialized_agent(120, consuming_wallet, U256::from(6)); - let result = subject.send_batch_of_payables( - &consuming_wallet, - gas_price, - nonce, - &unimportant_recipient, - &vec![account], - ); + let result = subject.send_batch_of_payables(agent, &unimportant_recipient, &vec![account]); assert_eq!( result, @@ -1928,11 +1782,9 @@ mod tests { secp256k1secrets::Error::InvalidSecretKey, ))); let consuming_wallet_secret_raw_bytes = b"okay-wallet"; - let mut subject = BlockchainInterfaceNonClandestine::new( - transport, - make_fake_event_loop_handle(), - Chain::PolyMumbai, - ); + let chain = Chain::PolyMumbai; + let mut subject = + BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); subject.batch_payable_tools = Box::new(batch_payable_tools); let recipient = make_wallet("unlucky man"); let consuming_wallet = make_paying_wallet(consuming_wallet_secret_raw_bytes); @@ -1950,8 +1802,12 @@ mod tests { ); } + const TEST_PAYMENT_AMOUNT: u128 = 1_000_000_000_000; + const TEST_GAS_PRICE_ETH: u64 = 110; + const TEST_GAS_PRICE_POLYGON: u64 = 50; + fn test_consuming_wallet_with_secret() -> Wallet { - let key_pair = Bip32ECKeyProvider::from_raw_secret( + let key_pair = Bip32EncryptionKeyProvider::from_raw_secret( &decode_hex("97923d8fd8de4a00f912bfb77ef483141dec551bd73ea59343ef5c4aac965d04") .unwrap(), ) @@ -1966,25 +1822,19 @@ mod tests { Wallet::from(address) } - const TEST_PAYMENT_AMOUNT: u128 = 1_000_000_000_000; - const TEST_GAS_PRICE_ETH: u64 = 110; - const TEST_GAS_PRICE_POLYGON: u64 = 50; - fn assert_that_signed_transactions_agrees_with_template( chain: Chain, nonce: u64, template: &[u8], ) { let transport = TestTransport::default(); - let subject = - BlockchainInterfaceNonClandestine::new(transport, make_fake_event_loop_handle(), chain); + let subject = BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); let consuming_wallet = test_consuming_wallet_with_secret(); let recipient_wallet = test_recipient_wallet(); let nonce_correct_type = U256::from(nonce); - let gas_price = match chain.rec().chain_family { - ChainFamily::Eth => TEST_GAS_PRICE_ETH, - ChainFamily::Polygon => TEST_GAS_PRICE_POLYGON, - _ => panic!("isn't our interest in this test"), + let gas_price = match chain { + Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => TEST_GAS_PRICE_ETH, + Chain::PolyMainnet | Chain::PolyMumbai => TEST_GAS_PRICE_POLYGON, }; let payable_account = make_payable_account_with_wallet_and_balance_and_timestamp_opt( recipient_wallet, @@ -2008,7 +1858,7 @@ mod tests { //with a real confirmation through a transaction sent with this data to the network #[test] - fn non_clandestine_signing_a_transaction_works_for_polygon_mumbai() { + fn web3_interface_signing_a_transaction_works_for_polygon_mumbai() { let chain = Chain::PolyMumbai; let nonce = 5; // signed_transaction_data changed after we changed the contract address of polygon matic @@ -2021,7 +1871,7 @@ mod tests { //with a real confirmation through a transaction sent with this data to the network #[test] - fn non_clandestine_signing_a_transaction_works_for_eth_ropsten() { + fn web3_interface_signing_a_transaction_works_for_eth_ropsten() { let chain = Chain::EthRopsten; let nonce = 1; //must stay like this! let signed_transaction_data = "f8a90185199c82cc0082dee894384dec25e03f94931767ce4c3556168468ba24c380b844a9059cbb0000000000000000000000007788df76bbd9a0c7c3e5bf0f77bb28c60a167a7b000000000000000000000000000000000000000000000000000000e8d4a510002aa0635fbb3652e1c3063afac6ffdf47220e0431825015aef7daff9251694e449bfca00b2ed6d556bd030ac75291bf58817da15a891cd027a4c261bb80b51f33b78adf"; @@ -2032,7 +1882,7 @@ mod tests { //not confirmed on the real network #[test] - fn non_clandestine_signing_a_transaction_for_polygon_mainnet() { + fn web3_interface_signing_a_transaction_for_polygon_mainnet() { let chain = Chain::PolyMainnet; let nonce = 10; //generated locally @@ -2053,7 +1903,7 @@ mod tests { //not confirmed on the real network #[test] - fn non_clandestine_signing_a_transaction_for_eth_mainnet() { + fn web3_interface_signing_a_transaction_for_eth_mainnet() { let chain = Chain::EthMainnet; let nonce = 10; //generated locally @@ -2075,7 +1925,7 @@ mod tests { //an adapted test from old times when we had our own signing method //I don't have data for the new chains so I omit them in this kind of tests #[test] - fn signs_various_transaction_for_eth_mainnet() { + fn signs_various_transactions_for_eth_mainnet() { let signatures = &[ &[ 248, 108, 9, 133, 4, 168, 23, 200, 0, 130, 82, 8, 148, 53, 53, 53, 53, 53, 53, 53, @@ -2147,7 +1997,7 @@ mod tests { private_key: H256, } - fn assert_signature(chain: Chain, slice_of_sclices: &[&[u8]]) { + fn assert_signature(chain: Chain, slice_of_slices: &[&[u8]]) { let first_part_tx_1 = r#"[{"nonce": "0x9", "gasPrice": "0x4a817c800", "gasLimit": "0x5208", "to": "0x3535353535353535353535353535353535353535", "value": "0xde0b6b3a7640000", "data": []}, {"private_key": "0x4646464646464646464646464646464646464646464646464646464646464646", "signed": "#; let first_part_tx_2 = r#"[{"nonce": "0x0", "gasPrice": "0xd55698372431", "gasLimit": "0x1e8480", "to": "0xF0109fC8DF283027b6285cc889F5aA624EaC1F55", "value": "0x3b9aca00", "data": []}, {"private_key": "0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318", "signed": "#; let first_part_tx_3 = r#"[{"nonce": "0x00", "gasPrice": "0x09184e72a000", "gasLimit": "0x2710", "to": null, "value": "0x00", "data": [127,116,101,115,116,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,87]}, {"private_key": "0xe331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109", "signed": "#; @@ -2159,7 +2009,7 @@ mod tests { "[{}]", vec![first_part_tx_1, first_part_tx_2, first_part_tx_3] .iter() - .zip(slice_of_sclices.iter()) + .zip(slice_of_slices.iter()) .zip(0usize..2) .fold(String::new(), |so_far, actual| [ so_far, @@ -2187,8 +2037,7 @@ mod tests { ][..], ]; let transport = TestTransport::default(); - let subject = - BlockchainInterfaceNonClandestine::new(transport, make_fake_event_loop_handle(), chain); + let subject = BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); let lengths_of_constant_parts: Vec = constant_parts.iter().map(|part| part.len()).collect(); for (((tx, signed), length), constant_part) in txs @@ -2197,13 +2046,14 @@ mod tests { .zip(constant_parts) { let secret = Wallet::from( - Bip32ECKeyProvider::from_raw_secret(&signed.private_key.0.as_ref()).unwrap(), + Bip32EncryptionKeyProvider::from_raw_secret(&signed.private_key.0.as_ref()) + .unwrap(), ) .prepare_secp256k1_secret() .unwrap(); let tx_params = from_raw_transaction_to_transaction_parameters(tx, chain); let sign = subject - .web3() + .web3 .accounts() .sign_transaction(tx_params, &secret) .wait() @@ -2230,66 +2080,19 @@ mod tests { } #[test] - fn blockchain_interface_non_clandestine_can_fetch_nonce() { - let prepare_params_arc = Arc::new(Mutex::new(vec![])); - let send_params_arc = Arc::new(Mutex::new(vec![])); - let transport = TestTransport::default() - .prepare_params(&prepare_params_arc) - .send_params(&send_params_arc) - .send_result(json!( - "0x0000000000000000000000000000000000000000000000000000000000000001" - )); - let subject = BlockchainInterfaceNonClandestine::new( - transport.clone(), - make_fake_event_loop_handle(), - TEST_DEFAULT_CHAIN, - ); - - let result = subject.get_transaction_count(&make_paying_wallet(b"gdasgsa")); - - assert_eq!(result, Ok(U256::from(1))); - let mut prepare_params = prepare_params_arc.lock().unwrap(); - let (method_name, actual_arguments) = prepare_params.remove(0); - assert!(prepare_params.is_empty()); - let actual_arguments: Vec = actual_arguments - .into_iter() - .map(|arg| serde_json::to_string(&arg).unwrap()) - .collect(); - assert_eq!(method_name, "eth_getTransactionCount".to_string()); - assert_eq!( - actual_arguments, - vec![ - String::from(r#""0x5c361ba8d82fcf0e5538b2a823e9d457a2296725""#), - String::from(r#""pending""#), - ] - ); - let send_params = send_params_arc.lock().unwrap(); - let rpc_call_params = vec![ - Value::String(String::from("0x5c361ba8d82fcf0e5538b2a823e9d457a2296725")), - Value::String(String::from("pending")), - ]; - let expected_request = - web3::helpers::build_request(1, "eth_getTransactionCount", rpc_call_params); - assert_eq!(*send_params, vec![(1, expected_request)]) - } - - #[test] - fn blockchain_interface_non_clandestine_can_fetch_transaction_receipt() { + fn blockchain_interface_web3_can_fetch_transaction_receipt() { let port = find_free_port(); let _test_server = TestServer::start (port, vec![ br#"{"jsonrpc":"2.0","id":2,"result":{"transactionHash":"0xa128f9ca1e705cc20a936a24a7fa1df73bad6e0aaf58e8e6ffcc154a7cff6e0e","blockHash":"0x6d0abccae617442c26104c2bc63d1bc05e1e002e555aec4ab62a46e826b18f18","blockNumber":"0xb0328d","contractAddress":null,"cumulativeGasUsed":"0x60ef","effectiveGasPrice":"0x22ecb25c00","from":"0x7424d05b59647119b01ff81e2d3987b6c358bf9c","gasUsed":"0x60ef","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000","status":"0x0","to":"0x384dec25e03f94931767ce4c3556168468ba24c3","transactionIndex":"0x0","type":"0x0"}}"# .to_vec() ]); let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let tx_hash = H256::from_str("a128f9ca1e705cc20a936a24a7fa1df73bad6e0aaf58e8e6ffcc154a7cff6e0e") .unwrap(); @@ -2316,185 +2119,35 @@ mod tests { fn get_transaction_receipt_handles_errors() { let port = find_free_port(); let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), + &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), REQUESTS_IN_PARALLEL, ) .unwrap(); - let subject = BlockchainInterfaceNonClandestine::new( - transport, - event_loop_handle, - TEST_DEFAULT_CHAIN, - ); + let chain = TEST_DEFAULT_CHAIN; + let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let tx_hash = make_tx_hash(4564546); - let result = subject.get_transaction_receipt(tx_hash); - - match result { - Err(BlockchainError::QueryFailed(err_message)) => assert!( - err_message.contains("Transport error: Error(Connect, Os"), - "we got this error msg: {}", - err_message - ), - Err(e) => panic!("we expected a different error than: {}", e), - Ok(x) => panic!("we expected an error, but got: {:?}", x), - }; - } - - #[test] - fn to_wei_converts_units_properly_for_max_value() { - let converted_wei = to_wei(u64::MAX); - - assert_eq!( - converted_wei, - U256::from_dec_str(format!("{}000000000", u64::MAX).as_str()).unwrap() - ); - } - - #[test] - fn to_wei_converts_units_properly_for_one() { - let converted_wei = to_wei(1); - - assert_eq!(converted_wei, U256::from_dec_str("1000000000").unwrap()); - } - - #[test] - fn constant_gwei_matches_calculated_value() { - let value = U256::from(1_000_000_000); - assert_eq!(value.0[0], 1_000_000_000); - assert_eq!(value.0[1], 0); - assert_eq!(value.0[2], 0); - assert_eq!(value.0[3], 0); - - let gwei = U256([1_000_000_000u64, 0, 0, 0]); - assert_eq!(value, gwei); - assert_eq!(gwei, GWEI); - assert_eq!(value, GWEI); - } - - #[test] - fn hash_the_smartcontract_transfer_function_signature() { - assert_eq!( - TRANSFER_METHOD_ID, - "transfer(address,uint256)".keccak256()[0..4] - ); - } - - fn collect_match_displayable_error_variants_in_exhaustive_mode( - errors_to_assert: &[E], - exhaustive_error_matching: C, - expected_check_nums: Vec, - ) -> Vec - where - E: Display, - C: Fn(&E) -> (String, u8), - { - let displayed_errors_with_check_nums = errors_to_assert - .iter() - .map(exhaustive_error_matching) - .collect::>(); - let check_nums_alone = displayed_errors_with_check_nums - .iter() - .map(|(_, num_check)| *num_check) - .collect::>(); - assert_eq!(check_nums_alone, expected_check_nums); - displayed_errors_with_check_nums - .into_iter() - .map(|(msg, _)| msg) - .collect() - } - - #[test] - fn blockchain_error_implements_display() { - //TODO at the time of writing this test 'core::mem::variant_count' was only in nightly, - // consider its implementation instead of these match statements here and in the test below - let original_errors = [ - BlockchainError::InvalidUrl, - BlockchainError::InvalidAddress, - BlockchainError::InvalidResponse, - BlockchainError::QueryFailed( - "Don't query so often, it gives me a headache".to_string(), - ), - ]; - let pretty_print_closure = |err_to_resolve: &BlockchainError| match err_to_resolve { - BlockchainError::InvalidUrl => (err_to_resolve.to_string(), 11), - BlockchainError::InvalidAddress => (err_to_resolve.to_string(), 22), - BlockchainError::InvalidResponse => (err_to_resolve.to_string(), 33), - BlockchainError::QueryFailed(..) => (err_to_resolve.to_string(), 44), + let actual_error = subject.get_transaction_receipt(tx_hash).unwrap_err(); + let error_message = if let BlockchainError::QueryFailed(em) = actual_error { + em + } else { + panic!("Expected BlockchainError::QueryFailed(msg)"); }; - - let actual_error_msgs = collect_match_displayable_error_variants_in_exhaustive_mode( - original_errors.as_slice(), - pretty_print_closure, - vec![11, 22, 33, 44], + assert_string_contains( + error_message.as_str(), + "Transport error: Error(Connect, Os { code: ", ); - - assert_eq!( - actual_error_msgs, - slice_of_strs_to_vec_of_strings(&[ - "Blockchain error: Invalid url", - "Blockchain error: Invalid address", - "Blockchain error: Invalid response", - "Blockchain error: Query failed: Don't query so often, it gives me a headache", - ]) - ) - } - - #[test] - fn payable_payment_error_implements_display() { - let original_errors = [ - PayableTransactionError::MissingConsumingWallet, - PayableTransactionError::GasPriceQueryFailed( - "Gas halves shut, no drop left".to_string(), - ), - PayableTransactionError::TransactionCount(BlockchainError::InvalidResponse), - PayableTransactionError::UnusableWallet( - "This is a LEATHER wallet, not LEDGER wallet, stupid.".to_string(), - ), - PayableTransactionError::Signing( - "You cannot sign with just three crosses here, clever boy".to_string(), - ), - PayableTransactionError::Sending { - msg: "Sending to cosmos belongs elsewhere".to_string(), - hashes: vec![make_tx_hash(0x6f), make_tx_hash(0xde)], - }, - ]; - let pretty_print_closure = |err_to_resolve: &PayableTransactionError| match err_to_resolve { - PayableTransactionError::MissingConsumingWallet => (err_to_resolve.to_string(), 11), - PayableTransactionError::GasPriceQueryFailed(_) => (err_to_resolve.to_string(), 22), - PayableTransactionError::TransactionCount(_) => (err_to_resolve.to_string(), 33), - PayableTransactionError::UnusableWallet(_) => (err_to_resolve.to_string(), 44), - PayableTransactionError::Signing(_) => (err_to_resolve.to_string(), 55), - PayableTransactionError::Sending { .. } => (err_to_resolve.to_string(), 66), - }; - - let actual_error_msgs = collect_match_displayable_error_variants_in_exhaustive_mode( - original_errors.as_slice(), - pretty_print_closure, - vec![11, 22, 33, 44, 55, 66], + assert_string_contains( + error_message.as_str(), + ", kind: ConnectionRefused, message: ", ); - - assert_eq!( - actual_error_msgs, - slice_of_strs_to_vec_of_strings(&[ - "Missing consuming wallet to pay payable from", - "Unsuccessful gas price query: \"Gas halves shut, no drop left\"", - "Transaction count fetching failed for: Blockchain error: Invalid response", - "Unusable wallet for signing payable transactions: \"This is a LEATHER wallet, not \ - LEDGER wallet, stupid.\"", - "Signing phase: \"You cannot sign with just three crosses here, clever boy\"", - "Sending phase: \"Sending to cosmos belongs elsewhere\". Signed and hashed \ - transactions: 0x000000000000000000000000000000000000000000000000000000000000006f, \ - 0x00000000000000000000000000000000000000000000000000000000000000de" - ]) - ) } #[test] fn advance_used_nonce() { let initial_nonce = U256::from(55); - let result = - BlockchainInterfaceNonClandestine::::advance_used_nonce(initial_nonce); + let result = BlockchainInterfaceWeb3::::advance_used_nonce(initial_nonce); assert_eq!(result, U256::from(56)) } @@ -2521,14 +2174,14 @@ mod tests { ]; let responses = vec![ Ok(Value::String(String::from("blah"))), - Err(web3::Error::Rpc(Error { + Err(web3::Error::Rpc(RPCError { code: ErrorCode::ParseError, message: "I guess we've got a problem".to_string(), data: None, })), ]; - let result = BlockchainInterfaceNonClandestine::::merged_output_data( + let result = BlockchainInterfaceWeb3::::merged_output_data( responses, fingerprint_inputs, &accounts, @@ -2537,12 +2190,12 @@ mod tests { assert_eq!( result, vec![ - Correct(PendingPayable { + Ok(PendingPayable { recipient_wallet: make_wallet("4567"), hash: make_tx_hash(444) }), - Failed(RpcPayableFailure { - rpc_error: web3::Error::Rpc(Error { + Err(RpcPayablesFailure { + rpc_error: web3::Error::Rpc(RPCError { code: ErrorCode::ParseError, message: "I guess we've got a problem".to_string(), data: None, @@ -2554,174 +2207,24 @@ mod tests { ) } - fn make_clandestine_subject(test_name: &str, chain: Chain) -> BlockchainInterfaceClandestine { - BlockchainInterfaceClandestine { - logger: Logger::new(test_name), - chain, - } - } - - fn all_chains() -> [Chain; 4] { - [ - Chain::EthMainnet, - Chain::PolyMainnet, - Chain::PolyMumbai, - Chain::Dev, - ] - } - - #[test] - fn blockchain_interface_clandestine_returns_contract_address() { - all_chains().into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject("irrelevant", chain).contract_address(), - chain.rec().contract - ) - }) - } - - #[test] - fn blockchain_interface_clandestine_retrieves_no_transactions() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_retrieves_no_transactions"; - let expected_msg = "Can't retrieve transactions clandestinely yet"; - let wallet = make_wallet("blah"); - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).retrieve_transactions(0, &wallet), - Err(BlockchainError::QueryFailed(expected_msg.to_string())) - ) - }); - - let expected_log_msg = format!("ERROR: {test_name}: {}", expected_msg); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); - } - - #[test] - fn blockchain_interface_clandestine_estimated_gas_limit_per_payable() { - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject("irrelevant", chain).estimated_gas_limit_per_payable(), - blockchain_interface_common::base_gas_limit(chain) - + TRANSACTION_DATA_GAS_MARGIN_MAX - ) - }); - } - - #[test] - fn blockchain_interface_clandestine_send_batch_of_payables() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_send_batch_of_payables"; - let wallet = make_wallet("blah"); - let chains = all_chains(); - let (recorder, _, _) = make_recorder(); - let recipient = recorder.start().recipient(); - let accounts = vec![make_payable_account(111)]; - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).send_batch_of_payables( - &wallet, - 123, - U256::one(), - &recipient, - &accounts - ), - Err(PayableTransactionError::Sending { - msg: "invalid attempt to send txs clandestinely".to_string(), - hashes: vec![], - }) - ) - }); - - let expected_log_msg = - format!("ERROR: {test_name}: Can't send transactions out clandestinely yet"); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); - } - - #[test] - fn blockchain_interface_clandestine_gets_no_gas_balance() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_gets_no_gas_balance"; - let wallet = make_wallet("blah"); - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).get_gas_balance(&wallet), - Ok(U256::zero()) - ) - }); - - let expected_log_msg = - format!("ERROR: {test_name}: Can't get gas balance clandestinely yet"); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); - } - - #[test] - fn blockchain_interface_clandestine_gets_no_token_balance() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_gets_no_token_balance"; - let wallet = make_wallet("blah"); - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).get_token_balance(&wallet), - Ok(U256::zero()) - ) - }); - - let expected_log_msg = - format!("ERROR: {test_name}: Can't get masq token balance clandestinely yet"); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); - } - - #[test] - fn blockchain_interface_clandestine_gets_no_transaction_count() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_gets_no_transaction_count"; - let wallet = make_wallet("blah"); - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).get_transaction_count(&wallet), - Ok(U256::zero()) - ) - }); - - let expected_log_msg = - format!("ERROR: {test_name}: Can't get transaction count clandestinely yet"); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + fn make_initialized_agent( + gas_price_gwei: u64, + consuming_wallet: Wallet, + nonce: U256, + ) -> Box { + Box::new( + BlockchainAgentMock::default() + .consuming_wallet_result(consuming_wallet) + .agreed_fee_per_computation_unit_result(gas_price_gwei) + .pending_transaction_id_result(nonce), + ) } #[test] - fn blockchain_interface_clandestine_gets_no_transaction_receipt() { - init_test_logging(); - let test_name = "blockchain_interface_clandestine_gets_no_transaction_receipt"; - let tx_hash = make_tx_hash(123); - let chains = all_chains(); - - chains.into_iter().for_each(|chain| { - assert_eq!( - make_clandestine_subject(test_name, chain).get_transaction_receipt(tx_hash), - Ok(None) - ) - }); - - let expected_log_msg = - format!("ERROR: {test_name}: Can't get transaction receipt clandestinely yet"); - TestLogHandler::new() - .assert_logs_contain_in_order(vec![expected_log_msg.as_str()].repeat(chains.len())); + fn hash_the_smart_contract_transfer_function_signature() { + assert_eq!( + "transfer(address,uint256)".keccak256()[0..4], + TRANSFER_METHOD_ID, + ); } } diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/test_utils.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/test_utils.rs new file mode 100644 index 000000000..55f9fbce7 --- /dev/null +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/test_utils.rs @@ -0,0 +1,172 @@ +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::batch_payable_tools::BatchPayableTools; +use actix::Recipient; +use jsonrpc_core as rpc; +use std::cell::RefCell; +use std::sync::{Arc, Mutex}; +use std::time::SystemTime; +use web3::transports::Batch; +use web3::types::{Bytes, SignedTransaction, TransactionParameters, H256}; +use web3::{BatchTransport, Error as Web3Error, Web3}; + +#[derive(Default)] +pub struct BatchPayableToolsMock { + sign_transaction_params: Arc< + Mutex< + Vec<( + TransactionParameters, + Web3>, + secp256k1secrets::key::SecretKey, + )>, + >, + >, + sign_transaction_results: RefCell>>, + append_transaction_to_batch_params: Arc>)>>>, + //append_transaction_to_batch returns just the unit type + //batch_wide_timestamp doesn't have params + batch_wide_timestamp_results: RefCell>, + send_new_payable_fingerprints_seeds_params: Arc< + Mutex< + Vec<( + SystemTime, + Recipient, + Vec<(H256, u128)>, + )>, + >, + >, + //new_payable_fingerprints returns just the unit type + submit_batch_params: Arc>>>>, + submit_batch_results: + RefCell>, Web3Error>>>, +} + +impl BatchPayableTools for BatchPayableToolsMock { + fn sign_transaction( + &self, + transaction_params: TransactionParameters, + web3: &Web3>, + key: &secp256k1secrets::key::SecretKey, + ) -> Result { + self.sign_transaction_params.lock().unwrap().push(( + transaction_params.clone(), + web3.clone(), + key.clone(), + )); + self.sign_transaction_results.borrow_mut().remove(0) + } + + fn append_transaction_to_batch(&self, signed_transaction: Bytes, web3: &Web3>) { + self.append_transaction_to_batch_params + .lock() + .unwrap() + .push((signed_transaction, web3.clone())); + } + + fn batch_wide_timestamp(&self) -> SystemTime { + self.batch_wide_timestamp_results.borrow_mut().remove(0) + } + + fn send_new_payable_fingerprints_seeds( + &self, + batch_wide_timestamp: SystemTime, + pp_fingerprint_sub: &Recipient, + hashes_and_balances: &[(H256, u128)], + ) { + self.send_new_payable_fingerprints_seeds_params + .lock() + .unwrap() + .push(( + batch_wide_timestamp, + (*pp_fingerprint_sub).clone(), + hashes_and_balances.to_vec(), + )); + } + + fn submit_batch( + &self, + web3: &Web3>, + ) -> Result>, Web3Error> { + self.submit_batch_params.lock().unwrap().push(web3.clone()); + self.submit_batch_results.borrow_mut().remove(0) + } +} + +impl BatchPayableToolsMock { + pub fn sign_transaction_params( + mut self, + params: &Arc< + Mutex< + Vec<( + TransactionParameters, + Web3>, + secp256k1secrets::key::SecretKey, + )>, + >, + >, + ) -> Self { + self.sign_transaction_params = params.clone(); + self + } + + pub fn sign_transaction_result(self, result: Result) -> Self { + self.sign_transaction_results.borrow_mut().push(result); + self + } + + pub fn batch_wide_timestamp_result(self, result: SystemTime) -> Self { + self.batch_wide_timestamp_results.borrow_mut().push(result); + self + } + + pub fn send_new_payable_fingerprint_credentials_params( + mut self, + params: &Arc< + Mutex< + Vec<( + SystemTime, + Recipient, + Vec<(H256, u128)>, + )>, + >, + >, + ) -> Self { + self.send_new_payable_fingerprints_seeds_params = params.clone(); + self + } + + pub fn append_transaction_to_batch_params( + mut self, + params: &Arc>)>>>, + ) -> Self { + self.append_transaction_to_batch_params = params.clone(); + self + } + + pub fn submit_batch_params(mut self, params: &Arc>>>>) -> Self { + self.submit_batch_params = params.clone(); + self + } + + pub fn submit_batch_result( + self, + result: Result>, Web3Error>, + ) -> Self { + self.submit_batch_results.borrow_mut().push(result); + self + } +} + +pub fn make_default_signed_transaction() -> SignedTransaction { + SignedTransaction { + message_hash: Default::default(), + v: 0, + r: Default::default(), + s: Default::default(), + raw_transaction: Default::default(), + transaction_hash: Default::default(), + } +} diff --git a/node/src/blockchain/blockchain_interface/data_structures/errors.rs b/node/src/blockchain/blockchain_interface/data_structures/errors.rs new file mode 100644 index 000000000..e4649c744 --- /dev/null +++ b/node/src/blockchain/blockchain_interface/data_structures/errors.rs @@ -0,0 +1,262 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::comma_joined_stringifiable; +use crate::db_config::persistent_configuration::PersistentConfigError; +use crate::sub_lib::wallet::Wallet; +use itertools::Either; +use std::fmt; +use std::fmt::{Display, Formatter}; +use variant_count::VariantCount; +use web3::types::{TransactionReceipt, H256}; + +const BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED: &str = "Uninitialized blockchain interface. To avoid \ +being delinquency-banned, you should restart the Node with a value for blockchain-service-url"; + +#[derive(Clone, Debug, PartialEq, Eq, VariantCount)] +pub enum BlockchainError { + InvalidUrl, + InvalidAddress, + InvalidResponse, + QueryFailed(String), + UninitializedBlockchainInterface, +} + +impl Display for BlockchainError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let err_spec = match self { + Self::InvalidUrl => Either::Left("Invalid url"), + Self::InvalidAddress => Either::Left("Invalid address"), + Self::InvalidResponse => Either::Left("Invalid response"), + Self::QueryFailed(msg) => Either::Right(format!("Query failed: {}", msg)), + Self::UninitializedBlockchainInterface => { + Either::Left(BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED) + } + }; + write!(f, "Blockchain error: {}", err_spec) + } +} + +pub type BlockchainResult = Result; +pub type ResultForReceipt = BlockchainResult>; + +#[derive(Clone, Debug, PartialEq, Eq, VariantCount)] +pub enum PayableTransactionError { + MissingConsumingWallet, + GasPriceQueryFailed(String), + TransactionID(BlockchainError), + UnusableWallet(String), + Signing(String), + Sending { msg: String, hashes: Vec }, + UninitializedBlockchainInterface, +} + +impl Display for PayableTransactionError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::MissingConsumingWallet => { + write!(f, "Missing consuming wallet to pay payable from") + } + Self::GasPriceQueryFailed(msg) => { + write!(f, "Unsuccessful gas price query: \"{}\"", msg) + } + Self::TransactionID(blockchain_err) => { + write!(f, "Transaction id fetching failed: {}", blockchain_err) + } + Self::UnusableWallet(msg) => write!( + f, + "Unusable wallet for signing payable transactions: \"{}\"", + msg + ), + Self::Signing(msg) => write!(f, "Signing phase: \"{}\"", msg), + Self::Sending { msg, hashes } => write!( + f, + "Sending phase: \"{}\". Signed and hashed transactions: {}", + msg, + comma_joined_stringifiable(hashes, |hash| format!("{:?}", hash)) + ), + Self::UninitializedBlockchainInterface => { + write!(f, "{}", BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED) + } + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, VariantCount)] +pub enum BlockchainAgentBuildError { + GasPrice(PersistentConfigError), + TransactionFeeBalance(Wallet, BlockchainError), + ServiceFeeBalance(Wallet, BlockchainError), + TransactionID(Wallet, BlockchainError), + UninitializedBlockchainInterface, +} + +impl Display for BlockchainAgentBuildError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let preformatted_or_complete = match self { + Self::GasPrice(persistent_config_e) => Either::Left(format!( + "gas price from the database: {:?}", + persistent_config_e + )), + Self::TransactionFeeBalance(wallet, blockchain_e) => Either::Left(format!( + "transaction fee balance for our earning wallet {} due to: {}", + wallet, blockchain_e + )), + Self::ServiceFeeBalance(wallet, blockchain_e) => Either::Left(format!( + "masq balance for our earning wallet {} due to {}", + wallet, blockchain_e + )), + Self::TransactionID(wallet, blockchain_e) => Either::Left(format!( + "transaction id for our earning wallet {} due to {}", + wallet, blockchain_e + )), + Self::UninitializedBlockchainInterface => { + Either::Right(BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED.to_string()) + } + }; + + match preformatted_or_complete { + Either::Left(ending) => write!( + f, + "Blockchain agent construction failed at fetching {}", + ending + ), + Either::Right(msg) => write!(f, "{}", msg), + } + } +} + +#[cfg(test)] +mod tests { + use crate::blockchain::blockchain_interface::data_structures::errors::BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED; + use crate::blockchain::blockchain_interface::{ + BlockchainAgentBuildError, BlockchainError, PayableTransactionError, + }; + use crate::blockchain::test_utils::make_tx_hash; + use crate::db_config::persistent_configuration::PersistentConfigError; + use crate::test_utils::make_wallet; + use masq_lib::utils::{slice_of_strs_to_vec_of_strings, to_string}; + + #[test] + fn constants_have_correct_values() { + assert_eq!( + BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED, + "Uninitialized blockchain interface. To avoid being delinquency-banned, you should \ + restart the Node with a value for blockchain-service-url" + ) + } + + #[test] + fn blockchain_error_implements_display() { + let original_errors = [ + BlockchainError::InvalidUrl, + BlockchainError::InvalidAddress, + BlockchainError::InvalidResponse, + BlockchainError::QueryFailed( + "Don't query so often, it gives me a headache".to_string(), + ), + BlockchainError::UninitializedBlockchainInterface, + ]; + + let actual_error_msgs = original_errors.iter().map(to_string).collect::>(); + + assert_eq!( + original_errors.len(), + BlockchainError::VARIANT_COUNT, + "you forgot to add all variants in this test" + ); + assert_eq!( + actual_error_msgs, + slice_of_strs_to_vec_of_strings(&[ + "Blockchain error: Invalid url", + "Blockchain error: Invalid address", + "Blockchain error: Invalid response", + "Blockchain error: Query failed: Don't query so often, it gives me a headache", + &format!("Blockchain error: {}", BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED) + ]) + ); + } + + #[test] + fn payable_payment_error_implements_display() { + let original_errors = [ + PayableTransactionError::MissingConsumingWallet, + PayableTransactionError::GasPriceQueryFailed( + "Gas halves shut, no drop left".to_string(), + ), + PayableTransactionError::TransactionID(BlockchainError::InvalidResponse), + PayableTransactionError::UnusableWallet( + "This is a LEATHER wallet, not LEDGER wallet, stupid.".to_string(), + ), + PayableTransactionError::Signing( + "You cannot sign with just three crosses here, clever boy".to_string(), + ), + PayableTransactionError::Sending { + msg: "Sending to cosmos belongs elsewhere".to_string(), + hashes: vec![make_tx_hash(0x6f), make_tx_hash(0xde)], + }, + PayableTransactionError::UninitializedBlockchainInterface, + ]; + + let actual_error_msgs = original_errors.iter().map(to_string).collect::>(); + + assert_eq!( + original_errors.len(), + PayableTransactionError::VARIANT_COUNT, + "you forgot to add all variants in this test" + ); + assert_eq!( + actual_error_msgs, + slice_of_strs_to_vec_of_strings(&[ + "Missing consuming wallet to pay payable from", + "Unsuccessful gas price query: \"Gas halves shut, no drop left\"", + "Transaction id fetching failed: Blockchain error: Invalid response", + "Unusable wallet for signing payable transactions: \"This is a LEATHER wallet, not \ + LEDGER wallet, stupid.\"", + "Signing phase: \"You cannot sign with just three crosses here, clever boy\"", + "Sending phase: \"Sending to cosmos belongs elsewhere\". Signed and hashed \ + transactions: 0x000000000000000000000000000000000000000000000000000000000000006f, \ + 0x00000000000000000000000000000000000000000000000000000000000000de", + BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED + ]) + ) + } + + #[test] + fn blockchain_agent_build_error_implements_display() { + let wallet = make_wallet("abc"); + let original_errors = [ + BlockchainAgentBuildError::GasPrice(PersistentConfigError::NotPresent), + BlockchainAgentBuildError::TransactionFeeBalance( + wallet.clone(), + BlockchainError::InvalidResponse, + ), + BlockchainAgentBuildError::ServiceFeeBalance( + wallet.clone(), + BlockchainError::InvalidAddress, + ), + BlockchainAgentBuildError::TransactionID(wallet.clone(), BlockchainError::InvalidUrl), + BlockchainAgentBuildError::UninitializedBlockchainInterface, + ]; + + let actual_error_msgs = original_errors.iter().map(to_string).collect::>(); + + assert_eq!( + original_errors.len(), + BlockchainAgentBuildError::VARIANT_COUNT, + "you forgot to add all variants in this test" + ); + assert_eq!( + actual_error_msgs, + slice_of_strs_to_vec_of_strings(&[ + "Blockchain agent construction failed at fetching gas price from the database: NotPresent", + "Blockchain agent construction failed at fetching transaction fee balance for our earning \ + wallet 0x0000000000000000000000000000000000616263 due to: Blockchain error: Invalid response", + "Blockchain agent construction failed at fetching masq balance for our earning wallet \ + 0x0000000000000000000000000000000000616263 due to Blockchain error: Invalid address", + "Blockchain agent construction failed at fetching transaction id for our earning wallet \ + 0x0000000000000000000000000000000000616263 due to Blockchain error: Invalid url", + BLOCKCHAIN_SERVICE_URL_NOT_SPECIFIED + ]) + ) + } +} diff --git a/node/src/blockchain/blockchain_interface/data_structures/mod.rs b/node/src/blockchain/blockchain_interface/data_structures/mod.rs new file mode 100644 index 000000000..d1d785aae --- /dev/null +++ b/node/src/blockchain/blockchain_interface/data_structures/mod.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod errors; +use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; +use crate::sub_lib::wallet::Wallet; +use web3::types::H256; +use web3::Error; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BlockchainTransaction { + pub block_number: u64, + pub from: Wallet, + pub wei_amount: u128, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RetrievedBlockchainTransactions { + pub new_start_block: u64, + pub transactions: Vec, +} + +pub type ProcessedPayableFallible = Result; + +#[derive(Debug, PartialEq, Clone)] +pub struct RpcPayablesFailure { + pub rpc_error: Error, + pub recipient_wallet: Wallet, + pub hash: H256, +} diff --git a/node/src/blockchain/blockchain_interface/lower_level_interface.rs b/node/src/blockchain/blockchain_interface/lower_level_interface.rs new file mode 100644 index 000000000..60b20bd3b --- /dev/null +++ b/node/src/blockchain/blockchain_interface/lower_level_interface.rs @@ -0,0 +1,24 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::blockchain::blockchain_interface::data_structures::errors::BlockchainResult; +use crate::sub_lib::wallet::Wallet; +use ethereum_types::U64; +use web3::types::U256; + +pub trait LowerBCI { + fn get_transaction_fee_balance(&self, wallet: &Wallet) -> ResultForBalance; + + // This is currently exclusive to the MASQ token but a more general naming might + // be needed for an architecture including also widely established public chains + // without a project-specific application layer on top of it + fn get_service_fee_balance(&self, wallet: &Wallet) -> ResultForBalance; + + fn get_block_number(&self) -> LatestBlockNumber; + + fn get_transaction_id(&self, wallet: &Wallet) -> ResultForNonce; +} + +pub type ResultForBalance = BlockchainResult; +pub type ResultForBothBalances = BlockchainResult<(web3::types::U256, web3::types::U256)>; +pub type ResultForNonce = BlockchainResult; +pub type LatestBlockNumber = BlockchainResult; diff --git a/node/src/blockchain/blockchain_interface/mod.rs b/node/src/blockchain/blockchain_interface/mod.rs new file mode 100644 index 000000000..5bb3a2789 --- /dev/null +++ b/node/src/blockchain/blockchain_interface/mod.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod blockchain_interface_null; +pub mod blockchain_interface_web3; +pub mod data_structures; +pub mod lower_level_interface; +pub mod test_utils; + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; +use crate::blockchain::blockchain_interface::data_structures::errors::{ + BlockchainAgentBuildError, BlockchainError, PayableTransactionError, ResultForReceipt, +}; +use crate::blockchain::blockchain_interface::data_structures::{ + ProcessedPayableFallible, RetrievedBlockchainTransactions, +}; +use crate::blockchain::blockchain_interface::lower_level_interface::LowerBCI; +use crate::db_config::persistent_configuration::PersistentConfiguration; +use crate::sub_lib::wallet::Wallet; +use actix::Recipient; +use web3::types::{Address, BlockNumber, H256}; + +pub trait BlockchainInterface { + fn contract_address(&self) -> Address; + + fn retrieve_transactions( + &self, + start_block: BlockNumber, + end_block: BlockNumber, + recipient: &Wallet, + ) -> Result; + + fn build_blockchain_agent( + &self, + consuming_wallet: &Wallet, + persistent_config: &dyn PersistentConfiguration, + ) -> Result, BlockchainAgentBuildError>; + + fn send_batch_of_payables( + &self, + agent: Box, + new_fingerprints_recipient: &Recipient, + accounts: &[PayableAccount], + ) -> Result, PayableTransactionError>; + + fn get_transaction_receipt(&self, hash: H256) -> ResultForReceipt; + + fn lower_interface(&self) -> &dyn LowerBCI; + + as_any_in_trait!(); +} diff --git a/node/src/blockchain/blockchain_interface/test_utils.rs b/node/src/blockchain/blockchain_interface/test_utils.rs new file mode 100644 index 000000000..87ff85eeb --- /dev/null +++ b/node/src/blockchain/blockchain_interface/test_utils.rs @@ -0,0 +1,131 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::blockchain::blockchain_interface::lower_level_interface::{ + LatestBlockNumber, LowerBCI, ResultForBalance, ResultForNonce, +}; + +use crate::blockchain::blockchain_interface::BlockchainInterface; +use crate::sub_lib::wallet::Wallet; +use crate::test_utils::http_test_server::TestServer; +use crate::test_utils::make_wallet; +use masq_lib::blockchains::chains::Chain; +use masq_lib::utils::find_free_port; +use serde_json::Value; +use std::cell::RefCell; +use std::sync::{Arc, Mutex}; + +#[derive(Default)] +pub struct LowerBCIMock { + get_transaction_fee_balance_params: Arc>>, + get_transaction_fee_balance_results: RefCell>, + get_masq_balance_params: Arc>>, + get_masq_balance_results: RefCell>, + get_block_number_results: RefCell>, + get_transaction_id_params: Arc>>, + get_transaction_id_results: RefCell>, +} + +impl LowerBCI for LowerBCIMock { + fn get_transaction_fee_balance(&self, address: &Wallet) -> ResultForBalance { + self.get_transaction_fee_balance_params + .lock() + .unwrap() + .push(address.clone()); + self.get_transaction_fee_balance_results + .borrow_mut() + .remove(0) + } + + fn get_service_fee_balance(&self, address: &Wallet) -> ResultForBalance { + self.get_masq_balance_params + .lock() + .unwrap() + .push(address.clone()); + self.get_masq_balance_results.borrow_mut().remove(0) + } + + fn get_block_number(&self) -> LatestBlockNumber { + self.get_block_number_results.borrow_mut().remove(0) + } + + fn get_transaction_id(&self, address: &Wallet) -> ResultForNonce { + self.get_transaction_id_params + .lock() + .unwrap() + .push(address.clone()); + self.get_transaction_id_results.borrow_mut().remove(0) + } +} + +impl LowerBCIMock { + pub fn get_transaction_fee_balance_params(mut self, params: &Arc>>) -> Self { + self.get_transaction_fee_balance_params = params.clone(); + self + } + + pub fn get_transaction_fee_balance_result(self, result: ResultForBalance) -> Self { + self.get_transaction_fee_balance_results + .borrow_mut() + .push(result); + self + } + + pub fn get_masq_balance_params(mut self, params: &Arc>>) -> Self { + self.get_masq_balance_params = params.clone(); + self + } + + pub fn get_masq_balance_result(self, result: ResultForBalance) -> Self { + self.get_masq_balance_results.borrow_mut().push(result); + self + } + + pub fn get_block_number_result(self, result: LatestBlockNumber) -> Self { + self.get_block_number_results.borrow_mut().push(result); + self + } + + pub fn get_transaction_id_params(mut self, params: &Arc>>) -> Self { + self.get_transaction_id_params = params.clone(); + self + } + + pub fn get_transaction_id_result(self, result: ResultForNonce) -> Self { + self.get_transaction_id_results.borrow_mut().push(result); + self + } +} + +pub fn test_blockchain_interface_is_connected_and_functioning(subject_factory: F) +where + F: Fn(u16, Chain) -> Box, +{ + let port = find_free_port(); + let test_server = TestServer::start( + port, + vec![br#"{"jsonrpc":"2.0","id":0,"result":someGarbage}"#.to_vec()], + ); + let wallet = make_wallet("123"); + let chain = Chain::PolyMainnet; + let subject = subject_factory(port, chain); + + // no assertion for the result, we anticipate an error from a badly formatted response from the server; + // yet enough to prove we have a proper connection + let _ = subject.lower_interface().get_service_fee_balance(&wallet); + + let requests = test_server.requests_so_far(); + let bodies: Vec = requests + .into_iter() + .map(|request| serde_json::from_slice(&request.body()).unwrap()) + .collect(); + assert_eq!( + bodies[0]["params"][0]["data"].to_string()[35..75], + wallet.to_string()[2..] + ); + assert_eq!( + bodies[0]["params"][0]["to"], + format!("{:?}", chain.rec().contract) + ); +} diff --git a/node/src/blockchain/blockchain_interface_initializer.rs b/node/src/blockchain/blockchain_interface_initializer.rs new file mode 100644 index 000000000..7448420a3 --- /dev/null +++ b/node/src/blockchain/blockchain_interface_initializer.rs @@ -0,0 +1,75 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ + BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, +}; +use crate::blockchain::blockchain_interface::BlockchainInterface; +use masq_lib::blockchains::chains::Chain; +use web3::transports::Http; + +pub(in crate::blockchain) struct BlockchainInterfaceInitializer {} + +impl BlockchainInterfaceInitializer { + // TODO when we have multiple chains of fundamentally different architectures and are able to switch them, + // this should probably be replaced by a HashMap of distinct interfaces for each chain + pub fn initialize_interface( + &self, + blockchain_service_url: &str, + chain: Chain, + ) -> Box { + self.initialize_web3_interface(blockchain_service_url, chain) + } + + fn initialize_web3_interface( + &self, + blockchain_service_url: &str, + chain: Chain, + ) -> Box { + match Http::with_max_parallel(blockchain_service_url, REQUESTS_IN_PARALLEL) { + Ok((event_loop_handle, transport)) => Box::new(BlockchainInterfaceWeb3::new( + transport, + event_loop_handle, + chain, + )), + Err(e) => panic!( + "Invalid blockchain service URL \"{}\". Error: {:?}. Chain: {}", + blockchain_service_url, + e, + chain.rec().literal_identifier + ), + } + } +} + +#[cfg(test)] +mod tests { + use crate::blockchain::blockchain_interface_initializer::BlockchainInterfaceInitializer; + use masq_lib::blockchains::chains::Chain; + + use std::net::Ipv4Addr; + + use crate::blockchain::blockchain_interface::test_utils::test_blockchain_interface_is_connected_and_functioning; + + use masq_lib::constants::DEFAULT_CHAIN; + + #[test] + fn initialize_web3_interface_works() { + let subject_factory = |port: u16, chain: Chain| { + let subject = BlockchainInterfaceInitializer {}; + let server_url = &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port); + subject.initialize_web3_interface(server_url, chain) + }; + + test_blockchain_interface_is_connected_and_functioning(subject_factory) + } + + #[test] + #[should_panic(expected = "Invalid blockchain service URL \"http://λ:8545\". \ + Error: Transport(\"InvalidUri(InvalidUriChar)\"). Chain: polygon-mainnet")] + fn invalid_blockchain_url_for_produces_panic_for_web3_interface() { + let blockchain_service_url = "http://λ:8545"; + let subject = BlockchainInterfaceInitializer {}; + + subject.initialize_web3_interface(blockchain_service_url, DEFAULT_CHAIN); + } +} diff --git a/node/src/blockchain/mod.rs b/node/src/blockchain/mod.rs index 7375b60e9..937380fd1 100644 --- a/node/src/blockchain/mod.rs +++ b/node/src/blockchain/mod.rs @@ -1,9 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod batch_payable_tools; + pub mod bip32; pub mod bip39; pub mod blockchain_bridge; pub mod blockchain_interface; +pub mod blockchain_interface_initializer; pub mod payer; pub mod signature; diff --git a/node/src/blockchain/payer.rs b/node/src/blockchain/payer.rs index 3979e054e..f794ee403 100644 --- a/node/src/blockchain/payer.rs +++ b/node/src/blockchain/payer.rs @@ -1,5 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::banned_dao::BAN_CACHE; +use crate::accountant::db_access_objects::banned_dao::BAN_CACHE; use crate::blockchain::signature::SerializableSignature; use crate::sub_lib::wallet::Wallet; use ethsign::Signature; diff --git a/node/src/blockchain/test_utils.rs b/node/src/blockchain/test_utils.rs index 8300a6821..93afc4456 100644 --- a/node/src/blockchain/test_utils.rs +++ b/node/src/blockchain/test_utils.rs @@ -2,33 +2,39 @@ #![cfg(test)] +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; -use crate::blockchain::blockchain_interface::{ - BlockchainError, BlockchainInterface, BlockchainResult, PayableTransactionError, - ProcessedPayableFallible, ResultForBalance, ResultForNonce, ResultForReceipt, - REQUESTS_IN_PARALLEL, +use crate::blockchain::blockchain_interface::blockchain_interface_web3::REQUESTS_IN_PARALLEL; +use crate::blockchain::blockchain_interface::data_structures::errors::{ + BlockchainAgentBuildError, BlockchainError, PayableTransactionError, ResultForReceipt, }; +use crate::blockchain::blockchain_interface::data_structures::{ + ProcessedPayableFallible, RetrievedBlockchainTransactions, +}; +use crate::blockchain::blockchain_interface::lower_level_interface::LowerBCI; +use crate::blockchain::blockchain_interface::test_utils::LowerBCIMock; +use crate::blockchain::blockchain_interface::BlockchainInterface; +use crate::db_config::persistent_configuration::PersistentConfiguration; +use crate::set_arbitrary_id_stamp_in_mock_impl; use crate::sub_lib::wallet::Wallet; +use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use actix::Recipient; use bip39::{Language, Mnemonic, Seed}; use ethereum_types::{BigEndianHash, H256}; use jsonrpc_core as rpc; use lazy_static::lazy_static; +use masq_lib::blockchains::chains::Chain; +use masq_lib::utils::to_string; use std::cell::RefCell; use std::collections::VecDeque; use std::fmt::Debug; use std::sync::{Arc, Mutex}; -use std::time::SystemTime; - -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::blockchain::batch_payable_tools::BatchPayableTools; -use web3::transports::{Batch, EventLoopHandle, Http}; -use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, U256}; -use web3::{BatchTransport, Error as Web3Error, Web3}; +use web3::transports::{EventLoopHandle, Http}; +use web3::types::{Address, BlockNumber, U256}; +use web3::{BatchTransport, Error as Web3Error}; use web3::{RequestId, Transport}; -use crate::blockchain::blockchain_interface::RetrievedBlockchainTransactions; - lazy_static! { static ref BIG_MEANINGLESS_PHRASE: Vec<&'static str> = vec![ "parent", "prevent", "vehicle", "tooth", "crazy", "cruel", "update", "mango", "female", @@ -38,10 +44,7 @@ lazy_static! { } pub fn make_meaningless_phrase_words() -> Vec { - BIG_MEANINGLESS_PHRASE - .iter() - .map(|word| word.to_string()) - .collect() + BIG_MEANINGLESS_PHRASE.iter().map(to_string).collect() } pub fn make_meaningless_phrase() -> String { @@ -55,16 +58,16 @@ pub fn make_meaningless_seed() -> Seed { #[derive(Default)] pub struct BlockchainInterfaceMock { - retrieve_transactions_parameters: Arc>>, + retrieve_transactions_parameters: Arc>>, retrieve_transactions_results: RefCell>>, - estimated_gas_limit_per_payable_results: RefCell>, + build_blockchain_agent_params: Arc>>, + build_blockchain_agent_results: + RefCell, BlockchainAgentBuildError>>>, send_batch_of_payables_params: Arc< Mutex< Vec<( - Wallet, - u64, - U256, + ArbitraryIdStamp, Recipient, Vec, )>, @@ -72,84 +75,57 @@ pub struct BlockchainInterfaceMock { >, send_batch_of_payables_results: RefCell, PayableTransactionError>>>, - get_gas_balance_params: Arc>>, - get_gas_balance_results: RefCell>, - get_token_balance_params: Arc>>, - get_token_balance_results: RefCell>, get_transaction_receipt_params: Arc>>, get_transaction_receipt_results: RefCell>, - contract_address_results: RefCell>, - get_transaction_count_parameters: Arc>>, - get_transaction_count_results: RefCell>>, + arbitrary_id_stamp_opt: Option, + helpers_result: Option>, } impl BlockchainInterface for BlockchainInterfaceMock { fn contract_address(&self) -> Address { - self.contract_address_results.borrow_mut().remove(0) + unimplemented!("not needed so far") } fn retrieve_transactions( &self, - start_block: u64, + start_block: BlockNumber, + end_block: BlockNumber, recipient: &Wallet, ) -> Result { - self.retrieve_transactions_parameters - .lock() - .unwrap() - .push((start_block, recipient.clone())); + self.retrieve_transactions_parameters.lock().unwrap().push(( + start_block, + end_block, + recipient.clone(), + )); self.retrieve_transactions_results.borrow_mut().remove(0) } - fn estimated_gas_limit_per_payable(&self) -> u64 { - *self - .estimated_gas_limit_per_payable_results - .borrow_mut() - .as_ref() - .unwrap() + fn build_blockchain_agent( + &self, + consuming_wallet: &Wallet, + persistent_config: &dyn PersistentConfiguration, + ) -> Result, BlockchainAgentBuildError> { + self.build_blockchain_agent_params.lock().unwrap().push(( + consuming_wallet.clone(), + persistent_config.arbitrary_id_stamp(), + )); + self.build_blockchain_agent_results.borrow_mut().remove(0) } fn send_batch_of_payables( &self, - consuming_wallet: &Wallet, - gas_price: u64, - last_nonce: U256, + agent: Box, new_fingerprints_recipient: &Recipient, accounts: &[PayableAccount], ) -> Result, PayableTransactionError> { self.send_batch_of_payables_params.lock().unwrap().push(( - consuming_wallet.clone(), - gas_price, - last_nonce, + agent.arbitrary_id_stamp(), new_fingerprints_recipient.clone(), accounts.to_vec(), )); self.send_batch_of_payables_results.borrow_mut().remove(0) } - fn get_gas_balance(&self, address: &Wallet) -> ResultForBalance { - self.get_gas_balance_params - .lock() - .unwrap() - .push(address.clone()); - self.get_gas_balance_results.borrow_mut().remove(0) - } - - fn get_token_balance(&self, address: &Wallet) -> ResultForBalance { - self.get_token_balance_params - .lock() - .unwrap() - .push(address.clone()); - self.get_token_balance_results.borrow_mut().remove(0) - } - - fn get_transaction_count(&self, wallet: &Wallet) -> ResultForNonce { - self.get_transaction_count_parameters - .lock() - .unwrap() - .push(wallet.clone()); - self.get_transaction_count_results.borrow_mut().remove(0) - } - fn get_transaction_receipt(&self, hash: H256) -> ResultForReceipt { self.get_transaction_receipt_params .lock() @@ -157,10 +133,17 @@ impl BlockchainInterface for BlockchainInterfaceMock { .push(hash); self.get_transaction_receipt_results.borrow_mut().remove(0) } + + fn lower_interface(&self) -> &dyn LowerBCI { + self.helpers_result.as_ref().unwrap().as_ref() + } } impl BlockchainInterfaceMock { - pub fn retrieve_transactions_params(mut self, params: &Arc>>) -> Self { + pub fn retrieve_transactions_params( + mut self, + params: &Arc>>, + ) -> Self { self.retrieve_transactions_parameters = params.clone(); self } @@ -173,10 +156,21 @@ impl BlockchainInterfaceMock { self } - pub fn estimated_gas_limit_per_payable_result(self, result: u64) -> Self { - self.estimated_gas_limit_per_payable_results + pub fn build_blockchain_agent_params( + mut self, + params: &Arc>>, + ) -> Self { + self.build_blockchain_agent_params = params.clone(); + self + } + + pub fn build_blockchain_agent_result( + self, + result: Result, BlockchainAgentBuildError>, + ) -> Self { + self.build_blockchain_agent_results .borrow_mut() - .replace(result); + .push(result); self } @@ -185,9 +179,7 @@ impl BlockchainInterfaceMock { params: &Arc< Mutex< Vec<( - Wallet, - u64, - U256, + ArbitraryIdStamp, Recipient, Vec, )>, @@ -208,41 +200,6 @@ impl BlockchainInterfaceMock { self } - pub fn get_gas_balance_params(mut self, params: &Arc>>) -> Self { - self.get_gas_balance_params = params.clone(); - self - } - - pub fn get_gas_balance_result(self, result: ResultForBalance) -> Self { - self.get_gas_balance_results.borrow_mut().push(result); - self - } - - pub fn get_token_balance_params(mut self, params: &Arc>>) -> Self { - self.get_token_balance_params = params.clone(); - self - } - - pub fn get_token_balance_result(self, result: ResultForBalance) -> Self { - self.get_token_balance_results.borrow_mut().push(result); - self - } - - pub fn contract_address_result(self, address: Address) -> Self { - self.contract_address_results.borrow_mut().push(address); - self - } - - pub fn get_transaction_count_params(mut self, params: &Arc>>) -> Self { - self.get_transaction_count_parameters = params.clone(); - self - } - - pub fn get_transaction_count_result(self, result: BlockchainResult) -> Self { - self.get_transaction_count_results.borrow_mut().push(result); - self - } - pub fn get_transaction_receipt_params(mut self, params: &Arc>>) -> Self { self.get_transaction_receipt_params = params.clone(); self @@ -254,6 +211,13 @@ impl BlockchainInterfaceMock { .push(result); self } + + pub fn helpers_results(mut self, aggregated_results: Box) -> Self { + self.helpers_result = Some(aggregated_results); + self + } + + set_arbitrary_id_stamp_in_mock_impl!(); } #[derive(Debug, Default, Clone)] @@ -356,174 +320,15 @@ pub fn make_fake_event_loop_handle() -> EventLoopHandle { .0 } -#[derive(Default)] -pub struct BatchPayableToolsFactoryMock { - make_results: RefCell>>>, -} - -impl BatchPayableToolsFactoryMock { - pub fn make_result(self, result: Box>) -> Self { - self.make_results.borrow_mut().push(result); - self - } -} - -#[derive(Default)] -pub struct BatchPayableToolsMock { - sign_transaction_params: Arc< - Mutex< - Vec<( - TransactionParameters, - Web3>, - secp256k1secrets::key::SecretKey, - )>, - >, - >, - sign_transaction_results: RefCell>>, - append_transaction_to_batch_params: Arc>)>>>, - //append_transaction_to_batch returns just the unit type - //batch_wide_timestamp doesn't have params - batch_wide_timestamp_results: RefCell>, - send_new_payable_fingerprints_seeds_params: Arc< - Mutex< - Vec<( - SystemTime, - Recipient, - Vec<(H256, u128)>, - )>, - >, - >, - //new_payable_fingerprints returns just the unit type - submit_batch_params: Arc>>>>, - submit_batch_results: - RefCell>, Web3Error>>>, -} - -impl BatchPayableTools for BatchPayableToolsMock { - fn sign_transaction( - &self, - transaction_params: TransactionParameters, - web3: &Web3>, - key: &secp256k1secrets::key::SecretKey, - ) -> Result { - self.sign_transaction_params.lock().unwrap().push(( - transaction_params.clone(), - web3.clone(), - key.clone(), - )); - self.sign_transaction_results.borrow_mut().remove(0) - } - - fn append_transaction_to_batch(&self, signed_transaction: Bytes, web3: &Web3>) { - self.append_transaction_to_batch_params - .lock() - .unwrap() - .push((signed_transaction, web3.clone())); - } - - fn batch_wide_timestamp(&self) -> SystemTime { - self.batch_wide_timestamp_results.borrow_mut().remove(0) - } - - fn send_new_payable_fingerprints_seeds( - &self, - batch_wide_timestamp: SystemTime, - pp_fingerprint_sub: &Recipient, - hashes_and_balances: &[(H256, u128)], - ) { - self.send_new_payable_fingerprints_seeds_params - .lock() - .unwrap() - .push(( - batch_wide_timestamp, - (*pp_fingerprint_sub).clone(), - hashes_and_balances.to_vec(), - )); - } - - fn submit_batch( - &self, - web3: &Web3>, - ) -> Result>, Web3Error> { - self.submit_batch_params.lock().unwrap().push(web3.clone()); - self.submit_batch_results.borrow_mut().remove(0) - } -} - -impl BatchPayableToolsMock { - pub fn sign_transaction_params( - mut self, - params: &Arc< - Mutex< - Vec<( - TransactionParameters, - Web3>, - secp256k1secrets::key::SecretKey, - )>, - >, - >, - ) -> Self { - self.sign_transaction_params = params.clone(); - self - } - pub fn sign_transaction_result(self, result: Result) -> Self { - self.sign_transaction_results.borrow_mut().push(result); - self - } - - pub fn batch_wide_timestamp_result(self, result: SystemTime) -> Self { - self.batch_wide_timestamp_results.borrow_mut().push(result); - self - } - - pub fn send_new_payable_fingerprint_credentials_params( - mut self, - params: &Arc< - Mutex< - Vec<( - SystemTime, - Recipient, - Vec<(H256, u128)>, - )>, - >, - >, - ) -> Self { - self.send_new_payable_fingerprints_seeds_params = params.clone(); - self - } - - pub fn append_transaction_to_batch_params( - mut self, - params: &Arc>)>>>, - ) -> Self { - self.append_transaction_to_batch_params = params.clone(); - self - } - - pub fn submit_batch_params(mut self, params: &Arc>>>>) -> Self { - self.submit_batch_params = params.clone(); - self - } - pub fn submit_batch_result( - self, - result: Result>, Web3Error>, - ) -> Self { - self.submit_batch_results.borrow_mut().push(result); - self - } -} - -pub fn make_default_signed_transaction() -> SignedTransaction { - SignedTransaction { - message_hash: Default::default(), - v: 0, - r: Default::default(), - s: Default::default(), - raw_transaction: Default::default(), - transaction_hash: Default::default(), - } -} - pub fn make_tx_hash(base: u32) -> H256 { H256::from_uint(&U256::from(base)) } + +pub fn all_chains() -> [Chain; 4] { + [ + Chain::EthMainnet, + Chain::PolyMainnet, + Chain::PolyMumbai, + Chain::Dev, + ] +} diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 533c9cb41..5d40c2379 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -15,7 +15,7 @@ use crate::json_discriminator_factory::JsonDiscriminatorFactory; use crate::listener_handler::ListenerHandler; use crate::listener_handler::ListenerHandlerFactory; use crate::listener_handler::ListenerHandlerFactoryReal; -use crate::neighborhood::DEFAULT_MIN_HOPS_COUNT; +use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::node_configurator::node_configurator_standard::{ NodeConfiguratorStandardPrivileged, NodeConfiguratorStandardUnprivileged, }; @@ -398,7 +398,7 @@ impl BootstrapperConfig { consuming_wallet_opt: None, neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: DEFAULT_MIN_HOPS_COUNT, + min_hops: DEFAULT_MIN_HOPS, }, when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, } @@ -724,9 +724,8 @@ mod tests { use crate::discriminator::Discriminator; use crate::discriminator::UnmaskedChunk; use crate::listener_handler::{ListenerHandler, ListenerHandlerFactory}; - use crate::node_test_utils::make_stream_handler_pool_subs_from; - use crate::node_test_utils::TestLogOwner; use crate::node_test_utils::{extract_log, DirsWrapperMock, IdWrapperMock}; + use crate::node_test_utils::{make_stream_handler_pool_subs_from_recorder, TestLogOwner}; use crate::server_initializer::test_utils::LoggerInitializerWrapperMock; use crate::server_initializer::LoggerInitializerWrapper; use crate::stream_handler_pool::StreamHandlerPoolSubs; @@ -741,7 +740,7 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::socket_server::ConfiguredByPrivilege; use crate::sub_lib::stream_connector::ConnectionInfo; - use crate::test_utils::neighborhood_test_utils::MIN_HOPS_COUNT_FOR_TEST; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::RecordAwaiter; @@ -753,8 +752,8 @@ mod tests { }; use crate::test_utils::{assert_contains, rate_pack}; use crate::test_utils::{main_cryptde, make_wallet}; - use actix::Recipient; use actix::System; + use actix::{Actor, Recipient}; use crossbeam_channel::unbounded; use futures::Future; use lazy_static::lazy_static; @@ -767,7 +766,7 @@ mod tests { use masq_lib::test_utils::fake_stream_holder::FakeStreamHolder; use masq_lib::test_utils::logging::{init_test_logging, TestLog, TestLogHandler}; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::utils::find_free_port; + use masq_lib::utils::{find_free_port, to_string}; use std::cell::RefCell; use std::collections::HashMap; use std::io; @@ -934,9 +933,9 @@ mod tests { sudo_user: Option<&str>, ) -> EnvironmentWrapperMock { EnvironmentWrapperMock { - sudo_uid: sudo_uid.map(|s| s.to_string()), - sudo_gid: sudo_gid.map(|s| s.to_string()), - sudo_user: sudo_user.map(|s| s.to_string()), + sudo_uid: sudo_uid.map(to_string), + sudo_gid: sudo_gid.map(to_string), + sudo_user: sudo_user.map(to_string), } } } @@ -1152,6 +1151,8 @@ mod tests { "2.2.2.2", "--real-user", "123:456:/home/booga", + "--chain", + "polygon-mumbai", ])) .unwrap(); @@ -1234,7 +1235,7 @@ mod tests { let clandestine_port_opt = Some(44444); let neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::OriginateOnly(vec![], rate_pack(9)), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let earning_wallet = make_wallet("earning wallet"); let consuming_wallet_opt = Some(make_wallet("consuming wallet")); @@ -1853,7 +1854,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; config.data_directory = data_dir.clone(); config.clandestine_port_opt = Some(port); @@ -1923,7 +1924,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; config.data_directory = data_dir.clone(); config.clandestine_port_opt = None; @@ -1972,7 +1973,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() @@ -2009,7 +2010,7 @@ mod tests { Chain::EthRopsten, cryptde, ))]), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() @@ -2039,7 +2040,7 @@ mod tests { config.clandestine_port_opt = None; config.neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() @@ -2070,7 +2071,7 @@ mod tests { config.data_directory = data_dir.to_path_buf(); config.neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::Standard(NodeAddr::default(), vec![], DEFAULT_RATE_PACK), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let mut subject = BootstrapperBuilder::new().config(config).build(); subject.set_up_clandestine_port(); @@ -2227,7 +2228,9 @@ mod tests { StreamHandlerPoolCluster { recording: Some(recording), awaiter: Some(awaiter), - subs: make_stream_handler_pool_subs_from(Some(stream_handler_pool)), + subs: make_stream_handler_pool_subs_from_recorder( + &stream_handler_pool.start(), + ), } }; diff --git a/node/src/daemon/daemon_initializer.rs b/node/src/daemon/daemon_initializer.rs index beda18164..61519c047 100644 --- a/node/src/daemon/daemon_initializer.rs +++ b/node/src/daemon/daemon_initializer.rs @@ -20,8 +20,6 @@ use masq_lib::shared_schema::ConfiguratorError; use std::collections::HashMap; use masq_lib::utils::ExpectValue; -#[cfg(test)] -use std::any::Any; use std::path::PathBuf; use std::str::FromStr; @@ -85,7 +83,7 @@ impl DaemonInitializer for DaemonInitializerReal { self.split(system, receiver); Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } pub trait Rerunner { diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index d97f080b2..6ad031cc6 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -11,13 +11,14 @@ use crate::db_config::config_dao_null::ConfigDaoNull; use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; +use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::node_configurator::node_configurator_standard::privileged_parse_args; use crate::node_configurator::unprivileged_parse_args_configuration::{ UnprivilegedParseArgsConfiguration, UnprivilegedParseArgsConfigurationDaoNull, UnprivilegedParseArgsConfigurationDaoReal, }; use crate::node_configurator::{ - data_directory_from_context, determine_config_file_path, DirsWrapper, DirsWrapperReal, + data_directory_from_context, determine_fundamentals, DirsWrapper, DirsWrapperReal, }; use crate::sub_lib::accountant::PaymentThresholds as PaymentThresholdsFromAccountant; use crate::sub_lib::accountant::DEFAULT_SCAN_INTERVALS; @@ -36,7 +37,7 @@ use masq_lib::multi_config::{ CommandLineVcl, ConfigFileVcl, EnvironmentVcl, MultiConfig, VirtualCommandLine, }; use masq_lib::shared_schema::{shared_app, ConfiguratorError}; -use masq_lib::utils::ExpectValue; +use masq_lib::utils::{add_chain_specific_directory, to_string, ExpectValue}; use std::collections::HashMap; use std::fmt::Display; use std::net::{IpAddr, Ipv4Addr}; @@ -124,15 +125,17 @@ impl SetupReporter for SetupReporterReal { crate::bootstrapper::RealUser::new(None, None, None) .populate(self.dirs_wrapper.as_ref()) }); - let data_directory = match all_but_configured.get("data-directory") { - Some(uisrv) if uisrv.status == Set => PathBuf::from(&uisrv.value), - _ => data_directory_from_context( - self.dirs_wrapper.as_ref(), - &real_user, - &data_directory_opt, - chain, - ), - }; + + let (data_directory, data_dir_status) = self.get_data_directory_and_status( + existing_setup.get("data-directory"), + incoming_setup.get("data-directory"), + &all_but_configured, + chain, + real_user, + data_directory_opt, + ); + let data_directory_setup = + Self::construct_cluster_with_only_data_directory(&data_directory, data_dir_status); let (configured_setup, error_opt) = self.calculate_configured_setup(&all_but_configured, &data_directory); if let Some(error) = error_opt { @@ -141,7 +144,12 @@ impl SetupReporter for SetupReporterReal { error_so_far.param_errors.iter().for_each(|param_error| { let _ = incoming_setup.remove(¶m_error.parameter); }); - let combined_setup = Self::combine_clusters(vec![&all_but_configured, &configured_setup]); + let combined_setup = Self::combine_clusters(vec![ + &all_but_configured, + &configured_setup, + &data_directory_setup, + ]); + eprintln_setup("DATA DIRECTORY SETUP", &data_directory_setup); eprintln_setup("CONFIGURED", &configured_setup); eprintln_setup("COMBINED", &combined_setup); let final_setup = value_retrievers(self.dirs_wrapper.as_ref()) @@ -254,6 +262,81 @@ impl SetupReporterReal { }) } + fn get_data_directory_and_status( + &self, + existing_setup_dir: Option<&UiSetupResponseValue>, + incoming_setup_dir: Option<&UiSetupResponseValue>, + all_but_configured: &SetupCluster, + chain: masq_lib::blockchains::chains::Chain, + real_user: crate::bootstrapper::RealUser, + data_directory_opt: Option, + ) -> (PathBuf, UiSetupResponseValueStatus) { + let (data_directory, data_dir_status) = match all_but_configured.get("data-directory") { + Some(uisrv) if uisrv.status == Set => { + Self::determine_setup_value_of_set_data_directory( + uisrv, + match existing_setup_dir { + Some(..) => existing_setup_dir, + None => None, + }, + match incoming_setup_dir { + Some(..) => incoming_setup_dir, + None => None, + }, + chain, + ) + } + _ => match data_directory_opt { + //this can mean only that environment variables had it + Some(data_dir) => (data_dir, UiSetupResponseValueStatus::Configured), + None => { + let data_dir = + data_directory_from_context(self.dirs_wrapper.as_ref(), &real_user, chain); + (data_dir, Default) + } + }, + }; + (data_directory, data_dir_status) + } + + fn determine_setup_value_of_set_data_directory( + semi_clusters_val: &UiSetupResponseValue, + existing_setup_dir: Option<&UiSetupResponseValue>, + incoming_setup_dir: Option<&UiSetupResponseValue>, + chain: BlockChain, + ) -> (PathBuf, UiSetupResponseValueStatus) { + match (existing_setup_dir, incoming_setup_dir) { + (_, Some(_)) => (add_chain_specific_directory(chain, Path::new(&semi_clusters_val.value)), semi_clusters_val.status), + (Some(recent_value),None) =>(Self::reconstitute_data_dir_by_chain(&recent_value.value, chain), recent_value.status), + (None, None) => panic!("broken code: data-directory value is neither in existing_setup or incoming_setup and yet this value \"{}\" was found in the merged cluster", semi_clusters_val.value) + } + } + + fn reconstitute_data_dir_by_chain( + previously_processed_data_dir: &str, + current_chain: BlockChain, + ) -> PathBuf { + let mut path = PathBuf::from(&previously_processed_data_dir); + path.pop(); + add_chain_specific_directory(current_chain, &path) + } + + fn construct_cluster_with_only_data_directory( + data_directory: &Path, + data_dir_status: UiSetupResponseValueStatus, + ) -> SetupCluster { + let mut setup = HashMap::new(); + setup.insert( + "data-directory".to_string(), + UiSetupResponseValue::new( + "data-directory", + data_directory.to_str().expect("data-directory expected"), + data_dir_status, + ), + ); + setup + } + fn calculate_fundamentals( dirs_wrapper: &dyn DirsWrapper, combined_setup: &SetupCluster, @@ -412,8 +495,8 @@ impl SetupReporterReal { Some(command_line) => command_line, None => vec![], }; - let (config_file_path, user_specified) = - determine_config_file_path(dirs_wrapper, &app, &command_line)?; + let (config_file_path, user_specified, _data_directory, _real_user) = + determine_fundamentals(dirs_wrapper, &app, &command_line)?; let config_file_vcl = match ConfigFileVcl::new(&config_file_path, user_specified) { Ok(cfv) => cfv, Err(e) => return Err(ConfiguratorError::required("config-file", &e.to_string())), @@ -632,16 +715,10 @@ impl ValueRetriever for DataDirectory { ) -> Option<(String, UiSetupResponseValueStatus)> { let real_user = &bootstrapper_config.real_user; let chain = bootstrapper_config.blockchain_bridge_config.chain; - let data_directory_opt = None; Some(( - data_directory_from_context( - self.dirs_wrapper.as_ref(), - real_user, - &data_directory_opt, - chain, - ) - .to_string_lossy() - .to_string(), + data_directory_from_context(self.dirs_wrapper.as_ref(), real_user, chain) + .to_string_lossy() + .to_string(), Default, )) } @@ -706,10 +783,7 @@ impl ValueRetriever for DnsServers { if ip_addrs.iter().any(|ip_addr| ip_addr.is_loopback()) { return None; } - let dns_servers = ip_addrs - .into_iter() - .map(|ip_addr| ip_addr.to_string()) - .join(","); + let dns_servers = ip_addrs.into_iter().map(to_string).join(","); Some((dns_servers, Default)) } Err(e) => { @@ -830,11 +904,44 @@ impl ValueRetriever for MappingProtocol { } } -struct MinHops {} +struct MinHops { + logger: Logger, +} + +impl MinHops { + pub fn new() -> Self { + Self { + logger: Logger::new("MinHops"), + } + } +} + impl ValueRetriever for MinHops { fn value_name(&self) -> &'static str { "min-hops" } + + fn computed_default( + &self, + _bootstrapper_config: &BootstrapperConfig, + persistent_config: &dyn PersistentConfiguration, + _db_password_opt: &Option, + ) -> Option<(String, UiSetupResponseValueStatus)> { + match persistent_config.min_hops() { + Ok(min_hops) => Some(if min_hops == DEFAULT_MIN_HOPS { + (DEFAULT_MIN_HOPS.to_string(), Default) + } else { + (min_hops.to_string(), Configured) + }), + Err(e) => { + error!( + self.logger, + "No value for min hops found in database; database is corrupt: {:?}", e + ); + None + } + } + } } struct NeighborhoodMode {} @@ -1063,7 +1170,7 @@ fn value_retrievers(dirs_wrapper: &dyn DirsWrapper) -> Vec, +} impl DumpConfigRunner for DumpConfigRunnerReal { fn go(&self, streams: &mut StdStreams, args: &[String]) -> Result<(), ConfiguratorError> { + let dirs_wrapper_ref: &dyn DirsWrapper = self.dirs_wrapper.as_ref(); let (real_user, data_directory, chain, password_opt) = - distill_args(&DirsWrapperReal {}, args)?; + distill_args(dirs_wrapper_ref, args)?; let cryptde = CryptDEReal::new(chain); PrivilegeDropperReal::new().drop_privileges(&real_user); let config_dao = make_config_dao( @@ -50,7 +50,7 @@ impl DumpConfigRunner for DumpConfigRunnerReal { Ok(()) } - implement_as_any!(); + as_any_in_trait_impl!(); } fn write_string(streams: &mut StdStreams, json: String) { @@ -145,10 +145,12 @@ fn distill_args( Box::new(EnvironmentVcl::new(&app)), ]; let multi_config = make_new_multi_config(&app, vcls)?; - let (real_user, data_directory_opt, chain) = - real_user_data_directory_opt_and_chain(dirs_wrapper, &multi_config); - let directory = - data_directory_from_context(dirs_wrapper, &real_user, &data_directory_opt, chain); + let (real_user, data_directory_path, chain) = + real_user_data_directory_path_and_chain(dirs_wrapper, &multi_config); + let directory = match data_directory_path { + Some(data_dir) => data_dir, + None => data_directory_from_context(dirs_wrapper, &real_user, chain), + }; let password_opt = value_m!(multi_config, "db-password", String); Ok((real_user, directory, chain, password_opt)) } @@ -164,18 +166,21 @@ mod tests { PersistentConfiguration, PersistentConfigurationReal, }; use crate::db_config::typed_config_layer::encode_bytes; + use crate::node_configurator::DirsWrapperReal; + use crate::node_test_utils::DirsWrapperMock; use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::neighborhood::{NodeDescriptor, DEFAULT_RATE_PACK}; use crate::test_utils::database_utils::bring_db_0_back_to_life_and_return_connection; use crate::test_utils::{main_cryptde, ArgsBuilder}; use masq_lib::constants::CURRENT_SCHEMA_VERSION; + use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::test_utils::environment_guard::{ClapGuard, EnvironmentGuard}; use masq_lib::test_utils::fake_stream_holder::FakeStreamHolder; - use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; + use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::NeighborhoodModeLight; use rustc_hex::ToHex; - use std::fs::File; + use std::fs::{create_dir_all, File}; use std::io::ErrorKind; use std::panic::{catch_unwind, AssertUnwindSafe}; @@ -192,7 +197,9 @@ mod tests { .param("--real-user", "123::") .opt("--dump-config") .into(); - let subject = DumpConfigRunnerReal; + let subject = DumpConfigRunnerReal { + dirs_wrapper: Box::new(DirsWrapperReal), + }; let caught_panic = catch_unwind(AssertUnwindSafe(|| { subject.go(&mut holder.streams(), args_vec.as_slice()) @@ -218,6 +225,8 @@ mod tests { "config_dumper", "dump_config_does_not_migrate_obsolete_database", ); + create_dir_all(&data_dir) + .expect("Could not create chain directory inside config_file_not_specified_but_exists home/MASQ directory"); let conn = bring_db_0_back_to_life_and_return_connection(&data_dir.join(DATABASE_FILE)); let dao = ConfigDaoReal::new(Box::new(ConnectionWrapperReal::new(conn))); let schema_version_before = dao.get("schema_version").unwrap().value_opt.unwrap(); @@ -226,10 +235,12 @@ mod tests { let args_vec: Vec = ArgsBuilder::new() .param("--data-directory", data_dir.to_str().unwrap()) .param("--real-user", "123::") - .param("--chain", TEST_DEFAULT_CHAIN.rec().literal_identifier) + .param("--chain", Chain::PolyMainnet.rec().literal_identifier) .opt("--dump-config") .into(); - let subject = DumpConfigRunnerReal; + let subject = DumpConfigRunnerReal { + dirs_wrapper: Box::new(DirsWrapperReal), + }; let result = subject.go(&mut holder.streams(), args_vec.as_slice()); @@ -239,22 +250,19 @@ mod tests { assert_eq!(holder.stderr.get_bytes().is_empty(), true); } - #[test] - fn dump_config_dumps_existing_database_without_password() { + fn check_that_dump_config_dumps_existing_database_without_password( + database_path: PathBuf, + mock_dirs_wrapper_opt: Option>, + non_default_data_directory_opt: Option, + ) { let _clap_guard = ClapGuard::new(); - let data_dir = ensure_node_home_directory_exists( - "config_dumper", - "dump_config_dumps_existing_database_without_password", - ) - .join("MASQ") - .join(TEST_DEFAULT_CHAIN.rec().literal_identifier); let mut holder = FakeStreamHolder::new(); { let conn = DbInitializerReal::default() .initialize( - &data_dir, + &database_path, DbInitializationConfig::create_or_migrate(ExternalData::new( - TEST_DEFAULT_CHAIN, + Chain::PolyMainnet, NeighborhoodModeLight::ZeroHop, None, )), @@ -291,13 +299,21 @@ mod tests { .set_blockchain_service_url("https://infura.io/ID") .unwrap() } - let args_vec: Vec = ArgsBuilder::new() - .param("--data-directory", data_dir.to_str().unwrap()) + let mut args_builder = ArgsBuilder::new() .param("--real-user", "123::") - .param("--chain", TEST_DEFAULT_CHAIN.rec().literal_identifier) - .opt("--dump-config") - .into(); - let subject = DumpConfigRunnerReal; + .param("--chain", Chain::PolyMainnet.rec().literal_identifier) + .opt("--dump-config"); + if let Some(data_dir) = non_default_data_directory_opt { + args_builder = args_builder.param("--data-directory", data_dir.to_str().unwrap()); + } + let args_vec: Vec = args_builder.into(); + let dirs_wrapper = mock_dirs_wrapper_opt.unwrap_or(Box::new(DirsWrapperMock { + data_dir_result: Some(PathBuf::from("/home/booga/.local/share".to_string())), + home_dir_result: Some(PathBuf::from("/home/booga".to_string())), + })); + let subject = DumpConfigRunnerReal { + dirs_wrapper: dirs_wrapper, + }; let result = subject.go(&mut holder.streams(), args_vec.as_slice()); @@ -308,7 +324,7 @@ mod tests { x => panic!("Expected JSON object; found {:?}", x), }; let conn = DbInitializerReal::default() - .initialize(&data_dir, DbInitializationConfig::panic_on_migration()) + .initialize(&database_path, DbInitializationConfig::panic_on_migration()) .unwrap(); let dao = ConfigDaoReal::new(conn); assert_value("blockchainServiceUrl", "https://infura.io/ID", &map); @@ -326,7 +342,7 @@ mod tests { ); assert_value( "chainName", - TEST_DEFAULT_CHAIN.rec().literal_identifier, + Chain::PolyMainnet.rec().literal_identifier, &map, ); assert_value("gasPrice", "1", &map); @@ -339,7 +355,7 @@ mod tests { assert_value("schemaVersion", &CURRENT_SCHEMA_VERSION.to_string(), &map); assert_value( "startBlock", - &TEST_DEFAULT_CHAIN.rec().contract_creation_block.to_string(), + &Chain::PolyMainnet.rec().contract_creation_block.to_string(), &map, ); assert_value( @@ -357,22 +373,60 @@ mod tests { assert!(output.ends_with("\n}\n")) //asserting that there is a blank line at the end } + #[test] + fn dump_config_dumps_existing_database_without_password_and_data_dir_specified() { + let home_dir = ensure_node_home_directory_exists( + "config_dumper", + "dump_config_dumps_existing_database_without_password_and_data_dir_specified", + ); + let data_dir = home_dir.join("data_dir"); + let database_path = data_dir.clone(); + let mock_dirs_wrapper_opt = None; + let non_default_data_directory_opt = Some(data_dir); + check_that_dump_config_dumps_existing_database_without_password( + database_path, + mock_dirs_wrapper_opt, + non_default_data_directory_opt, + ); + } + + #[test] + fn dump_config_dumps_existing_database_without_password_and_default_data_dir() { + let home_dir = ensure_node_home_directory_exists( + "config_dumper", + "dump_config_dumps_existing_database_without_password_and_default_data_dir", + ); + let data_dir = home_dir.join("data_dir"); + let database_path = data_dir + .join("MASQ") + .join(DEFAULT_CHAIN.rec().literal_identifier); + let mock_dirs_wrapper_opt = Some(Box::new( + DirsWrapperMock::new() + .data_dir_result(Some(data_dir)) + .home_dir_result(Some(home_dir)), + ) as Box); + let non_default_data_directory_opt = None; + check_that_dump_config_dumps_existing_database_without_password( + database_path, + mock_dirs_wrapper_opt, + non_default_data_directory_opt, + ); + } + #[test] fn dump_config_dumps_existing_database_with_correct_password() { let _clap_guard = ClapGuard::new(); let data_dir = ensure_node_home_directory_exists( "config_dumper", "dump_config_dumps_existing_database_with_correct_password", - ) - .join("MASQ") - .join(TEST_DEFAULT_CHAIN.rec().literal_identifier); + ); let mut holder = FakeStreamHolder::new(); { let conn = DbInitializerReal::default() .initialize( &data_dir, DbInitializationConfig::create_or_migrate(ExternalData::new( - TEST_DEFAULT_CHAIN, + Chain::PolyMainnet, NeighborhoodModeLight::ConsumeOnly, None, )), @@ -393,12 +447,12 @@ mod tests { Some(vec![ NodeDescriptor::try_from(( main_cryptde(), - "masq://eth-ropsten:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234", + "masq://polygon-mainnet:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234", )) .unwrap(), NodeDescriptor::try_from(( main_cryptde(), - "masq://eth-ropsten:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", + "masq://polygon-mainnet:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", )) .unwrap(), ]), @@ -412,11 +466,13 @@ mod tests { let args_vec: Vec = ArgsBuilder::new() .param("--data-directory", data_dir.to_str().unwrap()) .param("--real-user", "123::") - .param("--chain", TEST_DEFAULT_CHAIN.rec().literal_identifier) + .param("--chain", Chain::PolyMainnet.rec().literal_identifier) .param("--db-password", "password") .opt("--dump-config") .into(); - let subject = DumpConfigRunnerReal; + let subject = DumpConfigRunnerReal { + dirs_wrapper: Box::new(DirsWrapperReal), + }; let result = subject.go(&mut holder.streams(), args_vec.as_slice()); @@ -442,14 +498,14 @@ mod tests { "0x0123456789012345678901234567890123456789", &map, ); - assert_value("chainName", "eth-ropsten", &map); + assert_value("chainName", "polygon-mainnet", &map); assert_value("gasPrice", "1", &map); - assert_value("pastNeighbors", "masq://eth-ropsten:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234,masq://eth-ropsten:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", &map); + assert_value("pastNeighbors", "masq://polygon-mainnet:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234,masq://polygon-mainnet:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", &map); assert_value("neighborhoodMode", "consume-only", &map); assert_value("schemaVersion", &CURRENT_SCHEMA_VERSION.to_string(), &map); assert_value( "startBlock", - &TEST_DEFAULT_CHAIN.rec().contract_creation_block.to_string(), + &Chain::PolyMainnet.rec().contract_creation_block.to_string(), &map, ); let expected_ee_entry = dao.get("example_encrypted").unwrap().value_opt.unwrap(); @@ -471,16 +527,14 @@ mod tests { let data_dir = ensure_node_home_directory_exists( "config_dumper", "dump_config_dumps_existing_database_with_incorrect_password", - ) - .join("MASQ") - .join(TEST_DEFAULT_CHAIN.rec().literal_identifier); + ); let mut holder = FakeStreamHolder::new(); { let conn = DbInitializerReal::default() .initialize( &data_dir, DbInitializationConfig::create_or_migrate(ExternalData::new( - TEST_DEFAULT_CHAIN, + Chain::PolyMainnet, NeighborhoodModeLight::Standard, None, )), @@ -501,12 +555,12 @@ mod tests { Some(vec![ NodeDescriptor::try_from(( main_cryptde(), - "masq://eth-ropsten:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234", + "masq://polygon-mainnet:QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU@1.2.3.4:1234", )) .unwrap(), NodeDescriptor::try_from(( main_cryptde(), - "masq://eth-ropsten:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", + "masq://polygon-mainnet:QkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY@2.3.4.5:2345", )) .unwrap(), ]), @@ -520,11 +574,13 @@ mod tests { let args_vec: Vec = ArgsBuilder::new() .param("--data-directory", data_dir.to_str().unwrap()) .param("--real-user", "123::") - .param("--chain", TEST_DEFAULT_CHAIN.rec().literal_identifier) + .param("--chain", Chain::PolyMainnet.rec().literal_identifier) .param("--db-password", "incorrect") .opt("--dump-config") .into(); - let subject = DumpConfigRunnerReal; + let subject = DumpConfigRunnerReal { + dirs_wrapper: Box::new(DirsWrapperReal), + }; let result = subject.go(&mut holder.streams(), args_vec.as_slice()); @@ -553,7 +609,7 @@ mod tests { ); assert_value( "chainName", - TEST_DEFAULT_CHAIN.rec().literal_identifier, + Chain::PolyMainnet.rec().literal_identifier, &map, ); assert_value("gasPrice", "1", &map); @@ -566,7 +622,7 @@ mod tests { assert_value("schemaVersion", &CURRENT_SCHEMA_VERSION.to_string(), &map); assert_value( "startBlock", - &TEST_DEFAULT_CHAIN.rec().contract_creation_block.to_string(), + &Chain::PolyMainnet.rec().contract_creation_block.to_string(), &map, ); assert_value( diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index b411039f9..2fad52508 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -3,6 +3,7 @@ use crate::database::connection_wrapper::{ConnectionWrapper, ConnectionWrapperRe use crate::database::db_migrations::db_migrator::{DbMigrator, DbMigratorReal}; use crate::db_config::secure_config_layer::EXAMPLE_ENCRYPTED; +use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; use crate::sub_lib::utils::db_connection_launch_panic; @@ -232,6 +233,13 @@ impl DbInitializerReal { false, "last successful protocol for port mapping on the router", ); + Self::set_config_value( + conn, + "min_hops", + Some(&DEFAULT_MIN_HOPS.to_string()), + false, + "min hops", + ); Self::set_config_value( conn, "payment_thresholds", @@ -253,6 +261,7 @@ impl DbInitializerReal { false, "scan intervals", ); + Self::set_config_value(conn, "max_block_count", None, false, "maximum block count"); } fn create_pending_payable_table(&self, conn: &Connection) { @@ -735,7 +744,6 @@ mod tests { use itertools::Either::{Left, Right}; use itertools::{Either, Itertools}; use masq_lib::blockchains::chains::Chain; - use masq_lib::constants::CURRENT_SCHEMA_VERSION; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::{ ensure_node_home_directory_does_not_exist, ensure_node_home_directory_exists, @@ -757,7 +765,7 @@ mod tests { #[test] fn constants_have_correct_values() { assert_eq!(DATABASE_FILE, "node-data.db"); - assert_eq!(CURRENT_SCHEMA_VERSION, 7); + assert_eq!(CURRENT_SCHEMA_VERSION, 9); } #[test] @@ -1032,6 +1040,8 @@ mod tests { false, ); verify(&mut config_vec, "mapping_protocol", None, false); + verify(&mut config_vec, "max_block_count", None, false); + verify(&mut config_vec, "min_hops", Some("3"), false); verify( &mut config_vec, "neighborhood_mode", diff --git a/node/src/database/db_migrations/db_migrator.rs b/node/src/database/db_migrations/db_migrator.rs index aafe93574..4dba9ce97 100644 --- a/node/src/database/db_migrations/db_migrator.rs +++ b/node/src/database/db_migrations/db_migrator.rs @@ -9,6 +9,8 @@ use crate::database::db_migrations::migrations::migration_3_to_4::Migrate_3_to_4 use crate::database::db_migrations::migrations::migration_4_to_5::Migrate_4_to_5; use crate::database::db_migrations::migrations::migration_5_to_6::Migrate_5_to_6; use crate::database::db_migrations::migrations::migration_6_to_7::Migrate_6_to_7; +use crate::database::db_migrations::migrations::migration_7_to_8::Migrate_7_to_8; +use crate::database::db_migrations::migrations::migration_8_to_9::Migrate_8_to_9; use crate::database::db_migrations::migrator_utils::{ DBMigDeclarator, DBMigrationUtilities, DBMigrationUtilitiesReal, DBMigratorInnerConfiguration, }; @@ -75,6 +77,8 @@ impl DbMigratorReal { &Migrate_4_to_5, &Migrate_5_to_6, &Migrate_6_to_7, + &Migrate_7_to_8, + &Migrate_8_to_9, ] } diff --git a/node/src/database/db_migrations/migrations/migration_3_to_4.rs b/node/src/database/db_migrations/migrations/migration_3_to_4.rs index 2853bb94d..e06bd50d0 100644 --- a/node/src/database/db_migrations/migrations/migration_3_to_4.rs +++ b/node/src/database/db_migrations/migrations/migration_3_to_4.rs @@ -125,7 +125,7 @@ mod tests { }; use bip39::{Language, Mnemonic, MnemonicType, Seed}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; - use masq_lib::utils::derivation_path; + use masq_lib::utils::{derivation_path, to_string}; use rand::Rng; use rusqlite::ToSql; use std::panic::{catch_unwind, AssertUnwindSafe}; @@ -294,9 +294,9 @@ mod tests { seed_encrypted_opt: Option<&str>, ) -> String { let mig_declarator = &DBMigDeclaratorMock::default(); - let consuming_path_opt = consuming_path_opt.map(|str| str.to_string()); - let example_encrypted_opt = example_encrypted_opt.map(|str| str.to_string()); - let seed_encrypted_opt = seed_encrypted_opt.map(|str| str.to_string()); + let consuming_path_opt = consuming_path_opt.map(to_string); + let example_encrypted_opt = example_encrypted_opt.map(to_string); + let seed_encrypted_opt = seed_encrypted_opt.map(to_string); let panic = catch_unwind(AssertUnwindSafe(|| { Migrate_3_to_4::maybe_exchange_seed_for_private_key( consuming_path_opt, diff --git a/node/src/database/db_migrations/migrations/migration_4_to_5.rs b/node/src/database/db_migrations/migrations/migration_4_to_5.rs index d46c6ae43..7b3af68bd 100644 --- a/node/src/database/db_migrations/migrations/migration_4_to_5.rs +++ b/node/src/database/db_migrations/migrations/migration_4_to_5.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; +use crate::accountant::db_access_objects::utils::VigilantRusqliteFlatten; use crate::database::db_migrations::db_migrator::DatabaseMigration; use crate::database::db_migrations::migrator_utils::DBMigDeclarator; @@ -76,7 +76,7 @@ impl DatabaseMigration for Migrate_4_to_5 { #[cfg(test)] mod tests { - use crate::accountant::database_access_objects::utils::{from_time_t, to_time_t}; + use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::database::connection_wrapper::{ConnectionWrapper, ConnectionWrapperReal}; use crate::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, DATABASE_FILE, diff --git a/node/src/database/db_migrations/migrations/migration_6_to_7.rs b/node/src/database/db_migrations/migrations/migration_6_to_7.rs index 0fa8ab6c9..12f14ecfc 100644 --- a/node/src/database/db_migrations/migrations/migration_6_to_7.rs +++ b/node/src/database/db_migrations/migrations/migration_6_to_7.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::big_int_processing::big_int_divider::BigIntDivider; -use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; +use crate::accountant::db_access_objects::utils::VigilantRusqliteFlatten; +use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; use crate::accountant::gwei_to_wei; use crate::database::db_migrations::db_migrator::DatabaseMigration; use crate::database::db_migrations::migrator_utils::{ diff --git a/node/src/database/db_migrations/migrations/migration_7_to_8.rs b/node/src/database/db_migrations/migrations/migration_7_to_8.rs new file mode 100644 index 000000000..fea07a2fd --- /dev/null +++ b/node/src/database/db_migrations/migrations/migration_7_to_8.rs @@ -0,0 +1,79 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::database::db_migrations::db_migrator::DatabaseMigration; +use crate::database::db_migrations::migrator_utils::DBMigDeclarator; +use crate::neighborhood::DEFAULT_MIN_HOPS; + +#[allow(non_camel_case_types)] +pub struct Migrate_7_to_8; + +impl DatabaseMigration for Migrate_7_to_8 { + fn migrate<'a>( + &self, + mig_declaration_utilities: Box, + ) -> rusqlite::Result<()> { + let statement = format!( + "INSERT INTO config (name, value, encrypted) VALUES ('min_hops', '{DEFAULT_MIN_HOPS}', 0)", + ); + mig_declaration_utilities.execute_upon_transaction(&[&statement]) + } + + fn old_version(&self) -> usize { + 7 + } +} + +#[cfg(test)] +mod tests { + use crate::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, DATABASE_FILE, + }; + use crate::database::db_migrations::db_migrator::DatabaseMigration; + use crate::database::db_migrations::migrations::migration_7_to_8::Migrate_7_to_8; + use crate::neighborhood::DEFAULT_MIN_HOPS; + use crate::test_utils::database_utils::{ + bring_db_0_back_to_life_and_return_connection, make_external_data, retrieve_config_row, + }; + use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + + #[test] + fn old_version_says_7() { + let subject = Migrate_7_to_8 {}; + + let result = subject.old_version(); + + assert_eq!(result, 7); + } + + #[test] + fn migration_from_7_to_8_is_properly_set() { + let start_at = Migrate_7_to_8 {}.old_version(); + let dir_path = ensure_node_home_directory_exists( + "db_migrations", + "migration_from_7_to_8_is_properly_set", + ); + let db_path = dir_path.join(DATABASE_FILE); + let _ = bring_db_0_back_to_life_and_return_connection(&db_path); + let subject = DbInitializerReal::default(); + { + subject + .initialize_to_version(&dir_path, start_at, DbInitializationConfig::test_default()) + .unwrap(); + } + + let result = subject.initialize_to_version( + &dir_path, + start_at + 1, + DbInitializationConfig::create_or_migrate(make_external_data()), + ); + + let connection = result.unwrap(); + let (mhc_value, mhc_encrypted) = retrieve_config_row(connection.as_ref(), "min_hops"); + assert_eq!(mhc_value, Some(DEFAULT_MIN_HOPS.to_string())); + assert_eq!(mhc_encrypted, false); + let (schv_value, schv_encrypted) = + retrieve_config_row(connection.as_ref(), "schema_version"); + assert_eq!(schv_value, Some("8".to_string())); + assert_eq!(schv_encrypted, false); + } +} diff --git a/node/src/database/db_migrations/migrations/migration_8_to_9.rs b/node/src/database/db_migrations/migrations/migration_8_to_9.rs new file mode 100644 index 000000000..7a6370199 --- /dev/null +++ b/node/src/database/db_migrations/migrations/migration_8_to_9.rs @@ -0,0 +1,70 @@ +use crate::database::db_migrations::db_migrator::DatabaseMigration; +use crate::database::db_migrations::migrator_utils::DBMigDeclarator; + +#[allow(non_camel_case_types)] +pub struct Migrate_8_to_9; + +impl DatabaseMigration for Migrate_8_to_9 { + fn migrate<'a>( + &self, + declaration_utils: Box, + ) -> rusqlite::Result<()> { + declaration_utils.execute_upon_transaction(&[ + &"INSERT INTO config (name, value, encrypted) VALUES ('max_block_count', '', 0)", + ]) + } + + fn old_version(&self) -> usize { + 8 + } +} + +#[cfg(test)] +mod tests { + use crate::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, DATABASE_FILE, + }; + use crate::test_utils::database_utils::{ + bring_db_0_back_to_life_and_return_connection, make_external_data, retrieve_config_row, + }; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + use std::fs::create_dir_all; + + #[test] + fn migration_from_8_to_9_is_properly_set() { + init_test_logging(); + let dir_path = ensure_node_home_directory_exists( + "db_migrations", + "migration_from_8_to_9_is_properly_set", + ); + create_dir_all(&dir_path).unwrap(); + let db_path = dir_path.join(DATABASE_FILE); + let _ = bring_db_0_back_to_life_and_return_connection(&db_path); + let subject = DbInitializerReal::default(); + + let result = subject.initialize_to_version( + &dir_path, + 9, + DbInitializationConfig::create_or_migrate(make_external_data()), + ); + let connection = result.unwrap(); + let (mp_value, mp_encrypted) = retrieve_config_row(connection.as_ref(), "max_block_count"); + let (cs_value, cs_encrypted) = retrieve_config_row(connection.as_ref(), "schema_version"); + assert_eq!(mp_value, Some("".to_string())); + assert_eq!(mp_encrypted, false); + assert_eq!(cs_value, Some("9".to_string())); + assert_eq!(cs_encrypted, false); + TestLogHandler::new().assert_logs_contain_in_order(vec![ + "DbMigrator: Database successfully migrated from version 0 to 1", + "DbMigrator: Database successfully migrated from version 1 to 2", + "DbMigrator: Database successfully migrated from version 2 to 3", + "DbMigrator: Database successfully migrated from version 3 to 4", + "DbMigrator: Database successfully migrated from version 4 to 5", + "DbMigrator: Database successfully migrated from version 5 to 6", + "DbMigrator: Database successfully migrated from version 6 to 7", + "DbMigrator: Database successfully migrated from version 7 to 8", + "DbMigrator: Database successfully migrated from version 8 to 9", + ]); + } +} diff --git a/node/src/database/db_migrations/migrations/mod.rs b/node/src/database/db_migrations/migrations/mod.rs index 69df36eab..68b10ca9b 100644 --- a/node/src/database/db_migrations/migrations/mod.rs +++ b/node/src/database/db_migrations/migrations/mod.rs @@ -7,3 +7,5 @@ pub mod migration_3_to_4; pub mod migration_4_to_5; pub mod migration_5_to_6; pub mod migration_6_to_7; +pub mod migration_7_to_8; +pub mod migration_8_to_9; diff --git a/node/src/database/db_migrations/migrator_utils.rs b/node/src/database/db_migrations/migrator_utils.rs index e9bab401c..422273b06 100644 --- a/node/src/database/db_migrations/migrator_utils.rs +++ b/node/src/database/db_migrations/migrator_utils.rs @@ -5,7 +5,7 @@ use crate::database::db_initializer::ExternalData; use crate::database::db_migrations::db_migrator::{DatabaseMigration, DbMigratorReal}; use masq_lib::constants::CURRENT_SCHEMA_VERSION; use masq_lib::logger::Logger; -use masq_lib::utils::ExpectValue; +use masq_lib::utils::{to_string, ExpectValue}; use rusqlite::{params_from_iter, Error, ToSql, Transaction}; use std::fmt::{Display, Formatter}; @@ -71,7 +71,7 @@ impl<'a> DBMigrationUtilities for DBMigrationUtilitiesReal<'a> { .take() .expectv("owned root transaction") .commit() - .map_err(|e| e.to_string()) + .map_err(to_string) } fn make_mig_declarator<'b>( diff --git a/node/src/database/db_migrations/test_utils.rs b/node/src/database/db_migrations/test_utils.rs index d85fbc41b..11c68c5a7 100644 --- a/node/src/database/db_migrations/test_utils.rs +++ b/node/src/database/db_migrations/test_utils.rs @@ -4,6 +4,7 @@ use crate::database::db_initializer::ExternalData; use crate::database::db_migrations::migrator_utils::{DBMigDeclarator, StatementObject}; use masq_lib::logger::Logger; +use masq_lib::utils::to_string; use rusqlite::Transaction; use std::cell::RefCell; use std::sync::{Arc, Mutex}; @@ -53,7 +54,7 @@ impl DBMigDeclarator for DBMigDeclaratorMock { self.execute_upon_transaction_params.lock().unwrap().push( sql_statements .iter() - .map(|stm_obj| stm_obj.to_string()) + .map(to_string) .collect::>(), ); self.execute_upon_transaction_results.borrow_mut().remove(0) diff --git a/node/src/db_config/config_dao.rs b/node/src/db_config/config_dao.rs index 6c2b9ebab..7d386f151 100644 --- a/node/src/db_config/config_dao.rs +++ b/node/src/db_config/config_dao.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::utils::DaoFactoryReal; +use crate::accountant::db_access_objects::utils::DaoFactoryReal; use crate::database::connection_wrapper::ConnectionWrapper; +use masq_lib::utils::to_string; use rusqlite::types::ToSql; use rusqlite::{Row, Rows, Statement}; @@ -22,7 +23,7 @@ impl ConfigDaoRecord { pub fn new(name: &str, value: Option<&str>, encrypted: bool) -> Self { Self { name: name.to_string(), - value_opt: value.map(|x| x.to_string()), + value_opt: value.map(to_string), encrypted, } } @@ -266,4 +267,16 @@ mod tests { let result = subject.get("schema_version").unwrap(); assert_eq!(result, ConfigDaoRecord::new("schema_version", None, false)); } + + #[test] + fn test_handle_update_execution() { + let result = handle_update_execution(Err(rusqlite::Error::ExecuteReturnedResults)); + + assert_eq!( + result, + Err(ConfigDaoError::DatabaseError( + "Execute returned results - did you mean to call query?".to_string() + )) + ) + } } diff --git a/node/src/db_config/config_dao_null.rs b/node/src/db_config/config_dao_null.rs index 8e1b37818..2cc00e527 100644 --- a/node/src/db_config/config_dao_null.rs +++ b/node/src/db_config/config_dao_null.rs @@ -2,6 +2,7 @@ use crate::database::db_initializer::DbInitializerReal; use crate::db_config::config_dao::{ConfigDao, ConfigDaoError, ConfigDaoRecord}; +use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; use itertools::Itertools; @@ -108,6 +109,10 @@ impl Default for ConfigDaoNull { data.insert("blockchain_service_url".to_string(), (None, false)); data.insert("past_neighbors".to_string(), (None, true)); data.insert("mapping_protocol".to_string(), (None, false)); + data.insert( + "min_hops".to_string(), + (Some(DEFAULT_MIN_HOPS.to_string()), false), + ); data.insert("earning_wallet_address".to_string(), (None, false)); data.insert( "schema_version".to_string(), @@ -125,6 +130,7 @@ impl Default for ConfigDaoNull { "scan_intervals".to_string(), (Some(DEFAULT_SCAN_INTERVALS.to_string()), false), ); + data.insert("max_block_count".to_string(), (None, false)); Self { data } } } @@ -135,6 +141,7 @@ mod tests { use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::DbInitializer; use crate::db_config::config_dao::ConfigDaoReal; + use crate::neighborhood::DEFAULT_MIN_HOPS; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::{DEFAULT_CHAIN, ETH_MAINNET_CONTRACT_CREATION_BLOCK}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; @@ -144,6 +151,7 @@ mod tests { fn get_works() { let subject = ConfigDaoNull::default(); + assert_eq!(subject.get("booga"), Err(ConfigDaoError::NotPresent)); assert_eq!( subject.get("chain_name").unwrap(), ConfigDaoRecord::new( @@ -161,21 +169,16 @@ mod tests { ) ); assert_eq!( - subject.get("gas_price").unwrap(), - ConfigDaoRecord::new("gas_price", Some("1"), false) + subject.get("consuming_wallet_private_key").unwrap(), + ConfigDaoRecord::new("consuming_wallet_private_key", None, true) ); assert_eq!( - subject.get("start_block").unwrap(), - ConfigDaoRecord::new( - "start_block", - Some(&DEFAULT_CHAIN.rec().contract_creation_block.to_string()), - false - ) + subject.get("gas_price").unwrap(), + ConfigDaoRecord::new("gas_price", Some("1"), false) ); - assert_eq!(subject.get("booga"), Err(ConfigDaoError::NotPresent)); assert_eq!( - subject.get("consuming_wallet_private_key").unwrap(), - ConfigDaoRecord::new("consuming_wallet_private_key", None, true) + subject.get("min_hops").unwrap(), + ConfigDaoRecord::new("min_hops", Some(&DEFAULT_MIN_HOPS.to_string()), false) ); assert_eq!( subject.get("payment_thresholds").unwrap(), @@ -197,6 +200,14 @@ mod tests { false ) ); + assert_eq!( + subject.get("start_block").unwrap(), + ConfigDaoRecord::new( + "start_block", + Some(&DEFAULT_CHAIN.rec().contract_creation_block.to_string()), + false + ) + ); } #[test] @@ -263,6 +274,7 @@ mod tests { "schema_version", Some(format!("{}", CURRENT_SCHEMA_VERSION).as_str()), ), + ("max_block_count", None), ] .into_iter() .map(|(k, v_opt)| (k.to_string(), v_opt.map(|v| v.to_string()))) diff --git a/node/src/db_config/mocks.rs b/node/src/db_config/mocks.rs index 60b092c7d..a3083807c 100644 --- a/node/src/db_config/mocks.rs +++ b/node/src/db_config/mocks.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::db_config::config_dao::{ConfigDao, ConfigDaoError, ConfigDaoRecord}; +use masq_lib::utils::to_string; use std::cell::RefCell; use std::sync::{Arc, Mutex}; @@ -27,7 +28,7 @@ impl ConfigDao for ConfigDaoMock { self.set_params .lock() .unwrap() - .push((name.to_string(), value.map(|x| x.to_string()))); + .push((name.to_string(), value.map(to_string))); self.set_results.borrow_mut().remove(0) } } diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index d1b212582..3b44f48d0 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -1,8 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -#[cfg(test)] use crate::arbitrary_id_stamp_in_trait; -use crate::blockchain::bip32::Bip32ECKeyProvider; +use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::bip39::{Bip39, Bip39Error}; use crate::database::connection_wrapper::ConnectionWrapper; use crate::db_config::config_dao::{ConfigDao, ConfigDaoError, ConfigDaoReal, ConfigDaoRecord}; @@ -13,14 +12,12 @@ use crate::db_config::typed_config_layer::{ }; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::PlainData; -use crate::sub_lib::neighborhood::{NodeDescriptor, RatePack}; +use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack}; use crate::sub_lib::wallet::Wallet; -#[cfg(test)] -use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use masq_lib::constants::{HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT}; use masq_lib::shared_schema::{ConfiguratorError, ParamError}; -use masq_lib::utils::AutomapProtocol; use masq_lib::utils::NeighborhoodModeLight; +use masq_lib::utils::{to_string, AutomapProtocol}; use rustc_hex::{FromHex, ToHex}; use std::fmt::Display; use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; @@ -118,6 +115,8 @@ pub trait PersistentConfiguration { &mut self, value: Option, ) -> Result<(), PersistentConfigError>; + fn min_hops(&self) -> Result; + fn set_min_hops(&mut self, value: Hops) -> Result<(), PersistentConfigError>; fn neighborhood_mode(&self) -> Result; fn set_neighborhood_mode( &mut self, @@ -134,6 +133,8 @@ pub trait PersistentConfiguration { ) -> Result<(), PersistentConfigError>; fn start_block(&self) -> Result; fn set_start_block(&mut self, value: u64) -> Result<(), PersistentConfigError>; + fn max_block_count(&self) -> Result, PersistentConfigError>; + fn set_max_block_count(&mut self, value: Option) -> Result<(), PersistentConfigError>; fn set_wallet_info( &mut self, consuming_wallet_private_key: &str, @@ -147,7 +148,6 @@ pub trait PersistentConfiguration { fn scan_intervals(&self) -> Result; fn set_scan_intervals(&mut self, intervals: String) -> Result<(), PersistentConfigError>; - #[cfg(test)] arbitrary_id_stamp_in_trait!(); } @@ -219,13 +219,15 @@ impl PersistentConfiguration for PersistentConfigurationReal { "Database corruption {:?}: consuming private key is not hex, but '{}'", e, key ), - Ok(bytes) => match Bip32ECKeyProvider::from_raw_secret(bytes.as_slice()) { - Err(e) => panic!( - "Database corruption {:?}: consuming private key is invalid", - e - ), - Ok(pair) => Wallet::from(pair), - }, + Ok(bytes) => { + match Bip32EncryptionKeyProvider::from_raw_secret(bytes.as_slice()) { + Err(e) => panic!( + "Database corruption {:?}: consuming private key is invalid", + e + ), + Ok(pair) => Wallet::from(pair), + } + } }) }) } @@ -330,9 +332,20 @@ impl PersistentConfiguration for PersistentConfigurationReal { &mut self, value: Option, ) -> Result<(), PersistentConfigError> { - Ok(self - .dao - .set("mapping_protocol", value.map(|v| v.to_string()))?) + Ok(self.dao.set("mapping_protocol", value.map(to_string))?) + } + + fn min_hops(&self) -> Result { + let result = self.get("min_hops")?.map(|val| Hops::from_str(&val)); + match result { + None => Self::missing_value_panic("min_hops"), + Some(Ok(hops)) => Ok(hops), + Some(Err(msg)) => Err(PersistentConfigError::DatabaseError(msg)), + } + } + + fn set_min_hops(&mut self, value: Hops) -> Result<(), PersistentConfigError> { + Ok(self.dao.set("min_hops", Some(value.to_string()))?) } fn neighborhood_mode(&self) -> Result { @@ -396,6 +409,27 @@ impl PersistentConfiguration for PersistentConfigurationReal { self.simple_set_method("start_block", value) } + fn max_block_count(&self) -> Result, PersistentConfigError> { + match self.get("max_block_count") { + Ok(max_block_count) => match decode_u64(max_block_count) { + Ok(mbc_opt) => Ok(mbc_opt), + Err(TypedConfigLayerError::BadNumberFormat(value)) if value.is_empty() => Ok(None), + Err(e) => Err(PersistentConfigError::from(e)), + }, + Err(e) => Err(PersistentConfigError::from(e)), + } + } + + fn set_max_block_count(&mut self, value: Option) -> Result<(), PersistentConfigError> { + self.simple_set_method( + "max_block_count", + match encode_u64(value) { + Ok(Some(mbc)) => mbc, + _ => "".to_string(), + }, + ) + } + fn set_wallet_info( &mut self, consuming_wallet_private_key: &str, @@ -1043,7 +1077,7 @@ mod tests { let consuming_private_key = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; let consuming_wallet = Wallet::from( - Bip32ECKeyProvider::from_raw_secret( + Bip32EncryptionKeyProvider::from_raw_secret( consuming_private_key .from_hex::>() .unwrap() @@ -1176,7 +1210,7 @@ mod tests { let earning_private_key = ExtendedPrivKey::derive(seed_bytes.as_slice(), derivation_path.as_str()).unwrap(); let earning_key_pair = - Bip32ECKeyProvider::from_raw_secret(&earning_private_key.secret()).unwrap(); + Bip32EncryptionKeyProvider::from_raw_secret(&earning_private_key.secret()).unwrap(); let earning_wallet = Wallet::from(earning_key_pair); let earning_wallet_address = earning_wallet.to_string(); ( @@ -1685,6 +1719,49 @@ mod tests { assert_eq!(*set_params, vec![("mapping_protocol".to_string(), None)]); } + #[test] + fn min_hops_works() { + let config_dao = Box::new(ConfigDaoMock::new().get_result(Ok(ConfigDaoRecord::new( + "min_hops", + Some("3"), + false, + )))); + let subject = PersistentConfigurationReal::new(config_dao); + + let min_hops = subject.min_hops().unwrap(); + + assert_eq!(min_hops, Hops::ThreeHops); + } + + #[test] + fn set_min_hops_to_some() { + let set_params_arc = Arc::new(Mutex::new(vec![])); + let config_dao = ConfigDaoMock::new() + .set_params(&set_params_arc) + .set_result(Ok(())); + let mut subject = PersistentConfigurationReal::new(Box::new(config_dao)); + + let result = subject.set_min_hops(Hops::TwoHops); + + assert_eq!(result, Ok(())); + let set_params = set_params_arc.lock().unwrap(); + assert_eq!( + *set_params, + vec![("min_hops".to_string(), Some("2".to_string()))] + ); + } + + #[test] + #[should_panic(expected = "ever-supplied value missing: min_hops; database is corrupt!")] + fn panics_when_min_hops_value_is_missing() { + let config_dao = Box::new( + ConfigDaoMock::new().get_result(Ok(ConfigDaoRecord::new("min_hops", None, false))), + ); + let subject = PersistentConfigurationReal::new(config_dao); + + let _result = subject.min_hops(); + } + #[test] fn neighborhood_mode_works() { let get_params_arc = Arc::new(Mutex::new(vec![])); @@ -1875,6 +1952,42 @@ mod tests { ); } + #[test] + fn max_block_count_set_method_works_with_some() { + let set_params_arc = Arc::new(Mutex::new(Vec::new())); + let config_dao = ConfigDaoMock::new() + .set_params(&set_params_arc) + .set_result(Ok(())); + let mut subject = PersistentConfigurationReal::new(Box::new(config_dao)); + + let result = subject.set_max_block_count(Some(100_000u64)); + + assert!(result.is_ok()); + let set_params = set_params_arc.lock().unwrap(); + assert_eq!( + *set_params, + vec![("max_block_count".to_string(), Some(100_000u64.to_string()))] + ); + } + + #[test] + fn max_block_count_set_method_works_with_none() { + let set_params_arc = Arc::new(Mutex::new(Vec::new())); + let config_dao = ConfigDaoMock::new() + .set_params(&set_params_arc) + .set_result(Ok(())); + let mut subject = PersistentConfigurationReal::new(Box::new(config_dao)); + + let result = subject.set_max_block_count(None); + + assert!(result.is_ok()); + let set_params = set_params_arc.lock().unwrap(); + assert_eq!( + *set_params, + vec![("max_block_count".to_string(), Some("".to_string()))] + ); + } + #[test] #[should_panic( expected = "ever-supplied value missing: payment_thresholds; database is corrupt!" diff --git a/node/src/dispatcher.rs b/node/src/dispatcher.rs index 583677035..415f12612 100644 --- a/node/src/dispatcher.rs +++ b/node/src/dispatcher.rs @@ -211,7 +211,7 @@ mod tests { use super::*; use crate::actor_system_factory::{ActorFactory, ActorFactoryReal}; use crate::bootstrapper::BootstrapperConfig; - use crate::node_test_utils::make_stream_handler_pool_subs_from; + use crate::node_test_utils::make_stream_handler_pool_subs_from_recorder; use crate::stream_messages::NonClandestineAttributes; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::dispatcher::Endpoint; @@ -426,7 +426,7 @@ mod tests { let mut peer_actors = peer_actors_builder().build(); peer_actors.dispatcher = Dispatcher::make_subs_from(&subject_addr); let stream_handler_pool_subs = - make_stream_handler_pool_subs_from(Some(stream_handler_pool)); + make_stream_handler_pool_subs_from_recorder(&stream_handler_pool.start()); subject_addr .try_send(PoolBindMessage { dispatcher_subs: peer_actors.dispatcher.clone(), @@ -440,14 +440,11 @@ mod tests { System::current().stop_with_code(0); system.run(); - awaiter.await_message_count(1); let recording = recording_arc.lock().unwrap(); - let message = recording.get_record::(0); let actual_endpoint = message.endpoint.clone(); let actual_data = message.data.clone(); - assert_eq!(actual_endpoint, Endpoint::Socket(socket_addr)); assert_eq!(actual_data, data); assert_eq!(recording.len(), 1); diff --git a/node/src/hopper/routing_service.rs b/node/src/hopper/routing_service.rs index 6335975a7..57d2a54d3 100644 --- a/node/src/hopper/routing_service.rs +++ b/node/src/hopper/routing_service.rs @@ -500,7 +500,7 @@ impl RoutingService { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::banned_dao::BAN_CACHE; + use crate::accountant::db_access_objects::banned_dao::BAN_CACHE; use crate::bootstrapper::Bootstrapper; use crate::neighborhood::gossip::{GossipBuilder, Gossip_0v1}; use crate::node_test_utils::check_timestamp; diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 311d0d620..2548a7593 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -937,11 +937,7 @@ impl GossipHandler for StandardGossipHandler { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); - let patch = self.compute_patch( - &agrs, - database.root(), - neighborhood_metadata.min_hops_count as u8, - ); + let patch = self.compute_patch(&agrs, database.root(), neighborhood_metadata.db_patch_size); let filtered_agrs = self.filter_agrs_by_patch(agrs, patch); let mut db_changed = self.identify_and_add_non_introductory_new_nodes( @@ -989,7 +985,7 @@ impl StandardGossipHandler { &self, agrs: &[AccessibleGossipRecord], root_node: &NodeRecord, - min_hops_count: u8, + db_patch_size: u8, ) -> HashSet { let agrs_by_key = agrs .iter() @@ -1001,7 +997,7 @@ impl StandardGossipHandler { &mut patch, root_node.public_key(), &agrs_by_key, - min_hops_count, + db_patch_size, root_node, ); @@ -1334,12 +1330,12 @@ mod tests { use crate::neighborhood::gossip_producer::GossipProducerReal; use crate::neighborhood::node_record::NodeRecord; use crate::sub_lib::cryptde_null::CryptDENull; - use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, Hops}; + use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage}; use crate::sub_lib::utils::time_t_timestamp; use crate::test_utils::neighborhood_test_utils::{ db_from_node, gossip_about_nodes_from_database, linearly_connect_nodes, make_meaningless_db, make_node_record, make_node_record_f, make_node_records, - public_keys_from_node_records, MIN_HOPS_COUNT_FOR_TEST, + public_keys_from_node_records, DB_PATCH_SIZE_FOR_TEST, }; use crate::test_utils::unshared_test_utils::make_cpm_recipient; use crate::test_utils::{assert_contains, main_cryptde, vec_to_set}; @@ -1369,7 +1365,7 @@ mod tests { NeighborhoodMetadata { connection_progress_peers: vec![], cpm_recipient: make_cpm_recipient().0, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + db_patch_size: DB_PATCH_SIZE_FOR_TEST, } } @@ -2385,7 +2381,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let result = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); + let result = subject.compute_patch(&agrs, node_a_db.root(), DB_PATCH_SIZE_FOR_TEST); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2437,7 +2433,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let patch = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); + let patch = subject.compute_patch(&agrs, node_a_db.root(), DB_PATCH_SIZE_FOR_TEST); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2486,7 +2482,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let patch = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); + let patch = subject.compute_patch(&agrs, node_a_db.root(), DB_PATCH_SIZE_FOR_TEST); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2568,17 +2564,17 @@ mod tests { assert_eq!(result, GossipAcceptanceResult::Ignored); } - fn assert_compute_patch(min_hops_count: Hops) { + fn assert_compute_patch(db_patch_size: u8) { let subject = StandardGossipHandler::new(Logger::new("assert_compute_patch")); // one node to finish hops and another node that's outside the patch - let nodes_count = min_hops_count as usize + 2; + let nodes_count = db_patch_size as usize + 2; let nodes = make_node_records(nodes_count as u16); let db = linearly_connect_nodes(&nodes); // gossip is intended for the first node (also root), thereby it's excluded let gossip = gossip_about_nodes_from_database(&db, &nodes[1..]); let agrs: Vec = gossip.try_into().unwrap(); - let result = subject.compute_patch(&agrs, db.root(), min_hops_count as u8); + let result = subject.compute_patch(&agrs, db.root(), db_patch_size); // last node is excluded because it is outside the patch let expected_nodes = &nodes[0..nodes_count - 1]; @@ -2587,13 +2583,11 @@ mod tests { } #[test] - fn patch_can_be_calculated_for_different_hops() { - assert_compute_patch(Hops::OneHop); - assert_compute_patch(Hops::TwoHops); - assert_compute_patch(Hops::ThreeHops); - assert_compute_patch(Hops::FourHops); - assert_compute_patch(Hops::FiveHops); - assert_compute_patch(Hops::SixHops); + fn patch_can_be_calculated_for_realistic_sizes() { + assert_compute_patch(3); + assert_compute_patch(4); + assert_compute_patch(5); + assert_compute_patch(6); } #[test] diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index adbf9da13..4a0ba99ba 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -46,11 +46,11 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData}; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::RemoveNeighborMessage; -use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor}; +use crate::sub_lib::neighborhood::{ConfigurationChange, RemoveNeighborMessage}; +use crate::sub_lib::neighborhood::{ConfigurationChangeMessage, RouteQueryMessage}; use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService}; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; @@ -61,7 +61,6 @@ use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::utils::{ db_connection_launch_panic, handle_ui_crash_request, NODE_MAILBOX_CAPACITY, @@ -79,7 +78,7 @@ use neighborhood_database::NeighborhoodDatabase; use node_record::NodeRecord; pub const CRASH_KEY: &str = "NEIGHBORHOOD"; -pub const DEFAULT_MIN_HOPS_COUNT: Hops = Hops::ThreeHops; +pub const DEFAULT_MIN_HOPS: Hops = Hops::ThreeHops; pub const UNREACHABLE_HOST_PENALTY: i64 = 100_000_000; pub const RESPONSE_UNDESIRABILITY_FACTOR: usize = 1_000; // assumed response length is request * this @@ -94,7 +93,8 @@ pub struct Neighborhood { neighborhood_database: NeighborhoodDatabase, consuming_wallet_opt: Option, mode: NeighborhoodModeLight, - min_hops_count: Hops, + min_hops: Hops, + db_patch_size: u8, next_return_route_id: u32, overall_connection_status: OverallConnectionStatus, chain: Chain, @@ -138,13 +138,35 @@ impl Handler for Neighborhood { } } -//TODO comes across as basically dead code -// I think the idea was to supply the wallet if wallets hadn't been generated until recently during the ongoing Node's run -impl Handler for Neighborhood { +impl Handler for Neighborhood { type Result = (); - fn handle(&mut self, msg: SetConsumingWalletMessage, _ctx: &mut Self::Context) -> Self::Result { - self.consuming_wallet_opt = Some(msg.wallet); + fn handle( + &mut self, + msg: ConfigurationChangeMessage, + _ctx: &mut Self::Context, + ) -> Self::Result { + match msg.change { + ConfigurationChange::UpdateConsumingWallet(new_wallet) => { + self.consuming_wallet_opt = Some(new_wallet) + } + ConfigurationChange::UpdateMinHops(new_min_hops) => { + self.set_min_hops_and_patch_size(new_min_hops); + if self.overall_connection_status.can_make_routes() { + let node_to_ui_recipient = self + .node_to_ui_recipient_opt + .as_ref() + .expect("UI gateway is dead"); + self.overall_connection_status + .update_ocs_stage_and_send_message_to_ui( + OverallConnectionStage::ConnectedToNeighbor, + node_to_ui_recipient, + &self.logger, + ); + } + self.search_for_a_new_route(); + } + } } } @@ -342,6 +364,15 @@ impl Handler for Neighborhood { } } +// GH-728 +impl Handler for Neighborhood { + type Result = (); + + fn handle(&mut self, msg: NewPasswordMessage, _ctx: &mut Self::Context) -> Self::Result { + self.handle_new_password(msg.new_password); + } +} + impl Handler for Neighborhood { type Result = (); @@ -365,14 +396,6 @@ impl Handler for Neighborhood { } } -impl Handler for Neighborhood { - type Result = (); - - fn handle(&mut self, msg: NewPasswordMessage, _ctx: &mut Self::Context) -> Self::Result { - self.handle_new_password(msg.new_password); - } -} - #[derive(Debug, PartialEq, Eq, Clone)] pub struct AccessibleGossipRecord { pub signed_gossip: PlainData, @@ -414,7 +437,8 @@ enum RouteDirection { impl Neighborhood { pub fn new(cryptde: &'static dyn CryptDE, config: &BootstrapperConfig) -> Self { let neighborhood_config = &config.neighborhood_config; - let min_hops_count = neighborhood_config.min_hops_count; + let min_hops = neighborhood_config.min_hops; + let db_patch_size = Neighborhood::calculate_db_patch_size(min_hops); let neighborhood_mode = &neighborhood_config.mode; let mode: NeighborhoodModeLight = neighborhood_mode.into(); let neighbor_configs = neighborhood_mode.neighbor_configs(); @@ -458,7 +482,8 @@ impl Neighborhood { neighborhood_database, consuming_wallet_opt: config.consuming_wallet_opt.clone(), mode, - min_hops_count, + min_hops, + db_patch_size, next_return_route_id: 0, overall_connection_status, chain: config.blockchain_bridge_config.chain, @@ -486,10 +511,10 @@ impl Neighborhood { .recipient::>(), dispatcher_node_query: addr.clone().recipient::(), remove_neighbor: addr.clone().recipient::(), + configuration_change_msg_sub: addr.clone().recipient::(), stream_shutdown_sub: addr.clone().recipient::(), - set_consuming_wallet_sub: addr.clone().recipient::(), from_ui_message_sub: addr.clone().recipient::(), - new_password_sub: addr.clone().recipient::(), + new_password_sub: addr.clone().recipient::(), // GH-728 connection_progress_sub: addr.clone().recipient::(), } } @@ -497,6 +522,7 @@ impl Neighborhood { fn handle_start_message(&mut self) { debug!(self.logger, "Connecting to persistent database"); self.connect_database(); + self.validate_or_replace_min_hops_value(); self.send_debut_gossip_to_all_initial_descriptors(); } @@ -552,6 +578,26 @@ impl Neighborhood { } } + fn validate_or_replace_min_hops_value(&mut self) { + if let Some(persistent_config) = self.persistent_config_opt.as_ref() { + let value_in_db = persistent_config + .min_hops() + .expect("Min Hops value is not initialized inside Database"); + let value_in_neighborhood = self.min_hops; + if value_in_neighborhood != value_in_db { + info!( + self.logger, + "Database with different min hops value detected; \ + currently set: {:?}, found in db: {:?}; changing to {:?}", + value_in_neighborhood, + value_in_db, + value_in_db + ); + self.min_hops = value_in_db; + } + } + } + fn send_debut_gossip_to_all_initial_descriptors(&mut self) { if self.overall_connection_status.is_empty() { info!(self.logger, "Empty. No Nodes to report to; continuing"); @@ -733,7 +779,7 @@ impl Neighborhood { let neighborhood_metadata = NeighborhoodMetadata { connection_progress_peers: self.overall_connection_status.get_peer_addrs(), cpm_recipient, - min_hops_count: self.min_hops_count, + db_patch_size: self.db_patch_size, }; let acceptance_result = self.gossip_acceptor.handle( &mut self.neighborhood_database, @@ -813,9 +859,16 @@ impl Neighborhood { } fn check_connectedness(&mut self) { - if self.overall_connection_status.can_make_routes() { - return; + if !self.overall_connection_status.can_make_routes() { + self.search_for_a_new_route(); } + } + + fn search_for_a_new_route(&mut self) { + debug!( + self.logger, + "Searching for a {}-hop route...", self.min_hops + ); let msg = RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, @@ -826,7 +879,7 @@ impl Neighborhood { if self.handle_route_query_message(msg).is_some() { debug!( &self.logger, - "The connectivity check has found a {}-hop route.", self.min_hops_count as usize + "The connectivity check has found a {}-hop route.", self.min_hops as usize ); self.overall_connection_status .update_ocs_stage_and_send_message_to_ui( @@ -952,7 +1005,7 @@ impl Neighborhood { let over = self.make_route_segment( self.cryptde.public_key(), request_msg.target_key_opt.as_ref(), - self.min_hops_count as usize, + self.min_hops as usize, request_msg.target_component, request_msg.payload_size, RouteDirection::Over, @@ -967,7 +1020,7 @@ impl Neighborhood { let back = self.make_route_segment( over.keys.last().expect("Empty segment"), Some(self.cryptde.public_key()), - self.min_hops_count as usize, + self.min_hops as usize, request_msg .return_component_opt .expect("No return component"), @@ -1527,6 +1580,24 @@ impl Neighborhood { ); } + fn calculate_db_patch_size(min_hops: Hops) -> u8 { + let db_patch_size = if min_hops <= DEFAULT_MIN_HOPS { + DEFAULT_MIN_HOPS + } else { + min_hops + }; + + db_patch_size as u8 + } + + fn set_min_hops_and_patch_size(&mut self, new_min_hops: Hops) { + let (prev_min_hops, prev_db_patch_size) = (self.min_hops, self.db_patch_size); + self.min_hops = new_min_hops; + self.db_patch_size = Neighborhood::calculate_db_patch_size(new_min_hops); + debug!(self.logger, "The value of min_hops ({}-hop -> {}-hop) and db_patch_size ({} -> {}) has been changed", prev_min_hops, self.min_hops, prev_db_patch_size, self.db_patch_size); + } + + // GH-728 fn handle_new_password(&mut self, new_password: String) { self.db_password_opt = Some(new_password); } @@ -1603,7 +1674,8 @@ mod tests { use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; use crate::sub_lib::neighborhood::{ - AskAboutDebutGossipMessage, ExpectedServices, NeighborhoodMode, + AskAboutDebutGossipMessage, ConfigurationChange, ConfigurationChangeMessage, + ExpectedServices, NeighborhoodMode, }; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; @@ -1617,7 +1689,7 @@ mod tests { cryptdes_from_node_records, db_from_node, linearly_connect_nodes, make_global_cryptde_node_record, make_ip, make_node, make_node_descriptor, make_node_record, make_node_record_f, make_node_records, neighborhood_from_nodes, - MIN_HOPS_COUNT_FOR_TEST, + MIN_HOPS_FOR_TEST, }; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::rate_pack; @@ -1659,21 +1731,18 @@ mod tests { #[test] fn constants_have_correct_values() { assert_eq!(CRASH_KEY, "NEIGHBORHOOD"); - assert_eq!(DEFAULT_MIN_HOPS_COUNT, Hops::ThreeHops); + assert_eq!(DEFAULT_MIN_HOPS, Hops::ThreeHops); } #[test] - fn min_hops_count_is_set_inside_neighborhood() { - let min_hops_count = Hops::SixHops; + fn min_hops_and_db_patch_size_are_set_inside_neighborhood() { + let min_hops = Hops::SixHops; let mode = NeighborhoodMode::Standard( NodeAddr::new(&make_ip(1), &[1234, 2345]), vec![make_node_descriptor(make_ip(2))], rate_pack(100), ); - let neighborhood_config = NeighborhoodConfig { - mode, - min_hops_count, - }; + let neighborhood_config = NeighborhoodConfig { mode, min_hops }; let subject = Neighborhood::new( main_cryptde(), @@ -1681,11 +1750,13 @@ mod tests { neighborhood_config, make_wallet("earning"), None, - "min_hops_count_is_set_inside_neighborhood", + "min_hops_is_set_inside_neighborhood", ), ); - assert_eq!(subject.min_hops_count, Hops::SixHops); + let expected_db_patch_size = Neighborhood::calculate_db_patch_size(min_hops); + assert_eq!(subject.min_hops, min_hops); + assert_eq!(subject.db_patch_size, expected_db_patch_size); } #[test] @@ -1702,7 +1773,7 @@ mod tests { "masq://eth-ropsten:AQIDBA@1.2.3.4:1234", )) .unwrap()]), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), None, @@ -1727,7 +1798,7 @@ mod tests { "masq://eth-mainnet:AQIDBA@1.2.3.4:1234", )) .unwrap()]), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), None, @@ -1748,7 +1819,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), None, @@ -1777,7 +1848,7 @@ mod tests { vec![neighbor.node_descriptor(TEST_DEFAULT_CHAIN, cryptde)], DEFAULT_RATE_PACK.clone(), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), None, @@ -1816,13 +1887,16 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), "node_with_zero_hop_config_ignores_start_message", ), ); + subject.persistent_config_opt = Some(Box::new( + PersistentConfigurationMock::new().min_hops_result(Ok(MIN_HOPS_FOR_TEST)), + )); subject.data_directory = data_dir; let addr = subject.start(); let sub = addr.clone().recipient::(); @@ -1865,7 +1939,7 @@ mod tests { ], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -1945,7 +2019,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); @@ -2414,7 +2488,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let mut subject = Neighborhood::new( main_cryptde(), @@ -2491,7 +2565,7 @@ mod tests { ], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), None, @@ -2566,7 +2640,7 @@ mod tests { let system = System::new("route_query_works_when_node_is_set_for_one_hop_and_no_consuming_wallet"); let mut subject = make_standard_subject(); - subject.min_hops_count = Hops::OneHop; + subject.min_hops = Hops::OneHop; subject .neighborhood_database .root_mut() @@ -2648,7 +2722,7 @@ mod tests { ) { let system = System::new("route_query_responds_with_none_when_asked_for_one_hop_round_trip_route_without_consuming_wallet_when_back_route_needs_two_hops"); let mut subject = make_standard_subject(); - subject.min_hops_count = Hops::OneHop; + subject.min_hops = Hops::OneHop; let a = &make_node_record(1234, true); let b = &subject.neighborhood_database.root().clone(); let c = &make_node_record(3456, true); @@ -2680,7 +2754,7 @@ mod tests { ) { let system = System::new("route_query_responds_with_none_when_asked_for_two_hop_one_way_route_without_consuming_wallet"); let mut subject = make_standard_subject(); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); let msg = RouteQueryMessage::data_indefinite_route_request(None, 20000); @@ -2770,7 +2844,7 @@ mod tests { let earning_wallet = make_wallet("earning"); let system = System::new("route_query_messages"); let mut subject = make_standard_subject(); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; subject .neighborhood_database .root_mut() @@ -2890,7 +2964,7 @@ mod tests { let cryptde = main_cryptde(); let system = System::new("return_route_ids_increase"); let (_, _, _, mut subject) = make_o_r_e_subject(); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); @@ -2917,13 +2991,13 @@ mod tests { } #[test] - fn can_update_consuming_wallet() { + fn can_update_consuming_wallet_with_configuration_change_msg() { let cryptde = main_cryptde(); let system = System::new("can_update_consuming_wallet"); let (o, r, e, mut subject) = make_o_r_e_subject(); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; let addr: Addr = subject.start(); - let set_wallet_sub = addr.clone().recipient::(); + let configuration_change_msg_sub = addr.clone().recipient::(); let route_sub = addr.recipient::(); let expected_new_wallet = make_paying_wallet(b"new consuming wallet"); let expected_before_route = Route::round_trip( @@ -2947,9 +3021,11 @@ mod tests { let route_request_1 = route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 1000)); - let _ = set_wallet_sub.try_send(SetConsumingWalletMessage { - wallet: expected_new_wallet, - }); + configuration_change_msg_sub + .try_send(ConfigurationChangeMessage { + change: ConfigurationChange::UpdateConsumingWallet(expected_new_wallet), + }) + .unwrap(); let route_request_2 = route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000)); @@ -2963,6 +3039,145 @@ mod tests { assert_eq!(route_2, expected_after_route); } + #[test] + fn can_calculate_db_patch_size_from_min_hops() { + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::OneHop), 3); + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::TwoHops), 3); + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::ThreeHops), 3); + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::FourHops), 4); + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::FiveHops), 5); + assert_eq!(Neighborhood::calculate_db_patch_size(Hops::SixHops), 6); + } + + #[test] + fn can_set_min_hops_and_db_patch_size() { + init_test_logging(); + let test_name = "can_set_min_hops_and_db_patch_size"; + let initial_min_hops = Hops::TwoHops; + let new_min_hops = Hops::FourHops; + let mut subject = make_standard_subject(); + subject.logger = Logger::new(test_name); + subject.min_hops = initial_min_hops; + + subject.set_min_hops_and_patch_size(new_min_hops); + + let expected_db_patch_size = Neighborhood::calculate_db_patch_size(new_min_hops); + assert_eq!(subject.min_hops, new_min_hops); + assert_eq!(subject.db_patch_size, expected_db_patch_size); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: The value of min_hops (2-hop -> 4-hop) and db_patch_size (3 -> 4) has been changed" + )); + } + + #[test] + fn min_hops_can_be_changed_during_runtime_using_configuration_change_msg() { + init_test_logging(); + let test_name = "min_hops_can_be_changed_during_runtime_using_configuration_change_msg"; + let new_min_hops = Hops::FourHops; + let system = System::new(test_name); + let (ui_gateway, _, ui_gateway_recording) = make_recorder(); + let mut subject = make_standard_subject(); + subject.min_hops = Hops::TwoHops; + subject.logger = Logger::new(test_name); + subject.overall_connection_status.stage = OverallConnectionStage::RouteFound; + let subject_addr = subject.start(); + let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); + subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + + subject_addr + .try_send(ConfigurationChangeMessage { + change: ConfigurationChange::UpdateMinHops(new_min_hops), + }) + .unwrap(); + + subject_addr + .try_send(AssertionsMessage { + assertions: Box::new(move |neighborhood: &mut Neighborhood| { + let expected_db_patch_size = + Neighborhood::calculate_db_patch_size(new_min_hops); + assert_eq!(neighborhood.min_hops, new_min_hops); + assert_eq!(neighborhood.db_patch_size, expected_db_patch_size); + assert_eq!( + neighborhood.overall_connection_status.stage, + OverallConnectionStage::ConnectedToNeighbor + ); + }), + }) + .unwrap(); + System::current().stop(); + system.run(); + let recording = ui_gateway_recording.lock().unwrap(); + let message_opt = recording.get_record_opt::(0); + assert_eq!( + message_opt, + Some(&NodeToUiMessage { + target: MessageTarget::AllClients, + body: UiConnectionChangeBroadcast { + stage: UiConnectionStage::ConnectedToNeighbor + } + .tmb(0), + }) + ); + TestLogHandler::new().assert_logs_contain_in_order(vec![ + &format!( + "DEBUG: {test_name}: The stage of OverallConnectionStatus has been changed \ + from RouteFound to ConnectedToNeighbor. A message to the UI was also sent." + ), + &format!("DEBUG: {test_name}: Searching for a 4-hop route..."), + ]); + } + + #[test] + fn ocs_stage_is_not_changed_in_case_routes_can_not_be_found_before_min_hops_change() { + init_test_logging(); + let test_name = + "ocs_stage_is_not_regressed_in_case_routes_can_not_be_found_before_min_hops_change"; + let new_min_hops = Hops::FourHops; + let system = System::new(test_name); + let (ui_gateway, _, ui_gateway_recording) = make_recorder(); + let mut subject = make_standard_subject(); + subject.min_hops = Hops::TwoHops; + subject.logger = Logger::new(test_name); + subject.overall_connection_status.stage = OverallConnectionStage::NotConnected; + let subject_addr = subject.start(); + let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); + subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + + subject_addr + .try_send(ConfigurationChangeMessage { + change: ConfigurationChange::UpdateMinHops(new_min_hops), + }) + .unwrap(); + + subject_addr + .try_send(AssertionsMessage { + assertions: Box::new(move |neighborhood: &mut Neighborhood| { + let expected_db_patch_size = + Neighborhood::calculate_db_patch_size(new_min_hops); + assert_eq!(neighborhood.min_hops, new_min_hops); + assert_eq!(neighborhood.db_patch_size, expected_db_patch_size); + assert_eq!( + neighborhood.overall_connection_status.stage, + OverallConnectionStage::NotConnected + ); + }), + }) + .unwrap(); + System::current().stop(); + system.run(); + let recording = ui_gateway_recording.lock().unwrap(); + let message_opt = recording.get_record_opt::(0); + assert_eq!(message_opt, None); + let tlh = TestLogHandler::new(); + tlh.exists_no_log_containing(&format!( + "DEBUG: {test_name}: The stage of OverallConnectionStatus has been changed \ + from RouteFound to ConnectedToNeighbor. A message to the UI was also sent." + )); + tlh.exists_log_containing(&format!( + "DEBUG: {test_name}: Searching for a 4-hop route..." + )); + } + #[test] fn compose_route_query_response_returns_an_error_when_route_segment_keys_is_empty() { let mut subject = make_standard_subject(); @@ -3363,7 +3578,7 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -3762,13 +3977,14 @@ mod tests { initial_node_descriptors, rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); let mut subject = Neighborhood::new(main_cryptde(), &bootstrap_config); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); subject.gossip_acceptor = Box::new(gossip_acceptor); + subject.db_patch_size = 6; let mut peer_2_db = db_from_node(&peer_2); peer_2_db.add_node(peer_1.clone()).unwrap(); peer_2_db.add_arbitrary_full_neighbor(peer_2.public_key(), peer_1.public_key()); @@ -3780,6 +3996,8 @@ mod tests { subject.handle_agrs(agrs, peer_2_socket_addr, make_cpm_recipient().0); + let (_, _, _, neighborhood_metadata) = handle_params_arc.lock().unwrap().remove(0); + assert_eq!(neighborhood_metadata.db_patch_size, 6); TestLogHandler::new() .exists_log_containing(&format!("Gossip from {} ignored", peer_2_socket_addr)); } @@ -3794,7 +4012,7 @@ mod tests { let (accountant, _, _) = make_recorder(); let node_to_ui_recipient = ui_gateway.start().recipient::(); let connected_signal = accountant.start().recipient(); - subject.min_hops_count = hops; + subject.min_hops = hops; subject.logger = Logger::new(test_name); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); subject.connected_signal_opt = Some(connected_signal); @@ -3843,10 +4061,10 @@ mod tests { } #[test] - fn neighborhood_logs_when_three_hops_route_can_not_be_made() { + fn neighborhood_logs_when_min_hops_route_can_not_be_made() { init_test_logging(); - let test_name = "neighborhood_logs_when_three_hops_route_can_not_be_made"; - let mut subject: Neighborhood = make_neighborhood_with_linearly_connected_nodes(3); + let test_name = "neighborhood_logs_when_min_hops_route_can_not_be_made"; + let mut subject: Neighborhood = make_neighborhood_with_linearly_connected_nodes(5); let (ui_gateway, _, ui_gateway_arc) = make_recorder(); let (accountant, _, _) = make_recorder(); let node_to_ui_recipient = ui_gateway.start().recipient::(); @@ -3854,6 +4072,7 @@ mod tests { subject.logger = Logger::new(test_name); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); subject.connected_signal_opt = Some(connected_signal); + subject.min_hops = Hops::FiveHops; let system = System::new(test_name); subject.handle_gossip_agrs( @@ -3867,9 +4086,12 @@ mod tests { let ui_recording = ui_gateway_arc.lock().unwrap(); assert_eq!(ui_recording.len(), 0); assert_eq!(subject.overall_connection_status.can_make_routes(), false); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {}: The connectivity check still can't find a good route.", - test_name + let tlh = TestLogHandler::new(); + tlh.exists_log_containing(&format!( + "DEBUG: {test_name}: The connectivity check still can't find a good route.", + )); + tlh.exists_no_log_containing(&format!( + "DEBUG: {test_name}: The connectivity check has found a 5-hop route." )); } @@ -4485,7 +4707,7 @@ mod tests { vec![], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, this_node_inside.earning_wallet(), None, @@ -4548,13 +4770,16 @@ mod tests { vec![debut_target.clone()], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, NodeRecord::earning_wallet_from_key(&cryptde.public_key()), NodeRecord::consuming_wallet_from_key(&cryptde.public_key()), "node_gossips_to_neighbors_on_startup", ), ); + subject.persistent_config_opt = Some(Box::new( + PersistentConfigurationMock::new().min_hops_result(Ok(MIN_HOPS_FOR_TEST)), + )); subject.data_directory = data_dir; subject.logger = Logger::new("node_gossips_to_neighbors_on_startup"); let this_node = subject.neighborhood_database.root().clone(); @@ -4587,6 +4812,96 @@ mod tests { )); } + #[test] + fn node_validates_min_hops_value_from_persistent_configuration() { + let test_name = "node_validates_min_hops_value_from_persistent_configuration"; + let min_hops_in_neighborhood = Hops::SixHops; + let min_hops_in_persistent_configuration = min_hops_in_neighborhood; + let mut subject = Neighborhood::new( + main_cryptde(), + &bc_from_nc_plus( + NeighborhoodConfig { + mode: NeighborhoodMode::Standard( + NodeAddr::new(&make_ip(0), &[1234]), + vec![make_node_descriptor(make_ip(1))], + rate_pack(100), + ), + min_hops: min_hops_in_neighborhood, + }, + make_wallet("earning"), + None, + test_name, + ), + ); + subject.persistent_config_opt = Some(Box::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(min_hops_in_persistent_configuration)), + )); + let system = System::new(test_name); + let addr: Addr = subject.start(); + let peer_actors = peer_actors_builder().build(); + addr.try_send(BindMessage { peer_actors }).unwrap(); + + addr.try_send(StartMessage {}).unwrap(); + + addr.try_send(AssertionsMessage { + assertions: Box::new(move |neighborhood: &mut Neighborhood| { + assert_eq!(neighborhood.min_hops, min_hops_in_persistent_configuration); + }), + }) + .unwrap(); + System::current().stop(); + system.run(); + } + + #[test] + fn neighborhood_picks_min_hops_value_from_db_if_it_is_different_from_that_in_neighborhood() { + init_test_logging(); + let test_name = "neighborhood_picks_min_hops_value_from_db_if_it_is_different_from_that_in_neighborhood"; + let min_hops_in_neighborhood = Hops::SixHops; + let min_hops_in_db = Hops::TwoHops; + let mut subject = Neighborhood::new( + main_cryptde(), + &bc_from_nc_plus( + NeighborhoodConfig { + mode: NeighborhoodMode::Standard( + NodeAddr::new(&make_ip(0), &[1234]), + vec![make_node_descriptor(make_ip(1))], + rate_pack(100), + ), + min_hops: min_hops_in_neighborhood, + }, + make_wallet("earning"), + None, + test_name, + ), + ); + subject.logger = Logger::new(test_name); + subject.persistent_config_opt = Some(Box::new( + PersistentConfigurationMock::new().min_hops_result(Ok(min_hops_in_db)), + )); + let system = System::new(test_name); + let addr: Addr = subject.start(); + let peer_actors = peer_actors_builder().build(); + addr.try_send(BindMessage { peer_actors }).unwrap(); + + addr.try_send(StartMessage {}).unwrap(); + + let assertions_msg = AssertionsMessage { + assertions: Box::new(move |neighborhood: &mut Neighborhood| { + assert_eq!(neighborhood.min_hops, min_hops_in_db) + }), + }; + addr.try_send(assertions_msg).unwrap(); + System::current().stop(); + system.run(); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {test_name}: Database with different min hops value detected; \ + currently set: {:?}, found in db: {:?}; changing to {:?}", + min_hops_in_neighborhood, min_hops_in_db, min_hops_in_db + )); + } + /* Database, where we'll fail to make a three-hop route to C after removing A: @@ -4744,7 +5059,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -4806,7 +5121,7 @@ mod tests { vec![node_record_to_neighbor_config(&one_neighbor)], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -4873,7 +5188,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -4937,7 +5252,7 @@ mod tests { ))], rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, node_record.earning_wallet(), None, @@ -4979,12 +5294,12 @@ mod tests { #[test] fn make_round_trip_route_returns_error_when_no_non_next_door_neighbor_found() { // Make a triangle of Nodes - let min_hops_count = Hops::TwoHops; + let min_hops = Hops::TwoHops; let one_next_door_neighbor = make_node_record(3333, true); let another_next_door_neighbor = make_node_record(4444, true); let subject_node = make_global_cryptde_node_record(5555, true); // 9e7p7un06eHs6frl5A let mut subject = neighborhood_from_nodes(&subject_node, Some(&one_next_door_neighbor)); - subject.min_hops_count = min_hops_count; + subject.min_hops = min_hops; subject .neighborhood_database @@ -5019,7 +5334,7 @@ mod tests { assert_eq!( Err(format!( "Couldn't find any routes: at least {}-hop from {} to ProxyClient at Unknown", - min_hops_count as usize, + min_hops as usize, main_cryptde().public_key() )), result @@ -5033,7 +5348,7 @@ mod tests { let subject_node = make_global_cryptde_node_record(666, true); // 9e7p7un06eHs6frl5A let mut subject = neighborhood_from_nodes(&subject_node, Some(&next_door_neighbor)); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; subject .neighborhood_database @@ -5096,15 +5411,15 @@ mod tests { assert_eq!(expected_public_keys, actual_keys); } - fn assert_route_query_message(min_hops_count: Hops) { - let hops = min_hops_count as usize; + fn assert_route_query_message(min_hops: Hops) { + let hops = min_hops as usize; let nodes_count = hops + 1; let root_node = make_global_cryptde_node_record(4242, true); let mut nodes = make_node_records(nodes_count as u16); nodes[0] = root_node; let db = linearly_connect_nodes(&nodes); let mut subject = neighborhood_from_nodes(db.root(), nodes.get(1)); - subject.min_hops_count = min_hops_count; + subject.min_hops = min_hops; subject.neighborhood_database = db; let result = subject.make_round_trip_route(RouteQueryMessage { @@ -5182,7 +5497,7 @@ mod tests { fn check_fee_preference(payload_size: usize, a_not_b: bool) { let mut subject = make_standard_subject(); - subject.min_hops_count = Hops::TwoHops; + subject.min_hops = Hops::TwoHops; let db = &mut subject.neighborhood_database; let o = &db.root().public_key().clone(); let a = &db.add_node(make_node_record(2345, true)).unwrap(); @@ -5449,7 +5764,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, make_wallet("earning"), None, @@ -5569,6 +5884,7 @@ mod tests { let peer_actors = peer_actors_builder().build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + // GH-728 subject_addr .try_send(NewPasswordMessage { new_password: "borkety-bork".to_string(), @@ -5818,7 +6134,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, test_name); @@ -5842,7 +6158,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![make_node_descriptor(make_ip(1))]), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, make_wallet("earning"), None, diff --git a/node/src/neighborhood/neighborhood_database.rs b/node/src/neighborhood/neighborhood_database.rs index 79a4d1d1f..ceabe133a 100644 --- a/node/src/neighborhood/neighborhood_database.rs +++ b/node/src/neighborhood/neighborhood_database.rs @@ -161,7 +161,10 @@ impl NeighborhoodDatabase { Err(NodeRecordError::SelfNeighborAttempt(key)) => { Err(NeighborhoodDatabaseError::SelfNeighborAttempt(key)) } - Ok(_) => Ok(true), + Ok(_) => { + node.metadata.last_update = time_t_timestamp(); + Ok(true) + } }, None => Err(NodeKeyNotFound(node_key.clone())), } @@ -662,10 +665,15 @@ mod tests { &CryptDENull::from(this_node.public_key(), TEST_DEFAULT_CHAIN), ); subject.add_node(other_node.clone()).unwrap(); + subject.root_mut().metadata.last_update = time_t_timestamp() - 2; + let before = time_t_timestamp(); let result = subject.add_half_neighbor(other_node.public_key()); + let after = time_t_timestamp(); assert_eq!(Ok(true), result, "add_arbitrary_neighbor done goofed"); + assert!(before <= subject.root().metadata.last_update); + assert!(subject.root().metadata.last_update <= after); } #[test] diff --git a/node/src/neighborhood/overall_connection_status.rs b/node/src/neighborhood/overall_connection_status.rs index 04358f076..18e5b2a13 100644 --- a/node/src/neighborhood/overall_connection_status.rs +++ b/node/src/neighborhood/overall_connection_status.rs @@ -266,9 +266,8 @@ impl OverallConnectionStatus { node_to_ui_recipient: &Recipient, logger: &Logger, ) { - // TODO: Modify this fn when you're implementing the regressing transitions let prev_stage = self.stage; - if new_stage as usize > prev_stage as usize { + if new_stage != prev_stage { self.stage = new_stage; OverallConnectionStatus::send_message_to_ui(self.stage.into(), node_to_ui_recipient); debug!( @@ -909,9 +908,9 @@ mod tests { } #[test] - fn doesn_t_send_message_to_the_ui_in_case_stage_hasn_t_updated() { + fn does_not_send_message_to_the_ui_in_case_the_stage_has_not_updated() { init_test_logging(); - let test_name = "doesn_t_send_message_to_the_ui_in_case_stage_hasn_t_updated"; + let test_name = "does_not_send_message_to_the_ui_in_case_the_stage_has_not_updated"; let initial_stage = OverallConnectionStage::ConnectedToNeighbor; let new_stage = initial_stage; @@ -928,21 +927,29 @@ mod tests { } #[test] - fn doesn_t_send_a_message_to_ui_in_case_connection_drops_from_three_hops_to_connected_to_neighbor( - ) { + fn sends_a_message_to_ui_in_case_connection_drops_from_three_hops_to_connected_to_neighbor() { init_test_logging(); - let test_name = "doesn_t_send_a_message_to_ui_in_case_connection_drops_from_three_hops_to_connected_to_neighbor"; + let test_name = "sends_a_message_to_ui_in_case_connection_drops_from_three_hops_to_connected_to_neighbor"; let initial_stage = OverallConnectionStage::RouteFound; let new_stage = OverallConnectionStage::ConnectedToNeighbor; let (stage, message_opt) = assert_stage_and_node_to_ui_message(initial_stage, new_stage, test_name); - assert_eq!(stage, initial_stage); - assert_eq!(message_opt, None); + assert_eq!(stage, new_stage); + assert_eq!( + message_opt, + Some(NodeToUiMessage { + target: MessageTarget::AllClients, + body: UiConnectionChangeBroadcast { + stage: new_stage.into() + } + .tmb(0) + }) + ); TestLogHandler::new().exists_log_containing(&format!( - "TRACE: {}: There was an attempt to update the stage of OverallConnectionStatus \ - from {:?} to {:?}. The request has been discarded.", + "DEBUG: {}: The stage of OverallConnectionStatus has been changed \ + from {:?} to {:?}. A message to the UI was also sent.", test_name, initial_stage, new_stage )); } diff --git a/node/src/node_configurator/configurator.rs b/node/src/node_configurator/configurator.rs index 7a9da5f62..f319f8b10 100644 --- a/node/src/node_configurator/configurator.rs +++ b/node/src/node_configurator/configurator.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019-2021, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use std::path::PathBuf; +use std::str::FromStr; use actix::{Actor, Context, Handler, Recipient}; @@ -17,7 +18,7 @@ use masq_lib::ui_gateway::{ MessageBody, MessagePath, MessageTarget, NodeFromUiMessage, NodeToUiMessage, }; -use crate::blockchain::bip32::Bip32ECKeyProvider; +use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::bip39::Bip39; use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; @@ -26,6 +27,8 @@ use crate::db_config::persistent_configuration::{ PersistentConfigError, PersistentConfiguration, PersistentConfigurationReal, }; use crate::sub_lib::configurator::NewPasswordMessage; +use crate::sub_lib::neighborhood::ConfigurationChange::UpdateMinHops; +use crate::sub_lib::neighborhood::{ConfigurationChangeMessage, Hops}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::utils::{db_connection_launch_panic, handle_ui_crash_request}; use crate::sub_lib::wallet::Wallet; @@ -37,7 +40,7 @@ use masq_lib::constants::{ UNKNOWN_ERROR, UNRECOGNIZED_MNEMONIC_LANGUAGE_ERROR, UNRECOGNIZED_PARAMETER, }; use masq_lib::logger::Logger; -use masq_lib::utils::derivation_path; +use masq_lib::utils::{derivation_path, to_string}; use rustc_hex::{FromHex, ToHex}; use tiny_hderive::bip32::ExtendedPrivKey; @@ -45,8 +48,9 @@ pub const CRASH_KEY: &str = "CONFIGURATOR"; pub struct Configurator { persistent_config: Box, - node_to_ui_sub: Option>, - new_password_subs: Option>>, + new_password_subs: Option>>, // GH-728 + node_to_ui_sub_opt: Option>, + configuration_change_msg_sub_opt: Option>, crashable: bool, logger: Logger, } @@ -59,8 +63,10 @@ impl Handler for Configurator { type Result = (); fn handle(&mut self, msg: BindMessage, _ctx: &mut Self::Context) -> Self::Result { - self.node_to_ui_sub = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub.clone()); - self.new_password_subs = Some(vec![msg.peer_actors.neighborhood.new_password_sub]) + self.node_to_ui_sub_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub.clone()); + self.new_password_subs = Some(vec![msg.peer_actors.neighborhood.new_password_sub]); // GH-728 + self.configuration_change_msg_sub_opt = + Some(msg.peer_actors.neighborhood.configuration_change_msg_sub); } } @@ -107,8 +113,9 @@ impl Configurator { Box::new(PersistentConfigurationReal::new(Box::new(config_dao))); Configurator { persistent_config, - node_to_ui_sub: None, - new_password_subs: None, + new_password_subs: None, // GH-728 + node_to_ui_sub_opt: None, + configuration_change_msg_sub_opt: None, crashable, logger: Logger::new("Configurator"), } @@ -431,11 +438,7 @@ impl Configurator { let mnemonic = Bip39::mnemonic(mnemonic_type, language); let mnemonic_passphrase = Self::make_passphrase(passphrase_opt); let seed = Bip39::seed(&mnemonic, &mnemonic_passphrase); - let phrase_words: Vec = mnemonic - .into_phrase() - .split(' ') - .map(|w| w.to_string()) - .collect(); + let phrase_words: Vec = mnemonic.into_phrase().split(' ').map(to_string).collect(); Ok((seed, phrase_words)) } @@ -478,7 +481,7 @@ impl Configurator { } fn generate_wallet(seed: &Seed, derivation_path: &str) -> Result { - match Bip32ECKeyProvider::try_from((seed.as_bytes(), derivation_path)) { + match Bip32EncryptionKeyProvider::try_from((seed.as_bytes(), derivation_path)) { Err(e) => Err(( DERIVATION_PATH_ERROR, format!("Bad derivation-path syntax: {}: {}", e, derivation_path), @@ -547,12 +550,19 @@ impl Configurator { "earningWalletAddressOpt", )?; let start_block = Self::value_required(persistent_config.start_block(), "startBlock")?; + let max_block_count_opt = match persistent_config.max_block_count() { + Ok(value) => value, + Err(e) => panic!( + "Database corruption: Could not read max block count: {:?}", + e + ), + }; let neighborhood_mode = Self::value_required(persistent_config.neighborhood_mode(), "neighborhoodMode")? .to_string(); let port_mapping_protocol_opt = Self::value_not_required(persistent_config.mapping_protocol(), "portMappingProtocol")? - .map(|p| p.to_string()); + .map(to_string); let (consuming_wallet_private_key_opt, consuming_wallet_address_opt, past_neighbors) = match good_password_opt { Some(password) => { @@ -563,7 +573,7 @@ impl Configurator { Ok(bytes) => bytes, Err(e) => panic! ("Database corruption: consuming wallet private key '{}' cannot be converted from hexadecimal: {:?}", private_key_hex, e), }; - let key_pair = match Bip32ECKeyProvider::from_raw_secret(private_key_bytes.as_slice()) { + let key_pair = match Bip32EncryptionKeyProvider::from_raw_secret(private_key_bytes.as_slice()) { Ok(pair) => pair, Err(e) => panic!("Database corruption: consuming wallet private key '{}' is invalid: {:?}", private_key_hex, e), }; @@ -616,6 +626,7 @@ impl Configurator { clandestine_port, chain_name, gas_price, + max_block_count_opt, neighborhood_mode, consuming_wallet_private_key_opt, consuming_wallet_address_opt, @@ -690,33 +701,57 @@ impl Configurator { msg: UiSetConfigurationRequest, context_id: u64, ) -> MessageBody { + let configuration_change_msg_sub_opt = self.configuration_change_msg_sub_opt.clone(); + let logger = &self.logger; + debug!( + logger, + "A request from UI received: {:?} from context id: {}", msg, context_id + ); match Self::unfriendly_handle_set_configuration( msg, context_id, &mut self.persistent_config, + configuration_change_msg_sub_opt, + logger, ) { Ok(message_body) => message_body, - Err((code, msg)) => MessageBody { - opcode: "setConfiguration".to_string(), - path: MessagePath::Conversation(context_id), - payload: Err((code, msg)), - }, + Err((code, msg)) => { + error!( + logger, + "{}", + format!("The UiSetConfigurationRequest failed with an error {code}: {msg}") + ); + MessageBody { + opcode: "setConfiguration".to_string(), + path: MessagePath::Conversation(context_id), + payload: Err((code, msg)), + } + } } } fn unfriendly_handle_set_configuration( msg: UiSetConfigurationRequest, context_id: u64, - persist_config: &mut Box, + persistent_config: &mut Box, + configuration_change_msg_sub_opt: Option>, + logger: &Logger, ) -> Result { let password: Option = None; //prepared for an upgrade with parameters requiring the password match password { None => { if "gas-price" == &msg.name { - Self::set_gas_price(msg.value, persist_config)?; + Self::set_gas_price(msg.value, persistent_config)?; } else if "start-block" == &msg.name { - Self::set_start_block(msg.value, persist_config)?; + Self::set_start_block(msg.value, persistent_config)?; + } else if "min-hops" == &msg.name { + Self::set_min_hops( + msg.value, + persistent_config, + configuration_change_msg_sub_opt, + logger, + )?; } else { return Err(( UNRECOGNIZED_PARAMETER, @@ -746,6 +781,38 @@ impl Configurator { } } + fn set_min_hops( + string_number: String, + config: &mut Box, + configuration_change_msg_sub_opt: Option>, + logger: &Logger, + ) -> Result<(), (u64, String)> { + let min_hops = match Hops::from_str(&string_number) { + Ok(min_hops) => min_hops, + Err(e) => { + return Err((NON_PARSABLE_VALUE, format!("min hops: {:?}", e))); + } + }; + match config.set_min_hops(min_hops) { + Ok(_) => { + debug!( + logger, + "The value of min-hops has been changed to {}-hop inside the database", + min_hops + ); + configuration_change_msg_sub_opt + .as_ref() + .expect("Configurator is unbound") + .try_send(ConfigurationChangeMessage { + change: UpdateMinHops(min_hops), + }) + .expect("Neighborhood is dead"); + Ok(()) + } + Err(e) => Err((CONFIGURATOR_WRITE_ERROR, format!("min hops: {:?}", e))), + } + } + fn set_start_block( string_number: String, config: &mut Box, @@ -762,7 +829,7 @@ impl Configurator { fn send_to_ui_gateway(&self, target: MessageTarget, body: MessageBody) { let msg = NodeToUiMessage { target, body }; - self.node_to_ui_sub + self.node_to_ui_sub_opt .as_ref() .expect("Configurator is unbound") .try_send(msg) @@ -770,6 +837,7 @@ impl Configurator { } fn send_password_changes(&self, new_password: String) { + // GH-728 let msg = NewPasswordMessage { new_password }; self.new_password_subs .as_ref() @@ -827,14 +895,15 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use super::*; - use crate::blockchain::bip32::Bip32ECKeyProvider; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::bip39::Bip39; use crate::blockchain::test_utils::make_meaningless_phrase_words; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; + use crate::sub_lib::configurator::NewPasswordMessage; use crate::sub_lib::cryptde::PublicKey as PK; use crate::sub_lib::cryptde::{CryptDE, PlainData}; - use crate::sub_lib::neighborhood::{NodeDescriptor, RatePack}; + use crate::sub_lib::neighborhood::{ConfigurationChange, NodeDescriptor, RatePack}; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::{ @@ -865,11 +934,14 @@ mod tests { ))); let (recorder, _, _) = make_recorder(); let recorder_addr = recorder.start(); + let (neighborhood, _, _) = make_recorder(); + let neighborhood_addr = neighborhood.start(); let mut subject = Configurator::new(data_dir, false); - subject.node_to_ui_sub = Some(recorder_addr.recipient()); - subject.new_password_subs = Some(vec![]); + subject.node_to_ui_sub_opt = Some(recorder_addr.recipient()); + subject.configuration_change_msg_sub_opt = Some(neighborhood_addr.recipient()); + subject.new_password_subs = Some(vec![]); // GH-728 let _ = subject.handle_change_password( UiChangePasswordRequest { old_password_opt: None, @@ -1033,6 +1105,7 @@ mod tests { } ); let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); + // GH-728 assert_eq!( neighborhood_recording.get_record::(0), &NewPasswordMessage { @@ -1299,7 +1372,7 @@ mod tests { ExtendedPrivKey::derive(seed.as_slice(), derivation_path(0, 4).as_str()).unwrap(); let consuming_private_key = consuming_epk.secret().to_hex::().to_uppercase(); let consuming_wallet = Wallet::from( - Bip32ECKeyProvider::try_from((seed.as_slice(), derivation_path(0, 4).as_str())) + Bip32EncryptionKeyProvider::try_from((seed.as_slice(), derivation_path(0, 4).as_str())) .unwrap(), ); assert_eq!( @@ -1315,7 +1388,7 @@ mod tests { .unwrap() ); let earning_wallet = Wallet::from( - Bip32ECKeyProvider::try_from((seed.as_slice(), derivation_path(0, 5).as_str())) + Bip32EncryptionKeyProvider::try_from((seed.as_slice(), derivation_path(0, 5).as_str())) .unwrap(), ); assert_eq!( @@ -1693,7 +1766,7 @@ mod tests { ) .unwrap(); let earning_keypair = - Bip32ECKeyProvider::from_raw_secret(&earning_private_key.secret()).unwrap(); + Bip32EncryptionKeyProvider::from_raw_secret(&earning_private_key.secret()).unwrap(); let earning_wallet = Wallet::from(earning_keypair); let set_wallet_info_params = set_wallet_info_params_arc.lock().unwrap(); assert_eq!( @@ -1934,24 +2007,28 @@ mod tests { #[test] fn handle_set_configuration_works() { + init_test_logging(); + let test_name = "handle_set_configuration_works"; let set_start_block_params_arc = Arc::new(Mutex::new(vec![])); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); let persistent_config = PersistentConfigurationMock::new() .set_start_block_params(&set_start_block_params_arc) .set_start_block_result(Ok(())); - let subject = make_subject(Some(persistent_config)); + let mut subject = make_subject(Some(persistent_config)); + subject.logger = Logger::new(test_name); let subject_addr = subject.start(); let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + let msg = UiSetConfigurationRequest { + name: "start-block".to_string(), + value: "166666".to_string(), + }; + let context_id = 4444; subject_addr .try_send(NodeFromUiMessage { client_id: 1234, - body: UiSetConfigurationRequest { - name: "start-block".to_string(), - value: "166666".to_string(), - } - .tmb(4444), + body: msg.clone().tmb(context_id), }) .unwrap(); @@ -1964,6 +2041,10 @@ mod tests { assert_eq!(context_id, 4444); let check_start_block_params = set_start_block_params_arc.lock().unwrap(); assert_eq!(*check_start_block_params, vec![166666]); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {}: A request from UI received: {:?} from context id: {}", + test_name, msg, context_id + )); } #[test] @@ -2101,6 +2182,135 @@ mod tests { ); } + #[test] + fn handle_set_configuration_works_for_min_hops() { + init_test_logging(); + let test_name = "handle_set_configuration_works_for_min_hops"; + let new_min_hops = Hops::SixHops; + let set_min_hops_params_arc = Arc::new(Mutex::new(vec![])); + let persistent_config = PersistentConfigurationMock::new() + .set_min_hops_params(&set_min_hops_params_arc) + .set_min_hops_result(Ok(())); + let system = System::new("handle_set_configuration_works_for_min_hops"); + let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); + let neighborhood_addr = neighborhood.start(); + let mut subject = make_subject(Some(persistent_config)); + subject.logger = Logger::new(test_name); + subject.configuration_change_msg_sub_opt = + Some(neighborhood_addr.recipient::()); + + let result = subject.handle_set_configuration( + UiSetConfigurationRequest { + name: "min-hops".to_string(), + value: new_min_hops.to_string(), + }, + 4000, + ); + + System::current().stop(); + system.run(); + let neighborhood_recording = neighborhood_recording_arc.lock().unwrap(); + let message_to_neighborhood = + neighborhood_recording.get_record::(0); + let set_min_hops_params = set_min_hops_params_arc.lock().unwrap(); + let min_hops_in_db = set_min_hops_params.get(0).unwrap(); + assert_eq!( + result, + MessageBody { + opcode: "setConfiguration".to_string(), + path: MessagePath::Conversation(4000), + payload: Ok(r#"{}"#.to_string()) + } + ); + assert_eq!( + message_to_neighborhood, + &ConfigurationChangeMessage { + change: ConfigurationChange::UpdateMinHops(new_min_hops) + } + ); + assert_eq!(*min_hops_in_db, new_min_hops); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: The value of min-hops has been changed to {new_min_hops}-hop inside the database" + )); + } + + #[test] + fn handle_set_configuration_throws_err_for_invalid_min_hops() { + init_test_logging(); + let test_name = "handle_set_configuration_throws_err_for_invalid_min_hops"; + let mut subject = make_subject(None); + subject.logger = Logger::new(test_name); + + let result = subject.handle_set_configuration( + UiSetConfigurationRequest { + name: "min-hops".to_string(), + value: "600".to_string(), + }, + 4000, + ); + + assert_eq!( + result, + MessageBody { + opcode: "setConfiguration".to_string(), + path: MessagePath::Conversation(4000), + payload: Err(( + NON_PARSABLE_VALUE, + "min hops: \"Invalid value for min hops provided\"".to_string() + )) + } + ); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: The UiSetConfigurationRequest failed with an error \ + 281474976710668: min hops: \"Invalid value for min hops provided\"" + )); + } + + #[test] + fn handle_set_configuration_handles_failure_on_min_hops_database_issue() { + init_test_logging(); + let test_name = "handle_set_configuration_handles_failure_on_min_hops_database_issue"; + let persistent_config = PersistentConfigurationMock::new() + .set_min_hops_result(Err(PersistentConfigError::TransactionError)); + let system = + System::new("handle_set_configuration_handles_failure_on_min_hops_database_issue"); + let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); + let configuration_change_msg_sub = neighborhood + .start() + .recipient::(); + let mut subject = make_subject(Some(persistent_config)); + subject.configuration_change_msg_sub_opt = Some(configuration_change_msg_sub); + subject.logger = Logger::new(test_name); + + let result = subject.handle_set_configuration( + UiSetConfigurationRequest { + name: "min-hops".to_string(), + value: "4".to_string(), + }, + 4000, + ); + + System::current().stop(); + system.run(); + let recording = neighborhood_recording_arc.lock().unwrap(); + assert!(recording.is_empty()); + assert_eq!( + result, + MessageBody { + opcode: "setConfiguration".to_string(), + path: MessagePath::Conversation(4000), + payload: Err(( + CONFIGURATOR_WRITE_ERROR, + "min hops: TransactionError".to_string() + )) + } + ); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: The UiSetConfigurationRequest failed with an error \ + 281474976710658: min hops: TransactionError" + )); + } + #[test] fn handle_set_configuration_complains_about_unexpected_parameter() { let persistent_config = PersistentConfigurationMock::new(); @@ -2211,6 +2421,7 @@ mod tests { .gas_price_result(Ok(2345)) .consuming_wallet_private_key_result(Ok(Some(consuming_wallet_private_key))) .mapping_protocol_result(Ok(Some(AutomapProtocol::Igdp))) + .max_block_count_result(Ok(Some(100000))) .neighborhood_mode_result(Ok(NeighborhoodModeLight::Standard)) .past_neighbors_result(Ok(Some(vec![node_descriptor.clone()]))) .earning_wallet_address_result(Ok(Some(earning_wallet_address.clone()))) @@ -2236,6 +2447,7 @@ mod tests { clandestine_port: 1234, chain_name: "ropsten".to_string(), gas_price: 2345, + max_block_count_opt: Some(100000), neighborhood_mode: String::from("standard"), consuming_wallet_private_key_opt: None, consuming_wallet_address_opt: None, @@ -2311,7 +2523,7 @@ mod tests { "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF".to_string(); let consuming_wallet_address = format!( "{:?}", - Bip32ECKeyProvider::from_raw_secret( + Bip32EncryptionKeyProvider::from_raw_secret( consuming_wallet_private_key .from_hex::>() .unwrap() @@ -2339,6 +2551,7 @@ mod tests { .consuming_wallet_private_key_params(&consuming_wallet_private_key_params_arc) .consuming_wallet_private_key_result(Ok(Some(consuming_wallet_private_key.clone()))) .mapping_protocol_result(Ok(Some(AutomapProtocol::Igdp))) + .max_block_count_result(Ok(Some(100000))) .neighborhood_mode_result(Ok(NeighborhoodModeLight::ConsumeOnly)) .past_neighbors_params(&past_neighbors_params_arc) .past_neighbors_result(Ok(Some(vec![node_descriptor.clone()]))) @@ -2366,6 +2579,7 @@ mod tests { clandestine_port: 1234, chain_name: "ropsten".to_string(), gas_price: 2345, + max_block_count_opt: Some(100000), neighborhood_mode: String::from("consume-only"), consuming_wallet_private_key_opt: Some(consuming_wallet_private_key), consuming_wallet_address_opt: Some(consuming_wallet_address), @@ -2404,6 +2618,119 @@ mod tests { assert_eq!(*past_neighbors_params, vec!["password".to_string()]) } + #[test] + fn configuration_handles_retrieving_max_block_count_none() { + let persistent_config = PersistentConfigurationMock::new() + .check_password_result(Ok(true)) + .blockchain_service_url_result(Ok(None)) + .current_schema_version_result("3") + .clandestine_port_result(Ok(1234)) + .chain_name_result("ropsten".to_string()) + .gas_price_result(Ok(2345)) + .earning_wallet_address_result(Ok(None)) + .start_block_result(Ok(3456)) + .max_block_count_result(Ok(None)) + .neighborhood_mode_result(Ok(NeighborhoodModeLight::ZeroHop)) + .mapping_protocol_result(Ok(None)) + .consuming_wallet_private_key_result(Ok(None)) + .past_neighbors_result(Ok(None)) + .rate_pack_result(Ok(RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + })) + .scan_intervals_result(Ok(ScanIntervals { + pending_payable_scan_interval: Default::default(), + payable_scan_interval: Default::default(), + receivable_scan_interval: Default::default(), + })) + .payment_thresholds_result(Ok(PaymentThresholds { + debt_threshold_gwei: 0, + maturity_threshold_sec: 0, + payment_grace_period_sec: 0, + permanent_debt_allowed_gwei: 0, + threshold_interval_sec: 0, + unban_below_gwei: 0, + })); + let mut subject = make_subject(Some(persistent_config)); + + let (configuration, context_id) = + UiConfigurationResponse::fmb(subject.handle_configuration( + UiConfigurationRequest { + db_password_opt: Some("password".to_string()), + }, + 4321, + )) + .unwrap(); + + assert_eq!(context_id, 4321); + assert_eq!( + configuration, + UiConfigurationResponse { + blockchain_service_url_opt: None, + current_schema_version: "3".to_string(), + clandestine_port: 1234, + chain_name: "ropsten".to_string(), + gas_price: 2345, + max_block_count_opt: None, + neighborhood_mode: String::from("zero-hop"), + consuming_wallet_private_key_opt: None, + consuming_wallet_address_opt: None, + earning_wallet_address_opt: None, + port_mapping_protocol_opt: None, + past_neighbors: vec![], + payment_thresholds: UiPaymentThresholds { + threshold_interval_sec: 0, + debt_threshold_gwei: 0, + maturity_threshold_sec: 0, + payment_grace_period_sec: 0, + permanent_debt_allowed_gwei: 0, + unban_below_gwei: 0 + }, + rate_pack: UiRatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0 + }, + start_block: 3456, + scan_intervals: UiScanIntervals { + pending_payable_sec: 0, + payable_sec: 0, + receivable_sec: 0 + } + } + ); + } + + #[test] + #[should_panic( + expected = "Database corruption: Could not read max block count: DatabaseError(\"Corruption\")" + )] + fn configuration_panic_on_error_retrieving_max_block_count() { + let persistent_config = PersistentConfigurationMock::new() + .check_password_result(Ok(true)) + .blockchain_service_url_result(Ok(None)) + .current_schema_version_result("3") + .clandestine_port_result(Ok(1234)) + .chain_name_result("ropsten".to_string()) + .gas_price_result(Ok(2345)) + .earning_wallet_address_result(Ok(Some("4a5e43b54c6C56Ebf7".to_string()))) + .start_block_result(Ok(3456)) + .max_block_count_result(Err(PersistentConfigError::DatabaseError( + "Corruption".to_string(), + ))); + let mut subject = make_subject(Some(persistent_config)); + + let _result = subject.handle_configuration( + UiConfigurationRequest { + db_password_opt: Some("password".to_string()), + }, + 4321, + ); + } + #[test] fn configuration_handles_check_password_error() { let persistent_config = PersistentConfigurationMock::new() @@ -2441,6 +2768,7 @@ mod tests { "0x0123456789012345678901234567890123456789".to_string(), ))) .start_block_result(Ok(3456)) + .max_block_count_result(Ok(Some(100000))) .neighborhood_mode_result(Ok(NeighborhoodModeLight::ConsumeOnly)) .mapping_protocol_result(Ok(Some(AutomapProtocol::Igdp))) .consuming_wallet_private_key_result(cwpk); @@ -2547,8 +2875,9 @@ mod tests { fn from(persistent_config: Box) -> Self { Configurator { persistent_config, - node_to_ui_sub: None, - new_password_subs: None, + new_password_subs: None, // GH-728 + node_to_ui_sub_opt: None, + configuration_change_msg_sub_opt: None, crashable: false, logger: Logger::new("Configurator"), } diff --git a/node/src/node_configurator/mod.rs b/node/src/node_configurator/mod.rs index 294854a40..e2baff293 100644 --- a/node/src/node_configurator/mod.rs +++ b/node/src/node_configurator/mod.rs @@ -19,8 +19,9 @@ use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::multi_config::{merge, CommandLineVcl, EnvironmentVcl, MultiConfig, VclArg}; use masq_lib::shared_schema::{ chain_arg, config_file_arg, data_directory_arg, real_user_arg, ConfiguratorError, + DATA_DIRECTORY_HELP, }; -use masq_lib::utils::{localhost, ExpectValue}; +use masq_lib::utils::{add_masq_and_chain_directories, localhost}; use std::net::{SocketAddr, TcpListener}; use std::path::{Path, PathBuf}; @@ -28,15 +29,15 @@ pub trait NodeConfigurator { fn configure(&self, multi_config: &MultiConfig) -> Result; } -pub fn determine_config_file_path( +pub fn determine_fundamentals( dirs_wrapper: &dyn DirsWrapper, app: &App, args: &[String], -) -> Result<(PathBuf, bool), ConfiguratorError> { +) -> Result<(PathBuf, bool, PathBuf, RealUser), ConfiguratorError> { let orientation_schema = App::new("MASQNode") .arg(chain_arg()) .arg(real_user_arg()) - .arg(data_directory_arg()) + .arg(data_directory_arg(DATA_DIRECTORY_HELP)) .arg(config_file_arg()); let orientation_args: Vec> = merge( Box::new(EnvironmentVcl::new(app)), @@ -54,13 +55,22 @@ pub fn determine_config_file_path( .collect(); let orientation_vcl = CommandLineVcl::from(orientation_args); let multi_config = make_new_multi_config(&orientation_schema, vec![Box::new(orientation_vcl)])?; - let config_file_path = value_m!(multi_config, "config-file", PathBuf).expectv("config-file"); + let config_file_path = value_m!(multi_config, "config-file", PathBuf) + .expect("config-file parameter is not properly defaulted by clap"); let user_specified = multi_config.occurrences_of("config-file") > 0; - let (real_user, data_directory_opt, chain) = - real_user_data_directory_opt_and_chain(dirs_wrapper, &multi_config); - let directory = - data_directory_from_context(dirs_wrapper, &real_user, &data_directory_opt, chain); - Ok((directory.join(config_file_path), user_specified)) + let (real_user, data_directory_path, chain) = + real_user_data_directory_path_and_chain(dirs_wrapper, &multi_config); + let data_directory = match data_directory_path { + Some(data_dir) => data_dir, + None => data_directory_from_context(dirs_wrapper, &real_user, chain), + }; + let config_file_path = if config_file_path.is_relative() { + data_directory.join(config_file_path) + } else { + config_file_path + }; + + Ok((config_file_path, user_specified, data_directory, real_user)) } pub fn initialize_database( @@ -83,17 +93,17 @@ pub fn real_user_from_multi_config_or_populate( } } -pub fn real_user_data_directory_opt_and_chain( +pub fn real_user_data_directory_path_and_chain( dirs_wrapper: &dyn DirsWrapper, multi_config: &MultiConfig, ) -> (RealUser, Option, Chain) { let real_user = real_user_from_multi_config_or_populate(multi_config, dirs_wrapper); let chain_name = value_m!(multi_config, "chain", String) .unwrap_or_else(|| DEFAULT_CHAIN.rec().literal_identifier.to_string()); - let data_directory_opt = value_m!(multi_config, "data-directory", PathBuf); + let data_directory_path = value_m!(multi_config, "data-directory", PathBuf); ( real_user, - data_directory_opt, + data_directory_path, Chain::from(chain_name.as_str()), ) } @@ -101,35 +111,23 @@ pub fn real_user_data_directory_opt_and_chain( pub fn data_directory_from_context( dirs_wrapper: &dyn DirsWrapper, real_user: &RealUser, - data_directory_opt: &Option, chain: Chain, ) -> PathBuf { - match data_directory_opt { - Some(data_directory) => data_directory.clone(), - None => { - let right_home_dir = real_user - .home_dir_opt - .as_ref() - .expect("No real-user home directory; specify --real-user") - .to_string_lossy() - .to_string(); - let wrong_home_dir = dirs_wrapper - .home_dir() - .expect("No privileged home directory; specify --data-directory") - .to_string_lossy() - .to_string(); - let wrong_local_data_dir = dirs_wrapper - .data_dir() - .expect("No privileged local data directory; specify --data-directory") - .to_string_lossy() - .to_string(); - let right_local_data_dir = - wrong_local_data_dir.replace(&wrong_home_dir, &right_home_dir); - PathBuf::from(right_local_data_dir) - .join("MASQ") - .join(chain.rec().literal_identifier) - } - } + let right_home_dir = real_user + .home_dir_opt + .as_ref() + .expect("No real-user home directory; specify --real-user"); + let wrong_home_dir = dirs_wrapper + .home_dir() + .expect("No privileged home directory; specify --data-directory"); + let wrong_local_data_dir = dirs_wrapper + .data_dir() + .expect("No privileged local data directory; specify --data-directory"); + let adjusted_local_data_dir: &Path = wrong_local_data_dir + .strip_prefix(wrong_home_dir) + .expect("std lib failed"); + let homedir = right_home_dir.join(adjusted_local_data_dir); + add_masq_and_chain_directories(chain, &homedir) } pub fn port_is_busy(port: u16) -> bool { @@ -162,12 +160,13 @@ mod tests { use crate::node_test_utils::DirsWrapperMock; use crate::test_utils::ArgsBuilder; use masq_lib::test_utils::environment_guard::EnvironmentGuard; + use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::find_free_port; use std::net::{SocketAddr, TcpListener}; fn determine_config_file_path_app() -> App<'static, 'static> { App::new("test") - .arg(data_directory_arg()) + .arg(data_directory_arg(DATA_DIRECTORY_HELP)) .arg(config_file_arg()) } @@ -183,43 +182,44 @@ mod tests { "/nonexistent_home/nonexistent_alice".to_string(), )), ); - let data_dir_opt = None; - let chain_name = "eth-ropsten"; - - let result = data_directory_from_context( - &dirs_wrapper, - &real_user, - &data_dir_opt, - Chain::from(chain_name), - ); + let chain_name = "polygon-mumbai"; + + let result = + data_directory_from_context(&dirs_wrapper, &real_user, Chain::from(chain_name)); assert_eq!( result, PathBuf::from( - "/nonexistent_home/nonexistent_alice/.local/share/MASQ/eth-ropsten".to_string() + "/nonexistent_home/nonexistent_alice/.local/share/MASQ/polygon-mumbai".to_string() ) ) } #[test] fn determine_config_file_path_finds_path_in_args() { + let data_directory = ensure_node_home_directory_exists( + "node_configurator", + "determine_config_file_path_finds_path_in_args", + ); let _guard = EnvironmentGuard::new(); let args = ArgsBuilder::new() .param("--clandestine-port", "2345") - .param("--data-directory", "data-dir") + .param( + "--data-directory", + &data_directory.to_string_lossy().to_string(), + ) .param("--config-file", "booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), ) .unwrap(); - assert_eq!( &format!("{}", config_file_path.parent().unwrap().display()), - "data-dir", + &data_directory.to_string_lossy().to_string(), ); assert_eq!("booga.toml", config_file_path.file_name().unwrap()); assert_eq!(true, user_specified); @@ -227,22 +227,28 @@ mod tests { #[test] fn determine_config_file_path_finds_path_in_environment() { + let data_directory = ensure_node_home_directory_exists( + "node_configurator", + "determine_config_file_path_finds_path_in_environment", + ); let _guard = EnvironmentGuard::new(); let args = ArgsBuilder::new(); let args_vec: Vec = args.into(); - std::env::set_var("MASQ_DATA_DIRECTORY", "data_dir"); + std::env::set_var( + "MASQ_DATA_DIRECTORY", + &data_directory.to_string_lossy().to_string(), + ); std::env::set_var("MASQ_CONFIG_FILE", "booga.toml"); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), ) .unwrap(); - assert_eq!( - "data_dir", - &format!("{}", config_file_path.parent().unwrap().display()) + format!("{}", config_file_path.parent().unwrap().display()), + data_directory.to_string_lossy().to_string(), ); assert_eq!("booga.toml", config_file_path.file_name().unwrap()); assert_eq!(true, user_specified); @@ -257,7 +263,7 @@ mod tests { .param("--config-file", "/tmp/booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -280,7 +286,7 @@ mod tests { .param("--config-file", r"\tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -303,7 +309,7 @@ mod tests { .param("--config-file", r"c:\tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -326,7 +332,7 @@ mod tests { .param("--config-file", r"\\TMP\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -350,7 +356,7 @@ mod tests { .param("--config-file", r"c:tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified) = determine_config_file_path( + let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), diff --git a/node/src/node_configurator/node_configurator_standard.rs b/node/src/node_configurator/node_configurator_standard.rs index 8da663d66..10150b6e1 100644 --- a/node/src/node_configurator/node_configurator_standard.rs +++ b/node/src/node_configurator/node_configurator_standard.rs @@ -7,9 +7,10 @@ use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; use masq_lib::multi_config::MultiConfig; use masq_lib::shared_schema::ConfiguratorError; -use masq_lib::utils::{ExpectValue, NeighborhoodModeLight}; +use masq_lib::utils::NeighborhoodModeLight; use std::net::SocketAddr; use std::net::{IpAddr, Ipv4Addr}; +use std::path::PathBuf; use clap::value_t; use log::LevelFilter; @@ -23,8 +24,7 @@ use crate::node_configurator::unprivileged_parse_args_configuration::{ UnprivilegedParseArgsConfiguration, UnprivilegedParseArgsConfigurationDaoReal, }; use crate::node_configurator::{ - data_directory_from_context, determine_config_file_path, - real_user_data_directory_opt_and_chain, real_user_from_multi_config_or_populate, + data_directory_from_context, determine_fundamentals, real_user_data_directory_path_and_chain, }; use crate::server_initializer::GatheredParams; use crate::sub_lib::cryptde::PublicKey; @@ -141,12 +141,15 @@ pub fn server_initializer_collected_params<'a>( args: &[String], ) -> Result, ConfiguratorError> { let app = app_node(); - let (config_file_path, user_specified) = determine_config_file_path(dirs_wrapper, &app, args)?; + + let (config_file_path, user_specified, data_directory, real_user) = + determine_fundamentals(dirs_wrapper, &app, args)?; + let config_file_vcl = match ConfigFileVcl::new(&config_file_path, user_specified) { Ok(cfv) => Box::new(cfv), Err(e) => return Err(ConfiguratorError::required("config-file", &e.to_string())), }; - let multi_config = make_new_multi_config( + let full_multi_config = make_new_multi_config( &app, vec![ Box::new(CommandLineVcl::new(args.to_vec())), @@ -154,12 +157,14 @@ pub fn server_initializer_collected_params<'a>( config_file_vcl, ], )?; - let data_directory = config_file_path - .parent() - .map(|dir| dir.to_path_buf()) - .expectv("data_directory"); - let real_user = real_user_from_multi_config_or_populate(&multi_config, dirs_wrapper); - Ok(GatheredParams::new(multi_config, data_directory, real_user)) + let config_file_path = + value_m!(full_multi_config, "config-file", PathBuf).expect("defaulted param"); + Ok(GatheredParams::new( + full_multi_config, + config_file_path, + real_user, + data_directory, + )) } pub fn establish_port_configurations(config: &mut BootstrapperConfig) { @@ -188,10 +193,12 @@ pub fn privileged_parse_args( multi_config: &MultiConfig, privileged_config: &mut BootstrapperConfig, ) -> Result<(), ConfiguratorError> { - let (real_user, data_directory_opt, chain) = - real_user_data_directory_opt_and_chain(dirs_wrapper, multi_config); - let directory = - data_directory_from_context(dirs_wrapper, &real_user, &data_directory_opt, chain); + let (real_user, data_directory_path, chain) = + real_user_data_directory_path_and_chain(dirs_wrapper, multi_config); + let directory = match data_directory_path { + Some(data_directory_path) => data_directory_path, + None => data_directory_from_context(dirs_wrapper, &real_user, chain), + }; privileged_config.real_user = real_user; privileged_config.data_directory = directory; privileged_config.blockchain_bridge_config.chain = chain; @@ -247,6 +254,8 @@ fn configure_database( config: &BootstrapperConfig, persistent_config: &mut dyn PersistentConfiguration, ) -> Result<(), ConfiguratorError> { + // We don't want to panic in case clandestine_port or blockchain_service_url is not configured + // inside the bootstrap config if let Some(port) = config.clandestine_port_opt { if let Err(pce) = persistent_config.set_clandestine_port(port) { return Err(pce.into_configurator_error("clandestine-port")); @@ -256,6 +265,9 @@ fn configure_database( if let Err(pce) = persistent_config.set_neighborhood_mode(neighborhood_mode_light) { return Err(pce.into_configurator_error("neighborhood-mode")); } + if let Err(pce) = persistent_config.set_min_hops(config.neighborhood_config.min_hops) { + return Err(pce.into_configurator_error("min-hops")); + } if let Some(url) = config .blockchain_bridge_config .blockchain_service_url_opt @@ -274,7 +286,7 @@ fn configure_database( #[cfg(test)] mod tests { use super::*; - use crate::blockchain::bip32::Bip32ECKeyProvider; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::bootstrapper::{BootstrapperConfig, RealUser}; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::db_config::config_dao::ConfigDaoReal; @@ -303,7 +315,7 @@ mod tests { use std::convert::TryFrom; use std::fs::File; use std::io::Write; - use std::path::PathBuf; + use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::vec; @@ -379,6 +391,7 @@ mod tests { config.clandestine_port_opt = None; let mut persistent_config = PersistentConfigurationMock::new() .set_neighborhood_mode_result(Ok(())) + .set_min_hops_result(Ok(())) .set_gas_price_result(Err(PersistentConfigError::TransactionError)); let result = configure_database(&config, &mut persistent_config); @@ -396,6 +409,7 @@ mod tests { Some("https://infura.io/ID".to_string()); let mut persistent_config = PersistentConfigurationMock::new() .set_neighborhood_mode_result(Ok(())) + .set_min_hops_result(Ok(())) .set_blockchain_service_url_result(Err(PersistentConfigError::TransactionError)); let result = configure_database(&config, &mut persistent_config); @@ -423,6 +437,22 @@ mod tests { ) } + #[test] + fn configure_database_handles_error_during_setting_min_hops() { + let mut config = BootstrapperConfig::new(); + config.neighborhood_config.min_hops = Hops::FourHops; + let mut persistent_config = PersistentConfigurationMock::new() + .set_neighborhood_mode_result(Ok(())) + .set_min_hops_result(Err(PersistentConfigError::TransactionError)); + + let result = configure_database(&config, &mut persistent_config); + + assert_eq!( + result, + Err(PersistentConfigError::TransactionError.into_configurator_error("min-hops")) + ) + } + fn make_default_cli_params() -> ArgsBuilder { ArgsBuilder::new().param("--ip", "1.2.3.4") } @@ -432,7 +462,7 @@ mod tests { running_test(); let _guard = EnvironmentGuard::new(); let home_dir = ensure_node_home_directory_exists( - "node_configurator", + "node_configurator_standard", "server_initializer_collected_params_can_read_parameters_from_config_file", ); { @@ -460,7 +490,7 @@ mod tests { fn can_read_dns_servers_and_consuming_private_key_from_config_file() { running_test(); let home_dir = ensure_node_home_directory_exists( - "node_configurator", + "node_configurator_standard", "can_read_wallet_parameters_from_config_file", ); let mut persistent_config = PersistentConfigurationReal::new(Box::new(ConfigDaoReal::new( @@ -506,7 +536,8 @@ mod tests { let consuming_private_key_bytes: Vec = consuming_private_key.from_hex().unwrap(); let consuming_keypair = - Bip32ECKeyProvider::from_raw_secret(consuming_private_key_bytes.as_ref()).unwrap(); + Bip32EncryptionKeyProvider::from_raw_secret(consuming_private_key_bytes.as_ref()) + .unwrap(); assert_eq!( bootstrapper_config.consuming_wallet_opt, Some(Wallet::from(consuming_keypair)), @@ -527,7 +558,7 @@ mod tests { fn privileged_parse_args_creates_configurations() { running_test(); let home_dir = ensure_node_home_directory_exists( - "node_configurator", + "node_configurator_standard", "privileged_parse_args_creates_configurations", ); let args = ArgsBuilder::new() @@ -553,7 +584,8 @@ mod tests { "--consuming-private-key", "ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01", ) - .param("--real-user", "999:999:/home/booga"); + .param("--real-user", "999:999:/home/booga") + .param("--chain", "polygon-mumbai"); let mut config = BootstrapperConfig::new(); let vcls: Vec> = vec![Box::new(CommandLineVcl::new(args.into()))]; @@ -577,7 +609,7 @@ mod tests { config.neighborhood_config, NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, // not populated on the privileged side - min_hops_count: Hops::ThreeHops, + min_hops: Hops::ThreeHops, } ); assert_eq!( @@ -709,16 +741,15 @@ mod tests { #[test] fn server_initializer_collected_params_senses_when_user_specifies_config_file() { running_test(); - let data_dir = ensure_node_home_directory_exists( - "node_configurator_standard", - "server_initializer_collected_params_senses_when_user_specifies_config_file", - ); - let args = ArgsBuilder::new().param("--config-file", "booga.toml"); // nonexistent config file: should stimulate panic because user-specified + let home_dir = PathBuf::from("/unexisting_home/unexisting_alice"); + let data_dir = home_dir.join("data_dir"); + let args = ArgsBuilder::new() + .param("--config-file", "booga.toml") // nonexistent config file: should return error because user-specified + .param("--chain", "polygon-mainnet"); let args_vec: Vec = args.into(); let dir_wrapper = DirsWrapperMock::new() - .home_dir_result(Some(PathBuf::from("/unexisting_home/unexisting_alice"))) + .home_dir_result(Some(home_dir)) .data_dir_result(Some(data_dir)); - let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()).err(); match result { @@ -881,12 +912,14 @@ mod tests { .as_str(), )) .unwrap()]); + config.neighborhood_config.min_hops = Hops::FourHops; config.blockchain_bridge_config.blockchain_service_url_opt = Some("https://infura.io/ID".to_string()); let set_blockchain_service_params_arc = Arc::new(Mutex::new(vec![])); let set_clandestine_port_params_arc = Arc::new(Mutex::new(vec![])); let set_gas_price_params_arc = Arc::new(Mutex::new(vec![])); let set_neighborhood_mode_params_arc = Arc::new(Mutex::new(vec![])); + let set_min_hops_params_arc = Arc::new(Mutex::new(vec![])); let mut persistent_config = PersistentConfigurationMock::new() .set_clandestine_port_params(&set_clandestine_port_params_arc) .set_clandestine_port_result(Ok(())) @@ -895,7 +928,9 @@ mod tests { .set_neighborhood_mode_params(&set_neighborhood_mode_params_arc) .set_neighborhood_mode_result(Ok(())) .set_gas_price_params(&set_gas_price_params_arc) - .set_gas_price_result(Ok(())); + .set_gas_price_result(Ok(())) + .set_min_hops_params(&set_min_hops_params_arc) + .set_min_hops_result(Ok(())); let result = configure_database(&config, &mut persistent_config); @@ -914,6 +949,8 @@ mod tests { assert_eq!(*set_gas_price_params, vec![gas_price]); let set_clandestine_port_params = set_clandestine_port_params_arc.lock().unwrap(); assert_eq!(*set_clandestine_port_params, vec![1234]); + let set_min_hops_params = set_min_hops_params_arc.lock().unwrap(); + assert_eq!(*set_min_hops_params, vec![Hops::FourHops]) } #[test] @@ -928,6 +965,7 @@ mod tests { .set_blockchain_service_url_params(&set_blockchain_service_params_arc) .set_neighborhood_mode_params(&set_neighborhood_mode_params_arc) .set_neighborhood_mode_result(Ok(())) + .set_min_hops_result(Ok(())) .set_gas_price_result(Ok(())); let result = configure_database(&config, &mut persistent_config); @@ -986,4 +1024,87 @@ mod tests { let expected = ExternalData::new(DEFAULT_CHAIN, NeighborhoodModeLight::ZeroHop, None); assert_eq!(result, expected) } + + fn check_data_directory_combinations_when_user_specifies_data_directory_without_chain_specific_directory( + chain_opt: Option<&str>, + data_directory_opt: Option<&str>, + expected: Option<&str>, + ) { + let home_dir = PathBuf::from("/home/cooga"); + let standard_data_dir = PathBuf::from("/home/cooga/.local"); + + let args = match (chain_opt, data_directory_opt) { + (Some(chain_opt), Some(data_directory_opt)) => ArgsBuilder::new() + .param("--chain", chain_opt) + .param("--real-user", "999:999:/home/cooga") + .param("--data-directory", data_directory_opt), + (None, Some(data_directory_opt)) => ArgsBuilder::new() + .param("--data-directory", data_directory_opt) + .param("--real-user", "999:999:/home/cooga"), + (Some(chain_opt), None) => ArgsBuilder::new() + .param("--chain", chain_opt) + .param("--real-user", "999:999:/home/cooga"), + (None, None) => ArgsBuilder::new().param("--real-user", "999:999:/home/cooga"), + }; + let args_vec: Vec = args.into(); + let dir_wrapper = match data_directory_opt { + Some(data_directory_opt) => DirsWrapperMock::new() + .home_dir_result(Some(home_dir)) + .data_dir_result(Some(PathBuf::from(data_directory_opt))), + None => DirsWrapperMock::new() + .home_dir_result(Some(home_dir)) + .data_dir_result(Some(PathBuf::from(standard_data_dir))), + }; + + let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()) + .unwrap() + .data_directory + .to_string_lossy() + .to_string(); + + assert_eq!(result, expected.unwrap()); + } + + #[test] + fn server_initializer_collected_params_senses_when_user_specifies_data_directory_without_chain_specific_directory( + ) { + running_test(); + let _guard = EnvironmentGuard::new(); + let home_dir = Path::new("/home/cooga"); + let home_dir_poly_main = home_dir.join(".local").join("MASQ").join("polygon-mainnet"); + let home_dir_poly_mumbai = home_dir.join(".local").join("MASQ").join("polygon-mumbai"); + vec![ + (None, None, Some(home_dir_poly_main.to_str().unwrap())), + ( + Some("polygon-mumbai"), + None, + Some(home_dir_poly_mumbai.to_str().unwrap()), + ), + (None, Some("/cooga"), Some("/cooga")), + (Some("polygon-mumbai"), Some("/cooga"), Some("/cooga")), + ( + None, + Some("/cooga/polygon-mumbai"), + Some("/cooga/polygon-mumbai"), + ), + ( + None, + Some("/cooga/polygon-mumbai/polygon-mainnet"), + Some("/cooga/polygon-mumbai/polygon-mainnet"), + ), + ( + Some("polygon-mumbai"), + Some("/cooga/polygon-mumbai"), + Some("/cooga/polygon-mumbai"), + ), + ] + .iter() + .for_each(|(chain_opt, data_directory_opt, expected)| { + check_data_directory_combinations_when_user_specifies_data_directory_without_chain_specific_directory( + *chain_opt, + *data_directory_opt, + *expected + ); + }); + } } diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index db948b67a..4238bd8d5 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -1,7 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; -use crate::blockchain::bip32::Bip32ECKeyProvider; +use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::bootstrapper::BootstrapperConfig; use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration}; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET}; @@ -20,8 +20,7 @@ use masq_lib::constants::{DEFAULT_CHAIN, MASQ_URL_PREFIX}; use masq_lib::logger::Logger; use masq_lib::multi_config::MultiConfig; use masq_lib::shared_schema::{ConfiguratorError, ParamError}; -use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; -use masq_lib::utils::{AutomapProtocol, ExpectValue}; +use masq_lib::utils::{to_string, AutomapProtocol, ExpectValue}; use rustc_hex::FromHex; use std::net::{IpAddr, Ipv4Addr}; use std::str::FromStr; @@ -174,8 +173,8 @@ pub fn get_wallets( consuming_private_key ) }); - let key_pair = - Bip32ECKeyProvider::from_raw_secret(key_bytes.as_slice()).unwrap_or_else(|_| { + let key_pair = Bip32EncryptionKeyProvider::from_raw_secret(key_bytes.as_slice()) + .unwrap_or_else(|_| { panic!( "Wallet corruption: consuming wallet private key in invalid format: {:?}", key_bytes @@ -214,14 +213,16 @@ pub fn make_neighborhood_config( } }; - let min_hops_count = value_m!(multi_config, "min-hops", Hops) - .expect("Clap schema didn't specify the default value."); + let min_hops: Hops = match value_m!(multi_config, "min-hops", Hops) { + Some(hops) => hops, + None => match persistent_config.min_hops() { + Ok(hops) => hops, + Err(e) => panic!("Unable to find min_hops value in database: {:?}", e), + }, + }; match make_neighborhood_mode(multi_config, neighbor_configs, persistent_config) { - Ok(mode) => Ok(NeighborhoodConfig { - mode, - min_hops_count, - }), + Ok(mode) => Ok(NeighborhoodConfig { mode, min_hops }), Err(e) => Err(e), } } @@ -342,27 +343,28 @@ fn convert_ci_configs( match value_m!(multi_config, "neighbors", String) { None => Ok(None), Some(joined_configs) => { - let separate_configs: Vec = joined_configs - .split(',') - .map(|s| s.to_string()) - .collect_vec(); + let separate_configs: Vec = + joined_configs.split(',').map(to_string).collect_vec(); if separate_configs.is_empty() { Ok(None) } else { - let dummy_cryptde: Box = { - if value_m!(multi_config, "fake-public-key", String).is_none() { - Box::new(CryptDEReal::new(TEST_DEFAULT_CHAIN)) - } else { - Box::new(CryptDENull::new(TEST_DEFAULT_CHAIN)) - } - }; let desired_chain = Chain::from( value_m!(multi_config, "chain", String) .unwrap_or_else(|| DEFAULT_CHAIN.rec().literal_identifier.to_string()) .as_str(), ); - let results = - validate_descriptors_from_user(separate_configs, dummy_cryptde, desired_chain); + let cryptde_for_key_len: Box = { + if value_m!(multi_config, "fake-public-key", String).is_none() { + Box::new(CryptDEReal::new(desired_chain)) + } else { + Box::new(CryptDENull::new(desired_chain)) + } + }; + let results = validate_descriptors_from_user( + separate_configs, + cryptde_for_key_len, + desired_chain, + ); let (ok, err): (Vec, Vec) = results.into_iter().partition(|result| result.is_ok()); let ok = ok @@ -385,12 +387,12 @@ fn convert_ci_configs( fn validate_descriptors_from_user( descriptors: Vec, - dummy_cryptde: Box, + cryptde_for_key_len: Box, desired_native_chain: Chain, ) -> Vec> { descriptors.into_iter().map(|node_desc_from_ci| { let node_desc_trimmed = node_desc_from_ci.trim(); - match NodeDescriptor::try_from((dummy_cryptde.as_ref(), node_desc_trimmed)) { + match NodeDescriptor::try_from((cryptde_for_key_len.as_ref(), node_desc_trimmed)) { Ok(descriptor) => { let competence_from_descriptor = descriptor.blockchain; if desired_native_chain == competence_from_descriptor { @@ -619,9 +621,9 @@ fn is_user_specified(multi_config: &MultiConfig, parameter: &str) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::accountant::database_access_objects::utils::ThresholdUtils; + use crate::accountant::db_access_objects::utils::ThresholdUtils; use crate::apps::app_node; - use crate::blockchain::bip32::Bip32ECKeyProvider; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::db_config::config_dao::{ConfigDao, ConfigDaoReal}; @@ -632,6 +634,7 @@ mod tests { use crate::sub_lib::neighborhood::{Hops, DEFAULT_RATE_PACK}; use crate::sub_lib::utils::make_new_multi_config; use crate::sub_lib::wallet::Wallet; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::unshared_test_utils::{ configure_default_persistent_config, default_persistent_config_just_accountant_config, @@ -642,7 +645,7 @@ mod tests { use masq_lib::constants::DEFAULT_GAS_PRICE; use masq_lib::multi_config::{CommandLineVcl, NameValueVclArg, VclArg, VirtualCommandLine}; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; use masq_lib::utils::running_test; use std::path::PathBuf; use std::str::FromStr; @@ -714,13 +717,38 @@ mod tests { ], DEFAULT_RATE_PACK ), - min_hops_count: Hops::OneHop, + min_hops: Hops::OneHop, }) ); } #[test] - fn make_neighborhood_config_standard_missing_min_hops_count() { + #[should_panic(expected = "Unable to find min_hops value in database: NotPresent")] + fn node_panics_if_min_hops_value_does_not_exist_inside_multi_config_or_db() { + running_test(); + let multi_config = make_new_multi_config( + &app_node(), + vec![Box::new(CommandLineVcl::new( + ArgsBuilder::new() + .param("--neighborhood-mode", "standard") + .opt("--min-hops") + .into(), + ))], + ) + .unwrap(); + let mut persistent_config = PersistentConfigurationMock::new() + .min_hops_result(Err(PersistentConfigError::NotPresent)); + + let _result = make_neighborhood_config( + &UnprivilegedParseArgsConfigurationDaoReal {}, + &multi_config, + &mut persistent_config, + &mut BootstrapperConfig::new(), + ); + } + + #[test] + fn make_neighborhood_config_standard_missing_min_hops() { running_test(); let multi_config = make_new_multi_config( &app_node(), @@ -744,13 +772,12 @@ mod tests { &mut BootstrapperConfig::new(), ); - let min_hops_count = result.unwrap().min_hops_count; - assert_eq!(min_hops_count, Hops::ThreeHops); + let min_hops = result.unwrap().min_hops; + assert_eq!(min_hops, Hops::ThreeHops); } #[test] - fn make_neighborhood_config_standard_uses_default_value_when_no_min_hops_count_value_is_provided( - ) { + fn make_neighborhood_config_standard_uses_default_value_when_no_min_hops_value_is_provided() { running_test(); let args = ArgsBuilder::new() .param("--neighborhood-mode", "standard") @@ -770,13 +797,12 @@ mod tests { &mut BootstrapperConfig::new(), ); - let min_hops_count = result.unwrap().min_hops_count; - assert_eq!(min_hops_count, Hops::ThreeHops); + let min_hops = result.unwrap().min_hops; + assert_eq!(min_hops, Hops::ThreeHops); } #[test] - fn make_neighborhood_config_standard_throws_err_when_undesirable_min_hops_count_value_is_provided( - ) { + fn make_neighborhood_config_standard_throws_err_when_undesirable_min_hops_value_is_provided() { running_test(); let args = ArgsBuilder::new() .param("--neighborhood-mode", "standard") @@ -826,7 +852,7 @@ mod tests { let node_addr = match result { Ok(NeighborhoodConfig { mode: NeighborhoodMode::Standard(node_addr, _, _), - min_hops_count: Hops::ThreeHops, + min_hops: Hops::ThreeHops, }) => node_addr, x => panic!("Wasn't expecting {:?}", x), }; @@ -1530,7 +1556,8 @@ mod tests { assert_eq!( config.consuming_wallet_opt, Some(Wallet::from( - Bip32ECKeyProvider::from_raw_secret(consuming_private_key.as_slice()).unwrap() + Bip32EncryptionKeyProvider::from_raw_secret(consuming_private_key.as_slice()) + .unwrap() )), ); assert_eq!( @@ -1632,6 +1659,7 @@ mod tests { "masq://eth-ropsten:AQIDBA@1.2.3.4:1234,masq://eth-ropsten:AgMEBQ@2.3.4.5:2345", ), None, + None, ) .check_password_result(Ok(false)) .set_mapping_protocol_params(&set_mapping_protocol_params_arc) @@ -1681,7 +1709,7 @@ mod tests { vec![Box::new(CommandLineVcl::new(args.into()))]; let multi_config = make_new_multi_config(&app_node(), vcls).unwrap(); let mut persistent_configuration = { - let config = make_persistent_config(None, None, None, None, None, None) + let config = make_persistent_config(None, None, None, None, None, None, None) .blockchain_service_url_result(Ok(Some("https://infura.io/ID".to_string()))); default_persistent_config_just_accountant_config(config) }; @@ -2251,7 +2279,8 @@ mod tests { ) { running_test(); let multi_config = make_simplified_multi_config([]); - let mut persistent_config = make_persistent_config(None, None, None, None, None, None); + let mut persistent_config = + make_persistent_config(None, None, None, None, None, None, None); let mut config = BootstrapperConfig::new(); get_wallets(&multi_config, &mut persistent_config, &mut config).unwrap(); @@ -2292,6 +2321,7 @@ mod tests { None, None, None, + None, ); let mut config = BootstrapperConfig::new(); @@ -2317,6 +2347,7 @@ mod tests { None, None, None, + None, ); let mut config = BootstrapperConfig::new(); @@ -2342,6 +2373,7 @@ mod tests { None, None, None, + None, ); let mut config = BootstrapperConfig::new(); config.db_password_opt = Some("password".to_string()); @@ -2368,6 +2400,7 @@ mod tests { None, None, None, + None, ) .check_password_result(Ok(false)); let mut config = BootstrapperConfig::new(); @@ -2597,9 +2630,9 @@ mod tests { gas_price_opt: Option, past_neighbors_opt: Option<&str>, rate_pack_opt: Option, + min_hops_opt: Option, ) -> PersistentConfigurationMock { - let consuming_wallet_private_key_opt = - consuming_wallet_private_key_opt.map(|x| x.to_string()); + let consuming_wallet_private_key_opt = consuming_wallet_private_key_opt.map(to_string); let earning_wallet_opt = match earning_wallet_address_opt { None => None, Some(address) => Some(Wallet::from_str(address).unwrap()), @@ -2615,15 +2648,15 @@ mod tests { _ => Ok(None), }; let rate_pack = rate_pack_opt.unwrap_or(DEFAULT_RATE_PACK); + let min_hops = min_hops_opt.unwrap_or(MIN_HOPS_FOR_TEST); PersistentConfigurationMock::new() .consuming_wallet_private_key_result(Ok(consuming_wallet_private_key_opt)) - .earning_wallet_address_result( - Ok(earning_wallet_address_opt.map(|ewa| ewa.to_string())), - ) + .earning_wallet_address_result(Ok(earning_wallet_address_opt.map(to_string))) .earning_wallet_result(Ok(earning_wallet_opt)) .gas_price_result(Ok(gas_price)) .past_neighbors_result(past_neighbors_result) .mapping_protocol_result(Ok(Some(AutomapProtocol::Pcp))) .rate_pack_result(Ok(rate_pack)) + .min_hops_result(Ok(min_hops)) } } diff --git a/node/src/node_test_utils.rs b/node/src/node_test_utils.rs index b82c663a3..f4c1a8d29 100644 --- a/node/src/node_test_utils.rs +++ b/node/src/node_test_utils.rs @@ -106,8 +106,8 @@ impl IdWrapperMock { } pub struct DirsWrapperMock { - data_dir_result: Option, - home_dir_result: Option, + pub(crate) data_dir_result: Option, + pub(crate) home_dir_result: Option, } impl DirsWrapper for DirsWrapperMock { @@ -289,17 +289,6 @@ pub fn start_recorder_refcell_opt(recorder: &RefCell>) -> Addr< recorder.borrow_mut().take().unwrap().start() } -pub fn make_stream_handler_pool_subs_from( - stream_handler_pool_opt: Option, -) -> StreamHandlerPoolSubs { - let recorder = match stream_handler_pool_opt { - Some(recorder) => recorder, - None => Recorder::new(), - }; - let addr = recorder.start(); - make_stream_handler_pool_subs_from_recorder(&addr) -} - pub fn make_stream_handler_pool_subs_from_recorder(addr: &Addr) -> StreamHandlerPoolSubs { StreamHandlerPoolSubs { add_sub: recipient!(addr, AddStreamMsg), diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 9ffff8037..2eebd12ca 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -33,7 +33,6 @@ use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; use crate::sub_lib::proxy_server::ProxyServerSubs; use crate::sub_lib::proxy_server::{AddReturnRouteMessage, AddRouteMessage}; use crate::sub_lib::route::Route; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::stream_key::StreamKey; use crate::sub_lib::ttl_hashmap::TtlHashMap; @@ -108,21 +107,6 @@ impl Handler for ProxyServer { } } -//TODO comes across as basically dead code -// I think the idea was to supply the wallet if wallets hadn't been generated until recently, without the need to kill the Node -// I also found out that there is a test for this, but it changes nothing on it's normally unused -impl Handler for ProxyServer { - type Result = (); - - fn handle( - &mut self, - _msg: SetConsumingWalletMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - self.consuming_wallet_balance = Some(0); - } -} - impl Handler for ProxyServer { type Result = (); @@ -247,16 +231,16 @@ impl ProxyServer { add_return_route: recipient!(addr, AddReturnRouteMessage), add_route: recipient!(addr, AddRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), - set_consuming_wallet_sub: recipient!(addr, SetConsumingWalletMessage), node_from_ui: recipient!(addr, NodeFromUiMessage), } } fn handle_dns_resolve_failure(&mut self, msg: &ExpiredCoresPackage) { - let return_route_info = match self.get_return_route_info(&msg.remaining_route) { - Some(rri) => rri, - None => return, // TODO: Eventually we'll have to do something better here, but we'll probably need some heuristics. - }; + 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.main_cryptde.public_key(); @@ -342,10 +326,11 @@ 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) { - Some(rri) => rri, - 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( &return_route_info, response.sequenced_packet.data.len(), @@ -755,7 +740,11 @@ impl ProxyServer { } } - fn get_return_route_info(&self, remaining_route: &Route) -> Option> { + 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.main_cryptde) @@ -770,7 +759,7 @@ impl ProxyServer { match self.route_ids_to_return_routes.get(&return_route_id) { Some(rri) => Some(rri), None => { - error!(self.logger, "Can't report services consumed: received response with bogus return-route ID {}. Ignoring", return_route_id); + error!(self.logger, "Can't report services consumed: received response with bogus return-route ID {} for {}. Ignoring", return_route_id, source); None } } @@ -1996,86 +1985,6 @@ mod tests { assert_eq!(record, &expected_pkg); } - #[test] - fn proxy_server_applies_late_wallet_information() { - let main_cryptde = main_cryptde(); - let alias_cryptde = alias_cryptde(); - let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let hopper_mock = Recorder::new(); - let hopper_log_arc = hopper_mock.get_recording(); - let hopper_awaiter = hopper_mock.get_awaiter(); - let destination_key = PublicKey::from(&b"our destination"[..]); - let route_query_response = RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![make_exit_service_from_key(destination_key.clone())], - vec![], - 1234, - ), - }; - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let stream_key = make_meaningless_stream_key(); - let expected_data = http_request.to_vec(); - let msg_from_dispatcher = InboundClientData { - timestamp: SystemTime::now(), - peer_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), - last_data: true, - is_clandestine: false, - data: expected_data.clone(), - }; - let expected_http_request = PlainData::new(http_request); - let route = route_query_response.route.clone(); - 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_port: HTTP_PORT, - protocol: ProxyProtocol::HTTP, - originator_public_key: alias_cryptde.public_key().clone(), - }; - let expected_pkg = IncipientCoresPackage::new( - main_cryptde, - route, - expected_payload.into(), - &destination_key, - ) - .unwrap(); - thread::spawn(move || { - let stream_key_factory = StreamKeyFactoryMock::new(); // can't make any stream keys; shouldn't have to - let system = System::new("proxy_server_applies_late_wallet_information"); - let mut subject = ProxyServer::new(main_cryptde, alias_cryptde, true, None, false); - subject.stream_key_factory = Box::new(stream_key_factory); - subject.keys_and_addrs.insert(stream_key, socket_addr); - subject - .stream_key_routes - .insert(stream_key, route_query_response); - let subject_addr: Addr = subject.start(); - let mut peer_actors = peer_actors_builder().hopper(hopper_mock).build(); - peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - - subject_addr - .try_send(SetConsumingWalletMessage { - wallet: make_wallet("Consuming wallet"), - }) - .unwrap(); - - subject_addr.try_send(msg_from_dispatcher).unwrap(); - system.run(); - }); - - hopper_awaiter.await_message_count(1); - let recording = hopper_log_arc.lock().unwrap(); - let record = recording.get_record::(0); - assert_eq!(record, &expected_pkg); - } - #[test] fn proxy_server_receives_http_request_from_dispatcher_then_sends_multihop_cores_package_to_hopper( ) { @@ -4286,7 +4195,7 @@ mod tests { 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. Ignoring"); + 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); } @@ -4405,7 +4314,7 @@ mod tests { ); 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. Ignoring", 1000); + 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] diff --git a/node/src/run_modes_factories.rs b/node/src/run_modes_factories.rs index 05c179263..b3f3d3f08 100644 --- a/node/src/run_modes_factories.rs +++ b/node/src/run_modes_factories.rs @@ -15,8 +15,6 @@ use crate::server_initializer::{ use masq_lib::command::StdStreams; use masq_lib::shared_schema::ConfiguratorError; use masq_lib::utils::ExpectValue; -#[cfg(test)] -use std::any::Any; use std::cell::RefCell; pub type RunModeResult = Result<(), ConfiguratorError>; @@ -68,22 +66,24 @@ pub trait DaemonInitializerFactory { pub trait DumpConfigRunner { fn go(&self, streams: &mut StdStreams, args: &[String]) -> RunModeResult; - declare_as_any!(); + as_any_in_trait!(); } pub trait ServerInitializer: futures::Future { fn go(&mut self, streams: &mut StdStreams, args: &[String]) -> RunModeResult; - declare_as_any!(); + as_any_in_trait!(); } pub trait DaemonInitializer { fn go(&mut self, streams: &mut StdStreams, args: &[String]) -> RunModeResult; - declare_as_any!(); + as_any_in_trait!(); } impl DumpConfigRunnerFactory for DumpConfigRunnerFactoryReal { fn make(&self) -> Box { - Box::new(DumpConfigRunnerReal) + Box::new(DumpConfigRunnerReal { + dirs_wrapper: Box::new(DirsWrapperReal), + }) } } diff --git a/node/src/server_initializer.rs b/node/src/server_initializer.rs index f3eb99ea3..95ec30799 100644 --- a/node/src/server_initializer.rs +++ b/node/src/server_initializer.rs @@ -52,6 +52,7 @@ impl ServerInitializer for ServerInitializerReal { self.privilege_dropper .chown(¶ms.data_directory, ¶ms.real_user); + self.privilege_dropper.drop_privileges(¶ms.real_user); result @@ -66,7 +67,7 @@ impl ServerInitializer for ServerInitializerReal { .initialize_as_unprivileged(¶ms.multi_config, streams), ) } - implement_as_any!(); + as_any_in_trait_impl!(); } impl Future for ServerInitializerReal { @@ -116,20 +117,23 @@ impl ResultsCombiner for RunModeResult { pub struct GatheredParams<'a> { pub multi_config: MultiConfig<'a>, - pub data_directory: PathBuf, + pub config_file_path: PathBuf, pub real_user: RealUser, + pub data_directory: PathBuf, } impl<'a> GatheredParams<'a> { pub fn new( multi_config: MultiConfig<'a>, - data_directory: PathBuf, + config_file_path: PathBuf, real_user: RealUser, + data_directory: PathBuf, ) -> Self { Self { multi_config, - data_directory, + config_file_path, real_user, + data_directory, } } } diff --git a/node/src/stream_messages.rs b/node/src/stream_messages.rs index 4b838ecdc..34dccbf92 100644 --- a/node/src/stream_messages.rs +++ b/node/src/stream_messages.rs @@ -77,27 +77,20 @@ impl Debug for PoolBindMessage { #[cfg(test)] mod tests { use super::*; - use crate::node_test_utils::make_stream_handler_pool_subs_from; - use crate::test_utils::recorder::peer_actors_builder; - use actix::System; - - impl PartialEq for AddStreamMsg { - fn eq(&self, _other: &Self) -> bool { - // We need to implement PartialEq so that AddStreamMsg can be received by the Recorder; - // but AddStreamMsg breaks the rules for an actor message by containing references to - // outside resources (namely, an I/O stream) and therefore cannot have a real implementation - // of PartialEq. So here we break the rules again to patch up the problems created by - // the first breach of the rules. Don't move this into the production tree; it only needs - // to be here for the Recorder, and the Recorder is only in the test tree. - intentionally_blank!() - } - } + use crate::node_test_utils::make_stream_handler_pool_subs_from_recorder; + use crate::test_utils::recorder::{ + make_dispatcher_subs_from_recorder, make_recorder, peer_actors_builder, + }; + use actix::{Actor, System}; #[test] fn pool_bind_message_is_debug() { let _system = System::new("test"); - let dispatcher_subs = peer_actors_builder().build().dispatcher; - let stream_handler_pool_subs = make_stream_handler_pool_subs_from(None); + let (dispatcher, _, _) = make_recorder(); + let dispatcher_subs = make_dispatcher_subs_from_recorder(&dispatcher.start()); + let (stream_handler_pool, _, _) = make_recorder(); + let stream_handler_pool_subs = + make_stream_handler_pool_subs_from_recorder(&stream_handler_pool.start()); let neighborhood_subs = peer_actors_builder().build().neighborhood; let subject = PoolBindMessage { dispatcher_subs, diff --git a/node/src/stream_reader.rs b/node/src/stream_reader.rs index 6f297f806..7b42aad17 100644 --- a/node/src/stream_reader.rs +++ b/node/src/stream_reader.rs @@ -213,11 +213,11 @@ mod tests { use crate::json_discriminator_factory::JsonDiscriminatorFactory; use crate::json_masquerader::JsonMasquerader; use crate::masquerader::Masquerader; - use crate::node_test_utils::{check_timestamp, make_stream_handler_pool_subs_from}; + use crate::node_test_utils::{check_timestamp, make_stream_handler_pool_subs_from_recorder}; use crate::stream_handler_pool::StreamHandlerPoolSubs; use crate::stream_messages::RemovedStreamType::NonClandestine; use crate::sub_lib::dispatcher::DispatcherSubs; - use crate::test_utils::recorder::make_dispatcher_subs_from; + use crate::test_utils::recorder::make_dispatcher_subs_from_recorder; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::Recorder; use crate::test_utils::recorder::Recording; @@ -238,13 +238,16 @@ mod tests { fn stream_handler_pool_stuff() -> (Arc>, StreamHandlerPoolSubs) { let (shp, _, recording) = make_recorder(); - (recording, make_stream_handler_pool_subs_from(Some(shp))) + ( + recording, + make_stream_handler_pool_subs_from_recorder(&shp.start()), + ) } fn dispatcher_stuff() -> (Arc>, DispatcherSubs) { let (dispatcher, _, recording) = make_recorder(); let addr: Addr = dispatcher.start(); - (recording, make_dispatcher_subs_from(&addr)) + (recording, make_dispatcher_subs_from_recorder(&addr)) } #[test] diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 2c8fbb2fd..15ceb0613 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -1,13 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::banned_dao::BannedDaoFactory; -use crate::accountant::database_access_objects::payable_dao::PayableDaoFactory; -use crate::accountant::database_access_objects::pending_payable_dao::PendingPayableDaoFactory; -use crate::accountant::database_access_objects::receivable_dao::ReceivableDaoFactory; -use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; +use crate::accountant::db_access_objects::banned_dao::BannedDaoFactory; +use crate::accountant::db_access_objects::payable_dao::PayableDaoFactory; +use crate::accountant::db_access_objects::pending_payable_dao::PendingPayableDaoFactory; +use crate::accountant::db_access_objects::receivable_dao::ReceivableDaoFactory; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::{ checked_conversion, Accountant, ReceivedPayments, ReportTransactionReceipts, ScanError, SentPayables, }; +use crate::actor_system_factory::SubsFactory; use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::wallet::Wallet; @@ -15,8 +16,6 @@ use actix::Recipient; use actix::{Addr, Message}; use lazy_static::lazy_static; use masq_lib::ui_gateway::NodeFromUiMessage; -#[cfg(test)] -use std::any::Any; use std::fmt::{Debug, Formatter}; use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; @@ -95,7 +94,7 @@ pub struct AccountantSubs { pub report_routing_service_provided: Recipient, pub report_exit_service_provided: Recipient, pub report_services_consumed: Recipient, - pub report_consuming_wallet_balances_and_qualified_payables: Recipient, + pub report_payable_payments_setup: Recipient, pub report_inbound_payments: Recipient, pub init_pending_payable_fingerprints: Recipient, pub report_transaction_receipts: Recipient, @@ -110,13 +109,9 @@ impl Debug for AccountantSubs { } } -pub trait AccountantSubsFactory { - fn make(&self, addr: &Addr) -> AccountantSubs; -} - pub struct AccountantSubsFactoryReal {} -impl AccountantSubsFactory for AccountantSubsFactoryReal { +impl SubsFactory for AccountantSubsFactoryReal { fn make(&self, addr: &Addr) -> AccountantSubs { Accountant::make_subs_from(addr) } @@ -179,7 +174,7 @@ pub enum SignConversionError { pub trait MessageIdGenerator { fn id(&self) -> u32; - declare_as_any!(); + as_any_in_trait!(); } #[derive(Default)] @@ -189,7 +184,7 @@ impl MessageIdGenerator for MessageIdGeneratorReal { fn id(&self) -> u32 { MSG_ID_INCREMENTER.fetch_add(1, Ordering::Relaxed) } - implement_as_any!(); + as_any_in_trait_impl!(); } #[cfg(test)] @@ -197,10 +192,9 @@ mod tests { use crate::accountant::test_utils::AccountantBuilder; use crate::accountant::{checked_conversion, Accountant}; use crate::sub_lib::accountant::{ - AccountantSubsFactory, AccountantSubsFactoryReal, MessageIdGenerator, - MessageIdGeneratorReal, PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET, - DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, MSG_ID_INCREMENTER, - TEMPORARY_CONSUMING_WALLET, + AccountantSubsFactoryReal, MessageIdGenerator, MessageIdGeneratorReal, PaymentThresholds, + ScanIntervals, SubsFactory, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, + DEFAULT_SCAN_INTERVALS, MSG_ID_INCREMENTER, TEMPORARY_CONSUMING_WALLET, }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::recorder::{make_accountant_subs_from_recorder, Recorder}; diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 7c8e56110..65e59a68e 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -1,7 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::database_access_objects::payable_dao::PayableAccount; -use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; use crate::accountant::{RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder}; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::sub_lib::peer_actors::BindMessage; @@ -11,19 +12,22 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::ui_gateway::NodeFromUiMessage; use std::fmt; use std::fmt::{Debug, Formatter}; +use web3::types::U256; #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct BlockchainBridgeConfig { pub blockchain_service_url_opt: Option, pub chain: Chain, + // TODO: totally ignored during the setup of the BlockchainBridge actor! + // Use it in the body or delete this field pub gas_price: u64, } #[derive(Clone, PartialEq, Eq)] pub struct BlockchainBridgeSubs { pub bind: Recipient, - pub report_accounts_payable: Recipient, - pub pps_for_blockchain_bridge: Recipient, + pub outbound_payments_instructions: Recipient, + pub qualified_payables: Recipient, pub retrieve_transactions: Recipient, pub ui_sub: Recipient, pub request_transaction_receipts: Recipient, @@ -35,19 +39,28 @@ impl Debug for BlockchainBridgeSubs { } } -impl SkeletonOptHolder for PayablePaymentSetup { - fn skeleton_opt(&self) -> Option { - self.response_skeleton_opt - } +#[derive(Message)] +pub struct OutboundPaymentsInstructions { + pub affordable_accounts: Vec, + pub agent: Box, + pub response_skeleton_opt: Option, } -#[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct OutcomingPaymentsInstructions { - pub accounts: Vec, - pub response_skeleton_opt: Option, +impl OutboundPaymentsInstructions { + pub fn new( + affordable_accounts: Vec, + agent: Box, + response_skeleton_opt: Option, + ) -> Self { + Self { + affordable_accounts, + agent, + response_skeleton_opt, + } + } } -impl SkeletonOptHolder for OutcomingPaymentsInstructions { +impl SkeletonOptHolder for OutboundPaymentsInstructions { fn skeleton_opt(&self) -> Option { self.response_skeleton_opt } @@ -55,21 +68,57 @@ impl SkeletonOptHolder for OutcomingPaymentsInstructions { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConsumingWalletBalances { - pub transaction_fee_minor: u128, - pub masq_tokens_minor: u128, + // The supply of this currency isn't limited by our database and + // theoretically can be much bigger than of our utility currency + pub transaction_fee_balance_in_minor_units: U256, + // This supply must fit in u128 because otherwise our database would + // not be fully capable of handling math with it not threatened by + // an overflow + pub service_fee_balance_in_minor_units: u128, +} + +impl ConsumingWalletBalances { + pub fn new(transaction_fee: U256, service_fee: u128) -> Self { + Self { + transaction_fee_balance_in_minor_units: transaction_fee, + service_fee_balance_in_minor_units: service_fee, + } + } } #[cfg(test)] mod tests { - use crate::test_utils::recorder::{make_blockchain_bridge_subs_from, Recorder}; + use crate::actor_system_factory::SubsFactory; + use crate::blockchain::blockchain_bridge::{BlockchainBridge, BlockchainBridgeSubsFactoryReal}; + use crate::blockchain::test_utils::BlockchainInterfaceMock; + use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; + use crate::test_utils::recorder::{make_blockchain_bridge_subs_from_recorder, Recorder}; use actix::Actor; #[test] fn blockchain_bridge_subs_debug() { let recorder = Recorder::new().start(); - let subject = make_blockchain_bridge_subs_from(&recorder); + let subject = make_blockchain_bridge_subs_from_recorder(&recorder); assert_eq!(format!("{:?}", subject), "BlockchainBridgeSubs"); } + + #[test] + fn blockchain_bridge_subs_factory_produces_proper_subs() { + let subject = BlockchainBridgeSubsFactoryReal {}; + let blockchain_interface = BlockchainInterfaceMock::default(); + let persistent_config = PersistentConfigurationMock::new(); + let accountant = BlockchainBridge::new( + Box::new(blockchain_interface), + Box::new(persistent_config), + false, + None, + ); + let addr = accountant.start(); + + let subs = subject.make(&addr); + + assert_eq!(subs, BlockchainBridge::make_subs_from(&addr)) + } } diff --git a/node/src/sub_lib/combined_parameters.rs b/node/src/sub_lib/combined_parameters.rs index dfe188a81..f70f60f0f 100644 --- a/node/src/sub_lib/combined_parameters.rs +++ b/node/src/sub_lib/combined_parameters.rs @@ -5,7 +5,7 @@ use crate::sub_lib::combined_parameters::CombinedParamsDataTypes::U64; use crate::sub_lib::combined_parameters::InitializationState::{Initialized, Uninitialized}; use crate::sub_lib::neighborhood::RatePack; use masq_lib::constants::COMBINED_PARAMETERS_DELIMITER; -use masq_lib::utils::ExpectValue; +use masq_lib::utils::{to_string, ExpectValue}; use paste::paste; use std::any::Any; use std::collections::HashMap; @@ -55,9 +55,9 @@ impl CombinedParamsValueRetriever { fn parse(str_value: &str) -> Result where T: std::str::FromStr, - ::Err: ToString, + ::Err: Display, { - str_value.parse::().map_err(|e| e.to_string()) + str_value.parse::().map_err(to_string) } match data_type { CombinedParamsDataTypes::U64 => { diff --git a/node/src/sub_lib/configurator.rs b/node/src/sub_lib/configurator.rs index 43f5c3319..3f92a7535 100644 --- a/node/src/sub_lib/configurator.rs +++ b/node/src/sub_lib/configurator.rs @@ -6,6 +6,7 @@ use masq_lib::ui_gateway::NodeFromUiMessage; use std::fmt; use std::fmt::{Debug, Formatter}; +// GH-728 #[derive(Debug, actix::Message, Clone, PartialEq, Eq)] pub struct NewPasswordMessage { pub new_password: String, diff --git a/node/src/sub_lib/mod.rs b/node/src/sub_lib/mod.rs index 2bbc553ab..51360357b 100644 --- a/node/src/sub_lib/mod.rs +++ b/node/src/sub_lib/mod.rs @@ -34,7 +34,6 @@ pub mod proxy_server; pub mod route; pub mod sequence_buffer; pub mod sequencer; -pub mod set_consuming_wallet_message; pub mod socket_server; pub mod stream_connector; pub mod stream_handler_pool; diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index c5d660afb..64c30c2fe 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -12,7 +12,6 @@ use crate::sub_lib::hopper::ExpiredCoresPackage; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::route::Route; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::utils::{NotifyLaterHandle, NotifyLaterHandleReal}; @@ -370,7 +369,7 @@ impl Display for DescriptorParsingError<'_> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum Hops { OneHop = 1, TwoHops = 2, @@ -391,15 +390,21 @@ impl FromStr for Hops { "4" => Ok(Hops::FourHops), "5" => Ok(Hops::FiveHops), "6" => Ok(Hops::SixHops), - _ => Err("Invalid value for min hops count provided".to_string()), + _ => Err("Invalid value for min hops provided".to_string()), } } } +impl Display for Hops { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", *self as usize) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct NeighborhoodConfig { pub mode: NeighborhoodMode, - pub min_hops_count: Hops, + pub min_hops: Hops, } lazy_static! { @@ -417,10 +422,10 @@ pub struct NeighborhoodSubs { pub gossip_failure: Recipient>, pub dispatcher_node_query: Recipient, pub remove_neighbor: Recipient, + pub configuration_change_msg_sub: Recipient, pub stream_shutdown_sub: Recipient, - pub set_consuming_wallet_sub: Recipient, pub from_ui_message_sub: Recipient, - pub new_password_sub: Recipient, + pub new_password_sub: Recipient, // GH-728 pub connection_progress_sub: Recipient, } @@ -549,6 +554,17 @@ pub enum NRMetadataChange { AddUnreachableHost { hostname: String }, } +#[derive(Clone, Debug, Message, PartialEq, Eq)] +pub struct ConfigurationChangeMessage { + pub change: ConfigurationChange, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ConfigurationChange { + UpdateConsumingWallet(Wallet), + UpdateMinHops(Hops), +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[allow(non_camel_case_types)] pub enum GossipFailure_0v1 { @@ -576,7 +592,7 @@ impl fmt::Display for GossipFailure_0v1 { pub struct NeighborhoodMetadata { pub connection_progress_peers: Vec, pub cpm_recipient: Recipient, - pub min_hops_count: Hops, + pub db_patch_size: u8, } pub struct NeighborhoodTools { @@ -653,10 +669,10 @@ mod tests { gossip_failure: recipient!(recorder, ExpiredCoresPackage), dispatcher_node_query: recipient!(recorder, DispatcherNodeQueryMessage), remove_neighbor: recipient!(recorder, RemoveNeighborMessage), + configuration_change_msg_sub: recipient!(recorder, ConfigurationChangeMessage), stream_shutdown_sub: recipient!(recorder, StreamShutdownMsg), - set_consuming_wallet_sub: recipient!(recorder, SetConsumingWalletMessage), from_ui_message_sub: recipient!(recorder, NodeFromUiMessage), - new_password_sub: recipient!(recorder, NewPasswordMessage), + new_password_sub: recipient!(recorder, NewPasswordMessage), // GH-728 connection_progress_sub: recipient!(recorder, ConnectionProgressMessage), }; @@ -1248,7 +1264,7 @@ mod tests { } #[test] - fn min_hops_count_can_be_converted_from_str() { + fn valid_hops_can_be_converted_from_str() { assert_eq!(Hops::from_str("1").unwrap(), Hops::OneHop); assert_eq!(Hops::from_str("2").unwrap(), Hops::TwoHops); assert_eq!(Hops::from_str("3").unwrap(), Hops::ThreeHops); @@ -1258,12 +1274,22 @@ mod tests { } #[test] - fn min_hops_count_conversion_from_str_returns_error() { + fn invalid_hops_conversion_from_str_returns_error() { let result = Hops::from_str("100"); assert_eq!( result, - Err("Invalid value for min hops count provided".to_string()) + Err("Invalid value for min hops provided".to_string()) ) } + + #[test] + fn display_is_implemented_for_hops() { + assert_eq!(Hops::OneHop.to_string(), "1"); + assert_eq!(Hops::TwoHops.to_string(), "2"); + assert_eq!(Hops::ThreeHops.to_string(), "3"); + assert_eq!(Hops::FourHops.to_string(), "4"); + assert_eq!(Hops::FiveHops.to_string(), "5"); + assert_eq!(Hops::SixHops.to_string(), "6"); + } } diff --git a/node/src/sub_lib/proxy_server.rs b/node/src/sub_lib/proxy_server.rs index 5d3b242ae..8706d2943 100644 --- a/node/src/sub_lib/proxy_server.rs +++ b/node/src/sub_lib/proxy_server.rs @@ -8,7 +8,6 @@ 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; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_key::StreamKey; use crate::sub_lib::versioned_data::VersionedData; use actix::Message; @@ -79,7 +78,6 @@ pub struct ProxyServerSubs { pub add_return_route: Recipient, pub add_route: Recipient, pub stream_shutdown_sub: Recipient, - pub set_consuming_wallet_sub: Recipient, pub node_from_ui: Recipient, } @@ -111,7 +109,6 @@ mod tests { add_return_route: recipient!(recorder, AddReturnRouteMessage), add_route: recipient!(recorder, AddRouteMessage), stream_shutdown_sub: recipient!(recorder, StreamShutdownMsg), - set_consuming_wallet_sub: recipient!(recorder, SetConsumingWalletMessage), node_from_ui: recipient!(recorder, NodeFromUiMessage), }; diff --git a/node/src/sub_lib/set_consuming_wallet_message.rs b/node/src/sub_lib/set_consuming_wallet_message.rs deleted file mode 100644 index 186f4be77..000000000 --- a/node/src/sub_lib/set_consuming_wallet_message.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::sub_lib::wallet::Wallet; -use actix::Message; - -#[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct SetConsumingWalletMessage { - pub wallet: Wallet, -} diff --git a/node/src/sub_lib/utils.rs b/node/src/sub_lib/utils.rs index cad371e09..4b493cb5d 100644 --- a/node/src/sub_lib/utils.rs +++ b/node/src/sub_lib/utils.rs @@ -9,8 +9,6 @@ use masq_lib::multi_config::{MultiConfig, VirtualCommandLine}; use masq_lib::shared_schema::ConfiguratorError; use masq_lib::ui_gateway::NodeFromUiMessage; use masq_lib::utils::type_name_of; -#[cfg(test)] -use std::any::Any; use std::io::ErrorKind; use std::marker::PhantomData; use std::path::Path; @@ -151,7 +149,7 @@ where interval: Duration, ctx: &mut Context, ) -> Box; - declare_as_any!(); + as_any_in_trait!(); } #[derive(Default)] @@ -181,7 +179,7 @@ where let handle = ctx.notify_later(msg, interval); Box::new(NLSpawnHandleHolderReal::new(handle)) } - implement_as_any!(); + as_any_in_trait_impl!(); } pub trait NotifyHandle @@ -189,7 +187,7 @@ where A: Actor>, { fn notify<'a>(&'a self, msg: M, ctx: &'a mut Context); - declare_as_any!(); + as_any_in_trait!(); } impl Default for Box> @@ -236,7 +234,7 @@ where fn notify<'a>(&'a self, msg: M, ctx: &'a mut Context) { ctx.notify(msg) } - implement_as_any!(); + as_any_in_trait_impl!(); } pub fn db_connection_launch_panic(err: InitializationError, data_directory: &Path) -> ! { diff --git a/node/src/sub_lib/wallet.rs b/node/src/sub_lib/wallet.rs index 59898ee44..feb0667ec 100644 --- a/node/src/sub_lib/wallet.rs +++ b/node/src/sub_lib/wallet.rs @@ -1,5 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::blockchain::bip32::Bip32ECKeyProvider; +use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::payer::Payer; use crate::sub_lib::cryptde; use crate::sub_lib::cryptde::PublicKey as CryptdePublicKey; @@ -38,7 +38,7 @@ impl Display for WalletError { #[derive(Debug)] pub enum WalletKind { Address(Address), - SecretKey(Bip32ECKeyProvider), + SecretKey(Bip32EncryptionKeyProvider), PublicKey(PublicKey), Uninitialized, } @@ -48,7 +48,7 @@ impl Clone for WalletKind { match self { WalletKind::Address(address) => WalletKind::Address(H160(address.0)), WalletKind::SecretKey(keypair) => WalletKind::SecretKey( - Bip32ECKeyProvider::from_raw_secret(keypair.clone_secret().as_ref()) + Bip32EncryptionKeyProvider::from_raw_secret(keypair.clone_secret().as_ref()) .expect("failed to clone once checked secret"), ), WalletKind::PublicKey(public) => WalletKind::PublicKey( @@ -132,6 +132,12 @@ impl Wallet { } } + pub fn null() -> Self { + Wallet { + kind: WalletKind::Uninitialized, + } + } + pub fn string_address_from_keypair(&self) -> String { format!("{:#x}", self.address()) } @@ -233,8 +239,8 @@ impl From for Wallet { } } -impl From for Wallet { - fn from(keypair: Bip32ECKeyProvider) -> Self { +impl From for Wallet { + fn from(keypair: Bip32EncryptionKeyProvider) -> Self { Self { kind: WalletKind::SecretKey(keypair), } @@ -265,10 +271,10 @@ impl FromSql for Wallet { } } -impl TryInto for Wallet { +impl TryInto for Wallet { type Error = String; - fn try_into(self) -> Result { + fn try_into(self) -> Result { match self.kind { WalletKind::SecretKey(keypair) => Ok(keypair), _ => Err("Wallet contains no secret key: can't convert to Bip32KeyPair".to_string()), @@ -493,8 +499,11 @@ mod tests { let derivation_path = derivation_path(0, 5); let expected_seed = make_meaningless_seed(); let wallet = Wallet::from( - Bip32ECKeyProvider::try_from((expected_seed.as_bytes(), derivation_path.as_str())) - .unwrap(), + Bip32EncryptionKeyProvider::try_from(( + expected_seed.as_bytes(), + derivation_path.as_str(), + )) + .unwrap(), ); let result = wallet.string_address_from_keypair(); @@ -502,6 +511,18 @@ mod tests { assert_eq!(result, "0x28330c4b886fc83bd6e3409a9eae776c19403c2e") } + #[test] + fn null_wallet() { + let result = Wallet::null(); + + assert_eq!( + result, + Wallet { + kind: WalletKind::Uninitialized + } + ) + } + #[test] fn serialization_roundtrips_wallet_by_address_with_cbor_successfully() { let expected_wallet = make_wallet("A valid eth address!"); @@ -529,7 +550,7 @@ mod tests { ) .unwrap(); let seed = Seed::new(&mnemonic, "Test123!"); - let keypair = Bip32ECKeyProvider::try_from(( + let keypair = Bip32EncryptionKeyProvider::try_from(( seed.as_ref(), DEFAULT_CONSUMING_DERIVATION_PATH.as_str(), )) @@ -552,7 +573,7 @@ mod tests { ) .unwrap(); let seed = Seed::new(&mnemonic, "Test123!"); - let keypair = Bip32ECKeyProvider::try_from(( + let keypair = Bip32EncryptionKeyProvider::try_from(( seed.as_ref(), DEFAULT_CONSUMING_DERIVATION_PATH.as_str(), )) @@ -647,15 +668,17 @@ mod tests { #[test] fn can_convert_to_keypair_if_came_from_keypair() { let secret_key_text = "0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc"; - let keypair = - Bip32ECKeyProvider::from_raw_secret(&secret_key_text.from_hex::>().unwrap()) - .unwrap(); - let expected_keypair = - Bip32ECKeyProvider::from_raw_secret(&secret_key_text.from_hex::>().unwrap()) - .unwrap(); + let keypair = Bip32EncryptionKeyProvider::from_raw_secret( + &secret_key_text.from_hex::>().unwrap(), + ) + .unwrap(); + let expected_keypair = Bip32EncryptionKeyProvider::from_raw_secret( + &secret_key_text.from_hex::>().unwrap(), + ) + .unwrap(); let subject = Wallet::from(keypair); - let result: Bip32ECKeyProvider = subject.try_into().unwrap(); + let result: Bip32EncryptionKeyProvider = subject.try_into().unwrap(); assert_eq!(result, expected_keypair); } @@ -664,7 +687,7 @@ mod tests { fn cant_convert_to_keypair_if_didnt_come_from_keypair() { let subject = Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(); - let result: Result = subject.try_into(); + let result: Result = subject.try_into(); assert_eq!( result, @@ -698,14 +721,14 @@ mod tests { } } - fn keypair_a() -> Bip32ECKeyProvider { + fn keypair_a() -> Bip32EncryptionKeyProvider { let numbers = (0u8..32u8).collect::>(); - Bip32ECKeyProvider::from_raw_secret(&numbers).unwrap() + Bip32EncryptionKeyProvider::from_raw_secret(&numbers).unwrap() } - fn keypair_b() -> Bip32ECKeyProvider { + fn keypair_b() -> Bip32EncryptionKeyProvider { let numbers = (1u8..33u8).collect::>(); - Bip32ECKeyProvider::from_raw_secret(&numbers).unwrap() + Bip32EncryptionKeyProvider::from_raw_secret(&numbers).unwrap() } fn hash(wallet: &Wallet) -> u64 { diff --git a/node/src/test_utils/actor_system_factory.rs b/node/src/test_utils/actor_system_factory.rs new file mode 100644 index 000000000..ba0baeccc --- /dev/null +++ b/node/src/test_utils/actor_system_factory.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::db_access_objects::banned_dao::BannedCacheLoader; +use crate::database::connection_wrapper::ConnectionWrapper; +use std::sync::{Arc, Mutex}; + +#[derive(Default)] +pub struct BannedCacheLoaderMock { + pub load_params: Arc>>>, +} + +impl BannedCacheLoader for BannedCacheLoaderMock { + fn load(&self, conn: Box) { + self.load_params.lock().unwrap().push(conn); + } +} diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index f63d7682a..ac64b0681 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -2,14 +2,14 @@ #![cfg(test)] -use crate::accountant::database_access_objects::utils::VigilantRusqliteFlatten; +use crate::accountant::db_access_objects::utils::VigilantRusqliteFlatten; use crate::database::connection_wrapper::ConnectionWrapper; use crate::database::db_initializer::ExternalData; use crate::database::db_migrations::db_migrator::DbMigrator; use masq_lib::logger::Logger; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; -use masq_lib::utils::NeighborhoodModeLight; +use masq_lib::utils::{to_string, NeighborhoodModeLight}; use rusqlite::{Connection, Error}; use std::cell::RefCell; use std::env::current_dir; @@ -207,7 +207,7 @@ fn prepare_expected_vectors_of_words_including_sorting( .map(|slice_of_strs| { let mut one_line = slice_of_strs .into_iter() - .map(|word| word.to_string()) + .map(to_string) .collect::>(); one_line.sort(); one_line @@ -233,7 +233,7 @@ fn parse_sql_to_pieces(sql: &str) -> SQLLinesChoppedIntoWords { let mut vec_of_words = sql_line .split(|char: char| char.is_whitespace()) .filter(|chunk| !chunk.is_empty()) - .map(|chunk| chunk.to_string()) + .map(to_string) .collect::>(); vec_of_words.sort(); vec_of_words diff --git a/node/src/test_utils/http_test_server.rs b/node/src/test_utils/http_test_server.rs new file mode 100644 index 000000000..56e0daddf --- /dev/null +++ b/node/src/test_utils/http_test_server.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crossbeam_channel::{unbounded, Receiver}; +use simple_server::{Request, Server}; +use std::io::Write; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}; +use std::ops::Add; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +pub struct TestServer { + port: u16, + rx: Receiver>>, +} + +impl Drop for TestServer { + fn drop(&mut self) { + self.stop(); + } +} + +impl TestServer { + pub fn start(port: u16, bodies: Vec>) -> Self { + std::env::set_var("SIMPLESERVER_THREADS", "1"); + let (tx, rx) = unbounded(); + let _ = thread::spawn(move || { + let bodies_arc = Arc::new(Mutex::new(bodies)); + Server::new(move |req, mut rsp| { + if req.headers().get("X-Quit").is_some() { + panic!("Server stop requested"); + } + tx.send(req).unwrap(); + let body = bodies_arc.lock().unwrap().remove(0); + Ok(rsp.body(body)?) + }) + .listen(&Ipv4Addr::LOCALHOST.to_string(), &format!("{}", port)); + }); + let deadline = Instant::now().add(Duration::from_secs(5)); + loop { + thread::sleep(Duration::from_millis(10)); + match TcpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) { + Ok(_) => break, + Err(e) => eprintln!("No: {:?}", e), + } + if Instant::now().gt(&deadline) { + panic!("TestServer still not started after 5sec"); + } + } + TestServer { port, rx } + } + + pub fn requests_so_far(&self) -> Vec>> { + let mut requests = vec![]; + while let Ok(request) = self.rx.try_recv() { + requests.push(request); + } + return requests; + } + + fn stop(&mut self) { + let mut stream = + match TcpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), self.port)) { + Ok(s) => s, + Err(_) => return, + }; + stream + .write(b"DELETE /irrelevant.htm HTTP/1.1\r\nX-Quit: Yes") + .unwrap(); + } +} diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 9cf5f2ae8..f5cad813d 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -2,10 +2,12 @@ #[macro_use] pub mod channel_wrapper_mocks; +pub mod actor_system_factory; pub mod automap_mocks; pub mod data_hunk; pub mod data_hunk_framer; pub mod database_utils; +pub mod http_test_server; pub mod little_tcp_server; pub mod logfile_name_guard; pub mod neighborhood_test_utils; @@ -15,7 +17,8 @@ pub mod recorder_stop_conditions; pub mod stream_connector_mock; pub mod tcp_wrapper_mocks; pub mod tokio_wrapper_mocks; -use crate::blockchain::bip32::Bip32ECKeyProvider; + +use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::payer::Payer; use crate::bootstrapper::CryptDEPair; use crate::sub_lib::cryptde::CryptDE; @@ -511,7 +514,8 @@ pub fn make_payer(secret: &[u8], public_key: &PublicKey) -> Payer { pub fn make_paying_wallet(secret: &[u8]) -> Wallet { let digest = secret.keccak256(); Wallet::from( - Bip32ECKeyProvider::from_raw_secret(&digest).expect("Invalid Secret for Bip32ECKeyPair"), + Bip32EncryptionKeyProvider::from_raw_secret(&digest) + .expect("Invalid Secret for Bip32ECKeyPair"), ) } @@ -539,6 +543,14 @@ pub struct TestRawTransaction { pub data: Vec, } +#[macro_export] +macro_rules! arbitrary_id_stamp_in_trait { + () => { + #[cfg(test)] + $crate::arbitrary_id_stamp_in_trait_internal___!(); + }; +} + #[cfg(test)] pub mod unshared_test_utils { use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; @@ -555,6 +567,7 @@ pub mod unshared_test_utils { NLSpawnHandleHolder, NLSpawnHandleHolderReal, NotifyHandle, NotifyLaterHandle, }; use crate::test_utils::database_utils::bring_db_0_back_to_life_and_return_connection; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::{make_recorder, Recorder, Recording}; use crate::test_utils::recorder_stop_conditions::{StopCondition, StopConditions}; @@ -681,6 +694,7 @@ pub mod unshared_test_utils { .past_neighbors_result(Ok(None)) .gas_price_result(Ok(1)) .blockchain_service_url_result(Ok(None)) + .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) } pub fn default_persistent_config_just_accountant_config( @@ -886,7 +900,7 @@ pub mod unshared_test_utils { self } - pub fn permit_to_send_out(mut self) -> Self { + pub fn capture_msg_and_let_it_fly_on(mut self) -> Self { self.send_message_out = true; self } @@ -966,6 +980,7 @@ pub mod unshared_test_utils { pub mod arbitrary_id_stamp { use super::*; + use crate::arbitrary_id_stamp_in_trait; //The issues we are to solve might look as follows: @@ -997,27 +1012,31 @@ pub mod unshared_test_utils { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ArbitraryIdStamp { - id: usize, + id_opt: Option, } impl ArbitraryIdStamp { pub fn new() -> Self { + let mut access = ARBITRARY_ID_STAMP_SEQUENCER.lock().unwrap(); + access.0 += 1; ArbitraryIdStamp { - id: { - let mut access = ARBITRARY_ID_STAMP_SEQUENCER.lock().unwrap(); - access.0 += 1; - access.0 - }, + id_opt: Some(access.0), } } + + pub fn null() -> Self { + ArbitraryIdStamp { id_opt: None } + } } // To be added together with other methods in your trait + // DO NOT USE ME DIRECTLY, USE arbitrary_id_stamp_in_trait INSTEAD! #[macro_export] - macro_rules! arbitrary_id_stamp_in_trait { + macro_rules! arbitrary_id_stamp_in_trait_internal___ { () => { - #[cfg(test)] - fn arbitrary_id_stamp(&self) -> ArbitraryIdStamp { + fn arbitrary_id_stamp( + &self, + ) -> crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp { // No necessity to implement this method for all impls, // basically you want to do that just for the mock version @@ -1040,7 +1059,23 @@ pub mod unshared_test_utils { macro_rules! arbitrary_id_stamp_in_trait_impl { () => { fn arbitrary_id_stamp(&self) -> ArbitraryIdStamp { - *self.arbitrary_id_stamp_opt.as_ref().unwrap() + match self.arbitrary_id_stamp_opt { + Some(id) => id, + // In some implementations of mocks that have methods demanding args, the best we can do in order to + // capture and examine these args in assertions is to receive the ArbitraryIdStamp of the given + // argument. + // If such strategy is once decided for, transfers of this id will have to happen in all the tests + // relying on this mock, while also calling the intended method. So even in cases where we certainly + // are not really interested in checking that id, if we ignored that, the call of this method would + // blow up because the field that stores it is likely optional, with the value defaulted to None. + // + // As prevention of confusion from putting a requirement on devs to set the id stamp even though + // they're not planning to use it, we have a null type of that stamp to be there at most cases. + // As a result, we don't risk a direct punishment (for the None value being the problem) but also + // we'll set the assertion on fire if it doesn't match the expected id in tests where we suddenly + // do care + None => ArbitraryIdStamp::null(), + } } }; } @@ -1174,6 +1209,28 @@ pub mod unshared_test_utils { } } } + + pub struct SubsFactoryTestAddrLeaker + where + A: actix::Actor, + { + pub address_leaker: Sender>, + } + + impl SubsFactoryTestAddrLeaker + where + A: actix::Actor, + { + pub fn send_leaker_msg_and_return_meaningless_subs( + &self, + addr: &Addr, + make_subs_from_recorder_fn: fn(&Addr) -> S, + ) -> S { + self.address_leaker.try_send(addr.clone()).unwrap(); + let meaningless_addr = Recorder::new().start(); + make_subs_from_recorder_fn(&meaningless_addr) + } + } } #[cfg(test)] diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index e85ccc4cf..25dfeeadb 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -3,7 +3,7 @@ use crate::bootstrapper::BootstrapperConfig; use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord, Gossip_0v1}; use crate::neighborhood::neighborhood_database::NeighborhoodDatabase; use crate::neighborhood::node_record::{NodeRecord, NodeRecordInner_0v1}; -use crate::neighborhood::{AccessibleGossipRecord, Neighborhood, DEFAULT_MIN_HOPS_COUNT}; +use crate::neighborhood::{AccessibleGossipRecord, Neighborhood, DEFAULT_MIN_HOPS}; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::cryptde::{CryptDE, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; @@ -18,7 +18,8 @@ use std::convert::TryFrom; use std::net::IpAddr; use std::net::Ipv4Addr; -pub const MIN_HOPS_COUNT_FOR_TEST: Hops = DEFAULT_MIN_HOPS_COUNT; +pub const MIN_HOPS_FOR_TEST: Hops = DEFAULT_MIN_HOPS; +pub const DB_PATCH_SIZE_FOR_TEST: u8 = DEFAULT_MIN_HOPS as u8; impl From<(&NeighborhoodDatabase, &PublicKey, bool)> for AccessibleGossipRecord { fn from( @@ -97,11 +98,11 @@ pub fn neighborhood_from_nodes( vec![NodeDescriptor::from((neighbor, Chain::EthRopsten, cryptde))], *root.rate_pack(), ), - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, None => NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, - min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + min_hops: MIN_HOPS_FOR_TEST, }, }; config.earning_wallet = root.earning_wallet(); diff --git a/node/src/test_utils/persistent_configuration_mock.rs b/node/src/test_utils/persistent_configuration_mock.rs index b831d1094..e93ce308c 100644 --- a/node/src/test_utils/persistent_configuration_mock.rs +++ b/node/src/test_utils/persistent_configuration_mock.rs @@ -4,7 +4,7 @@ use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration}; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; -use crate::sub_lib::neighborhood::{NodeDescriptor, RatePack}; +use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; @@ -12,6 +12,7 @@ use masq_lib::utils::AutomapProtocol; use masq_lib::utils::NeighborhoodModeLight; use std::cell::RefCell; use std::sync::{Arc, Mutex}; +use std::u64; #[allow(clippy::type_complexity)] #[derive(Clone, Default)] @@ -44,6 +45,9 @@ pub struct PersistentConfigurationMock { mapping_protocol_results: RefCell, PersistentConfigError>>>, set_mapping_protocol_params: Arc>>>, set_mapping_protocol_results: RefCell>>, + min_hops_results: RefCell>>, + set_min_hops_params: Arc>>, + set_min_hops_results: RefCell>>, neighborhood_mode_results: RefCell>>, set_neighborhood_mode_params: Arc>>, set_neighborhood_mode_results: RefCell>>, @@ -56,6 +60,10 @@ pub struct PersistentConfigurationMock { start_block_results: RefCell>>, set_start_block_params: Arc>>, set_start_block_results: RefCell>>, + max_block_count_params: Arc>>, + max_block_count_results: RefCell, PersistentConfigError>>>, + set_max_block_count_params: Arc>>>, + set_max_block_count_results: RefCell>>, payment_thresholds_results: RefCell>>, set_payment_thresholds_params: Arc>>, set_payment_thresholds_results: RefCell>>, @@ -172,6 +180,15 @@ impl PersistentConfiguration for PersistentConfigurationMock { self.set_mapping_protocol_results.borrow_mut().remove(0) } + fn min_hops(&self) -> Result { + self.min_hops_results.borrow_mut().remove(0) + } + + fn set_min_hops(&mut self, value: Hops) -> Result<(), PersistentConfigError> { + self.set_min_hops_params.lock().unwrap().push(value); + self.set_min_hops_results.borrow_mut().remove(0) + } + fn neighborhood_mode(&self) -> Result { self.neighborhood_mode_results.borrow_mut().remove(0) } @@ -220,6 +237,16 @@ impl PersistentConfiguration for PersistentConfigurationMock { Self::result_from(&self.set_start_block_results) } + fn max_block_count(&self) -> Result, PersistentConfigError> { + self.max_block_count_params.lock().unwrap().push(()); + Self::result_from(&self.max_block_count_results) + } + + fn set_max_block_count(&mut self, value: Option) -> Result<(), PersistentConfigError> { + self.set_max_block_count_params.lock().unwrap().push(value); + Self::result_from(&self.set_max_block_count_results) + } + fn set_wallet_info( &mut self, consuming_wallet_private_key: &str, @@ -300,7 +327,7 @@ impl PersistentConfigurationMock { self } - pub fn current_schema_version_result(self, result: &str) -> PersistentConfigurationMock { + pub fn current_schema_version_result(self, result: &str) -> Self { self.current_schema_version_results .borrow_mut() .push(result.to_string()); @@ -321,63 +348,66 @@ impl PersistentConfigurationMock { pub fn change_password_params( mut self, params: &Arc, String)>>>, - ) -> PersistentConfigurationMock { + ) -> Self { self.change_password_params = params.clone(); self } - pub fn change_password_result( - self, - result: Result<(), PersistentConfigError>, - ) -> PersistentConfigurationMock { + pub fn change_password_result(self, result: Result<(), PersistentConfigError>) -> Self { self.change_password_results.borrow_mut().push(result); self } - pub fn check_password_params( - mut self, - params: &Arc>>>, - ) -> PersistentConfigurationMock { + pub fn check_password_params(mut self, params: &Arc>>>) -> Self { self.check_password_params = params.clone(); self } - pub fn check_password_result( - self, - result: Result, - ) -> PersistentConfigurationMock { + pub fn check_password_result(self, result: Result) -> Self { self.check_password_results.borrow_mut().push(result); self } - pub fn clandestine_port_result( - self, - result: Result, - ) -> PersistentConfigurationMock { + pub fn clandestine_port_result(self, result: Result) -> Self { self.clandestine_port_results.borrow_mut().push(result); self } - pub fn set_clandestine_port_params( + pub fn set_clandestine_port_params(mut self, params: &Arc>>) -> Self { + self.set_clandestine_port_params = params.clone(); + self + } + + pub fn set_clandestine_port_result(self, result: Result<(), PersistentConfigError>) -> Self { + self.set_clandestine_port_results.borrow_mut().push(result); + self + } + + pub fn min_hops_result(self, result: Result) -> Self { + self.min_hops_results.borrow_mut().push(result); + self + } + + pub fn set_min_hops_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> PersistentConfigurationMock { - self.set_clandestine_port_params = params.clone(); + self.set_min_hops_params = params.clone(); self } - pub fn set_clandestine_port_result( + pub fn set_min_hops_result( self, result: Result<(), PersistentConfigError>, ) -> PersistentConfigurationMock { - self.set_clandestine_port_results.borrow_mut().push(result); + self.set_min_hops_results.borrow_mut().push(result); self } pub fn neighborhood_mode_result( self, result: Result, - ) -> PersistentConfigurationMock { + ) -> Self { self.neighborhood_mode_results.borrow_mut().push(result); self } @@ -385,23 +415,17 @@ impl PersistentConfigurationMock { pub fn set_neighborhood_mode_params( mut self, params: &Arc>>, - ) -> PersistentConfigurationMock { + ) -> Self { self.set_neighborhood_mode_params = params.clone(); self } - pub fn set_neighborhood_mode_result( - self, - result: Result<(), PersistentConfigError>, - ) -> PersistentConfigurationMock { + pub fn set_neighborhood_mode_result(self, result: Result<(), PersistentConfigError>) -> Self { self.set_neighborhood_mode_results.borrow_mut().push(result); self } - pub fn consuming_wallet_params( - mut self, - params: &Arc>>, - ) -> PersistentConfigurationMock { + pub fn consuming_wallet_params(mut self, params: &Arc>>) -> Self { self.consuming_wallet_params = params.clone(); self } @@ -409,15 +433,12 @@ impl PersistentConfigurationMock { pub fn consuming_wallet_result( self, result: Result, PersistentConfigError>, - ) -> PersistentConfigurationMock { + ) -> Self { self.consuming_wallet_results.borrow_mut().push(result); self } - pub fn consuming_wallet_private_key_params( - mut self, - params: &Arc>>, - ) -> PersistentConfigurationMock { + pub fn consuming_wallet_private_key_params(mut self, params: &Arc>>) -> Self { self.consuming_wallet_private_key_params = params.clone(); self } @@ -425,7 +446,7 @@ impl PersistentConfigurationMock { pub fn consuming_wallet_private_key_result( self, result: Result, PersistentConfigError>, - ) -> PersistentConfigurationMock { + ) -> Self { self.consuming_wallet_private_key_results .borrow_mut() .push(result); @@ -436,7 +457,7 @@ impl PersistentConfigurationMock { pub fn set_wallet_info_params( mut self, params: &Arc>>, - ) -> PersistentConfigurationMock { + ) -> Self { self.set_wallet_info_params = params.clone(); self } @@ -451,10 +472,7 @@ impl PersistentConfigurationMock { self } - pub fn set_gas_price_params( - mut self, - params: &Arc>>, - ) -> PersistentConfigurationMock { + pub fn set_gas_price_params(mut self, params: &Arc>>) -> Self { self.set_gas_price_params = params.clone(); self } @@ -464,10 +482,7 @@ impl PersistentConfigurationMock { self } - pub fn past_neighbors_params( - mut self, - params: &Arc>>, - ) -> PersistentConfigurationMock { + pub fn past_neighbors_params(mut self, params: &Arc>>) -> Self { self.past_neighbors_params = params.clone(); self } @@ -475,7 +490,7 @@ impl PersistentConfigurationMock { pub fn past_neighbors_result( self, result: Result>, PersistentConfigError>, - ) -> PersistentConfigurationMock { + ) -> Self { self.past_neighbors_results.borrow_mut().push(result); self } @@ -484,15 +499,12 @@ impl PersistentConfigurationMock { pub fn set_past_neighbors_params( mut self, params: &Arc>, String)>>>, - ) -> PersistentConfigurationMock { + ) -> Self { self.set_past_neighbors_params = params.clone(); self } - pub fn set_past_neighbors_result( - self, - result: Result<(), PersistentConfigError>, - ) -> PersistentConfigurationMock { + pub fn set_past_neighbors_result(self, result: Result<(), PersistentConfigError>) -> Self { self.set_past_neighbors_results.borrow_mut().push(result); self } @@ -500,7 +512,7 @@ impl PersistentConfigurationMock { pub fn earning_wallet_result( self, result: Result, PersistentConfigError>, - ) -> PersistentConfigurationMock { + ) -> Self { self.earning_wallet_results.borrow_mut().push(result); self } @@ -508,7 +520,7 @@ impl PersistentConfigurationMock { pub fn earning_wallet_address_result( self, result: Result, PersistentConfigError>, - ) -> PersistentConfigurationMock { + ) -> Self { self.earning_wallet_address_results .borrow_mut() .push(result); @@ -525,10 +537,7 @@ impl PersistentConfigurationMock { self } - pub fn set_start_block_params( - mut self, - params: &Arc>>, - ) -> PersistentConfigurationMock { + pub fn set_start_block_params(mut self, params: &Arc>>) -> Self { self.set_start_block_params = params.clone(); self } @@ -538,6 +547,29 @@ impl PersistentConfigurationMock { self } + pub fn max_block_count_params(mut self, params: &Arc>>) -> Self { + self.max_block_count_params = params.clone(); + self + } + + pub fn max_block_count_result( + self, + result: Result, PersistentConfigError>, + ) -> Self { + self.max_block_count_results.borrow_mut().push(result); + self + } + + pub fn set_max_block_count_params(mut self, params: &Arc>>>) -> Self { + self.set_max_block_count_params = params.clone(); + self + } + + pub fn set_max_block_count_result(self, result: Result<(), PersistentConfigError>) -> Self { + self.set_max_block_count_results.borrow_mut().push(result); + self + } + pub fn payment_thresholds_result( self, result: Result, diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 92a29fb2d..22566cbdc 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -1,6 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] -use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; + +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; use crate::accountant::ReportTransactionReceipts; use crate::accountant::{ ReceivedPayments, RequestTransactionReceipts, ScanError, ScanForPayables, @@ -17,15 +19,17 @@ use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::blockchain_bridge::BlockchainBridgeSubs; -use crate::sub_lib::blockchain_bridge::OutcomingPaymentsInstructions; -use crate::sub_lib::configurator::{ConfiguratorSubs, NewPasswordMessage}; +use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +use crate::sub_lib::configurator::NewPasswordMessage; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{DispatcherSubs, StreamShutdownMsg}; use crate::sub_lib::hopper::IncipientCoresPackage; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{HopperSubs, MessageType}; -use crate::sub_lib::neighborhood::ConnectionProgressMessage; use crate::sub_lib::neighborhood::NeighborhoodSubs; +use crate::sub_lib::neighborhood::{ConfigurationChangeMessage, ConnectionProgressMessage}; + +use crate::sub_lib::configurator::ConfiguratorSubs; use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; @@ -40,12 +44,13 @@ use crate::sub_lib::proxy_server::ProxyServerSubs; use crate::sub_lib::proxy_server::{ AddReturnRouteMessage, AddRouteMessage, ClientRequestPayload_0v1, }; -use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::ui_gateway::UiGatewaySubs; use crate::sub_lib::utils::MessageScheduler; -use crate::test_utils::recorder_stop_conditions::StopConditions; +use crate::test_utils::recorder_stop_conditions::{ + ForcedMatchable, PretendedMatchableWrapper, StopConditions, +}; use crate::test_utils::to_millis; use crate::test_utils::unshared_test_utils::system_killer_actor::SystemKillerActor; use actix::Addr; @@ -55,7 +60,7 @@ use actix::MessageResult; use actix::System; use actix::{Actor, Message}; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; -use std::any::Any; +use std::any::{Any, TypeId}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; @@ -82,65 +87,91 @@ impl Actor for Recorder { type Context = Context; } -macro_rules! recorder_message_handler { - ($message_type: ty) => { +macro_rules! message_handler_common { + ($message_type: ty, $handling_fn: ident) => { impl Handler<$message_type> for Recorder { type Result = (); fn handle(&mut self, msg: $message_type, _ctx: &mut Self::Context) { - self.handle_msg(msg) + self.$handling_fn(msg) + } + } + }; +} + +macro_rules! matchable { + ($message_type: ty) => { + impl ForcedMatchable<$message_type> for $message_type { + fn correct_msg_type_id(&self) -> TypeId { + TypeId::of::<$message_type>() } } }; } -recorder_message_handler!(AddReturnRouteMessage); -recorder_message_handler!(AddRouteMessage); -recorder_message_handler!(AddStreamMsg); -recorder_message_handler!(BindMessage); -recorder_message_handler!(CrashNotification); -recorder_message_handler!(DaemonBindMessage); -recorder_message_handler!(DispatcherNodeQueryMessage); -recorder_message_handler!(DispatcherNodeQueryResponse); -recorder_message_handler!(DnsResolveFailure_0v1); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(ExpiredCoresPackage); -recorder_message_handler!(InboundClientData); -recorder_message_handler!(InboundServerData); -recorder_message_handler!(IncipientCoresPackage); -recorder_message_handler!(NewPasswordMessage); -recorder_message_handler!(NewPublicIp); -recorder_message_handler!(NodeFromUiMessage); -recorder_message_handler!(NodeToUiMessage); -recorder_message_handler!(UpdateNodeRecordMetadataMessage); -recorder_message_handler!(NoLookupIncipientCoresPackage); -recorder_message_handler!(PoolBindMessage); -recorder_message_handler!(ReceivedPayments); -recorder_message_handler!(RemoveNeighborMessage); -recorder_message_handler!(RemoveStreamMsg); -recorder_message_handler!(ReportServicesConsumedMessage); -recorder_message_handler!(ReportExitServiceProvidedMessage); -recorder_message_handler!(ReportRoutingServiceProvidedMessage); -recorder_message_handler!(ScanError); -recorder_message_handler!(PayablePaymentSetup); -recorder_message_handler!(SentPayables); -recorder_message_handler!(SetConsumingWalletMessage); -recorder_message_handler!(StartMessage); -recorder_message_handler!(StreamShutdownMsg); -recorder_message_handler!(TransmitDataMsg); -recorder_message_handler!(PendingPayableFingerprintSeeds); -recorder_message_handler!(RetrieveTransactions); -recorder_message_handler!(RequestTransactionReceipts); -recorder_message_handler!(ReportTransactionReceipts); -recorder_message_handler!(OutcomingPaymentsInstructions); -recorder_message_handler!(ScanForReceivables); -recorder_message_handler!(ScanForPayables); -recorder_message_handler!(ConnectionProgressMessage); -recorder_message_handler!(ScanForPendingPayables); +// t, m, p (type, match, predicate) represents a list of the possible system stop conditions + +macro_rules! recorder_message_handler_t_m_p { + ($message_type: ty) => { + message_handler_common!($message_type, handle_msg_t_m_p); + matchable!($message_type); + }; +} + +macro_rules! recorder_message_handler_t_p { + ($message_type: ty) => { + message_handler_common!($message_type, handle_msg_t_p); + }; +} + +recorder_message_handler_t_m_p!(AddReturnRouteMessage); +recorder_message_handler_t_m_p!(AddRouteMessage); +recorder_message_handler_t_p!(AddStreamMsg); +recorder_message_handler_t_m_p!(BindMessage); +recorder_message_handler_t_p!(BlockchainAgentWithContextMessage); +recorder_message_handler_t_m_p!(ConfigurationChangeMessage); +recorder_message_handler_t_m_p!(ConnectionProgressMessage); +recorder_message_handler_t_m_p!(CrashNotification); +recorder_message_handler_t_m_p!(DaemonBindMessage); +recorder_message_handler_t_m_p!(DispatcherNodeQueryMessage); +recorder_message_handler_t_m_p!(DispatcherNodeQueryResponse); +recorder_message_handler_t_m_p!(DnsResolveFailure_0v1); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(ExpiredCoresPackage); +recorder_message_handler_t_m_p!(InboundClientData); +recorder_message_handler_t_m_p!(InboundServerData); +recorder_message_handler_t_m_p!(IncipientCoresPackage); +recorder_message_handler_t_m_p!(NewPasswordMessage); // GH-728 +recorder_message_handler_t_m_p!(NewPublicIp); +recorder_message_handler_t_m_p!(NodeFromUiMessage); +recorder_message_handler_t_m_p!(NodeToUiMessage); +recorder_message_handler_t_m_p!(NoLookupIncipientCoresPackage); +recorder_message_handler_t_p!(OutboundPaymentsInstructions); +recorder_message_handler_t_m_p!(PendingPayableFingerprintSeeds); +recorder_message_handler_t_m_p!(PoolBindMessage); +recorder_message_handler_t_m_p!(QualifiedPayablesMessage); +recorder_message_handler_t_m_p!(ReceivedPayments); +recorder_message_handler_t_m_p!(RemoveNeighborMessage); +recorder_message_handler_t_m_p!(RemoveStreamMsg); +recorder_message_handler_t_m_p!(ReportExitServiceProvidedMessage); +recorder_message_handler_t_m_p!(ReportRoutingServiceProvidedMessage); +recorder_message_handler_t_m_p!(ReportServicesConsumedMessage); +recorder_message_handler_t_m_p!(ReportTransactionReceipts); +recorder_message_handler_t_m_p!(RequestTransactionReceipts); +recorder_message_handler_t_m_p!(RetrieveTransactions); +recorder_message_handler_t_m_p!(ScanError); +recorder_message_handler_t_m_p!(ScanForPayables); +recorder_message_handler_t_m_p!(ScanForPendingPayables); +recorder_message_handler_t_m_p!(ScanForReceivables); +recorder_message_handler_t_m_p!(SentPayables); +recorder_message_handler_t_m_p!(StartMessage); +recorder_message_handler_t_m_p!(StreamShutdownMsg); +recorder_message_handler_t_m_p!(TransmitDataMsg); +recorder_message_handler_t_m_p!(UpdateNodeRecordMetadataMessage); impl Handler> for Recorder where @@ -149,7 +180,17 @@ where type Result = (); fn handle(&mut self, msg: MessageScheduler, _ctx: &mut Self::Context) { - self.handle_msg(msg) + self.handle_msg_t_m_p(msg) + } +} + +impl ForcedMatchable for MessageScheduler +where + OuterM: PartialEq + 'static, + InnerM: PartialEq + Send + Message, +{ + fn correct_msg_type_id(&self) -> TypeId { + TypeId::of::() } } @@ -230,9 +271,12 @@ impl Recorder { system_killer.start(); } - fn handle_msg(&mut self, msg: T) { + fn handle_msg_t_m_p(&mut self, msg: M) + where + M: 'static + ForcedMatchable + Send, + { let kill_system = if let Some(stop_conditions) = &mut self.stop_conditions_opt { - stop_conditions.resolve_stop_conditions::(&msg) + stop_conditions.resolve_stop_conditions::(&msg) } else { false }; @@ -243,6 +287,14 @@ impl Recorder { System::current().stop() } } + + //for messages that cannot implement PartialEq + fn handle_msg_t_p(&mut self, msg: M) + where + M: 'static + Send, + { + self.handle_msg_t_m_p(PretendedMatchableWrapper(msg)) + } } impl Recording { @@ -278,7 +330,7 @@ impl Recording { self.get_record_inner_body(index).ok() } - fn get_record_inner_body(&self, index: usize) -> Result<&T, String> { + fn get_record_inner_body(&self, index: usize) -> Result<&T, String> { let item_box = match self.messages.get(index) { Some(item_box) => item_box, None => { @@ -289,14 +341,20 @@ impl Recording { )) } }; - let item_opt = item_box.downcast_ref::(); - - match item_opt { + match item_box.downcast_ref::() { Some(item) => Ok(item), - None => Err(format!( - "Message {:?} could not be downcast to the expected type", - item_box - )), + None => { + // double-checking for an uncommon, yet possible other type of an actor message, which doesn't implement PartialEq + let item_opt = item_box.downcast_ref::>(); + + match item_opt { + Some(item) => Ok(&item.0), + None => Err(format!( + "Message {:?} could not be downcast to the expected type", + item_box + )), + } + } } } } @@ -334,7 +392,7 @@ pub fn make_recorder() -> (Recorder, RecordAwaiter, Arc>) { (recorder, awaiter, recording) } -pub fn make_proxy_server_subs_from(addr: &Addr) -> ProxyServerSubs { +pub fn make_proxy_server_subs_from_recorder(addr: &Addr) -> ProxyServerSubs { ProxyServerSubs { bind: recipient!(addr, BindMessage), from_dispatcher: recipient!(addr, InboundClientData), @@ -343,12 +401,11 @@ pub fn make_proxy_server_subs_from(addr: &Addr) -> ProxyServerSubs { add_return_route: recipient!(addr, AddReturnRouteMessage), add_route: recipient!(addr, AddRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), - set_consuming_wallet_sub: recipient!(addr, SetConsumingWalletMessage), node_from_ui: recipient!(addr, NodeFromUiMessage), } } -pub fn make_dispatcher_subs_from(addr: &Addr) -> DispatcherSubs { +pub fn make_dispatcher_subs_from_recorder(addr: &Addr) -> DispatcherSubs { DispatcherSubs { ibcd_sub: recipient!(addr, InboundClientData), bind: recipient!(addr, BindMessage), @@ -359,7 +416,7 @@ pub fn make_dispatcher_subs_from(addr: &Addr) -> DispatcherSubs { } } -pub fn make_hopper_subs_from(addr: &Addr) -> HopperSubs { +pub fn make_hopper_subs_from_recorder(addr: &Addr) -> HopperSubs { HopperSubs { bind: recipient!(addr, BindMessage), from_hopper_client: recipient!(addr, IncipientCoresPackage), @@ -369,7 +426,7 @@ pub fn make_hopper_subs_from(addr: &Addr) -> HopperSubs { } } -pub fn make_proxy_client_subs_from(addr: &Addr) -> ProxyClientSubs { +pub fn make_proxy_client_subs_from_recorder(addr: &Addr) -> ProxyClientSubs { ProxyClientSubs { bind: recipient!(addr, BindMessage), from_hopper: recipient!(addr, ExpiredCoresPackage), @@ -379,7 +436,7 @@ pub fn make_proxy_client_subs_from(addr: &Addr) -> ProxyClientSubs { } } -pub fn make_neighborhood_subs_from(addr: &Addr) -> NeighborhoodSubs { +pub fn make_neighborhood_subs_from_recorder(addr: &Addr) -> NeighborhoodSubs { NeighborhoodSubs { bind: recipient!(addr, BindMessage), start: recipient!(addr, StartMessage), @@ -390,10 +447,10 @@ pub fn make_neighborhood_subs_from(addr: &Addr) -> NeighborhoodSubs { gossip_failure: recipient!(addr, ExpiredCoresPackage), dispatcher_node_query: recipient!(addr, DispatcherNodeQueryMessage), remove_neighbor: recipient!(addr, RemoveNeighborMessage), + configuration_change_msg_sub: recipient!(addr, ConfigurationChangeMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), - set_consuming_wallet_sub: recipient!(addr, SetConsumingWalletMessage), from_ui_message_sub: recipient!(addr, NodeFromUiMessage), - new_password_sub: recipient!(addr, NewPasswordMessage), + new_password_sub: recipient!(addr, NewPasswordMessage), // GH-728 connection_progress_sub: recipient!(addr, ConnectionProgressMessage), } } @@ -405,10 +462,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu report_routing_service_provided: recipient!(addr, ReportRoutingServiceProvidedMessage), report_exit_service_provided: recipient!(addr, ReportExitServiceProvidedMessage), report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), - report_consuming_wallet_balances_and_qualified_payables: recipient!( - addr, - PayablePaymentSetup - ), + report_payable_payments_setup: recipient!(addr, BlockchainAgentWithContextMessage), report_inbound_payments: recipient!(addr, ReceivedPayments), init_pending_payable_fingerprints: recipient!(addr, PendingPayableFingerprintSeeds), report_transaction_receipts: recipient!(addr, ReportTransactionReceipts), @@ -426,18 +480,18 @@ pub fn make_ui_gateway_subs_from_recorder(addr: &Addr) -> UiGatewaySub } } -pub fn make_blockchain_bridge_subs_from(addr: &Addr) -> BlockchainBridgeSubs { +pub fn make_blockchain_bridge_subs_from_recorder(addr: &Addr) -> BlockchainBridgeSubs { BlockchainBridgeSubs { bind: recipient!(addr, BindMessage), - report_accounts_payable: recipient!(addr, OutcomingPaymentsInstructions), - pps_for_blockchain_bridge: recipient!(addr, PayablePaymentSetup), + outbound_payments_instructions: recipient!(addr, OutboundPaymentsInstructions), + qualified_payables: recipient!(addr, QualifiedPayablesMessage), retrieve_transactions: recipient!(addr, RetrieveTransactions), ui_sub: recipient!(addr, NodeFromUiMessage), request_transaction_receipts: recipient!(addr, RequestTransactionReceipts), } } -pub fn make_configurator_subs_from(addr: &Addr) -> ConfiguratorSubs { +pub fn make_configurator_subs_from_recorder(addr: &Addr) -> ConfiguratorSubs { ConfiguratorSubs { bind: recipient!(addr, BindMessage), node_from_ui_sub: recipient!(addr, NodeFromUiMessage), @@ -534,15 +588,15 @@ impl PeerActorsBuilder { let configurator_addr = self.configurator.start(); PeerActors { - proxy_server: make_proxy_server_subs_from(&proxy_server_addr), - dispatcher: make_dispatcher_subs_from(&dispatcher_addr), - hopper: make_hopper_subs_from(&hopper_addr), - proxy_client_opt: Some(make_proxy_client_subs_from(&proxy_client_addr)), - neighborhood: make_neighborhood_subs_from(&neighborhood_addr), + proxy_server: make_proxy_server_subs_from_recorder(&proxy_server_addr), + dispatcher: make_dispatcher_subs_from_recorder(&dispatcher_addr), + hopper: make_hopper_subs_from_recorder(&hopper_addr), + proxy_client_opt: Some(make_proxy_client_subs_from_recorder(&proxy_client_addr)), + neighborhood: make_neighborhood_subs_from_recorder(&neighborhood_addr), accountant: make_accountant_subs_from_recorder(&accountant_addr), ui_gateway: make_ui_gateway_subs_from_recorder(&ui_gateway_addr), - blockchain_bridge: make_blockchain_bridge_subs_from(&blockchain_bridge_addr), - configurator: make_configurator_subs_from(&configurator_addr), + blockchain_bridge: make_blockchain_bridge_subs_from_recorder(&blockchain_bridge_addr), + configurator: make_configurator_subs_from_recorder(&configurator_addr), } } } @@ -552,13 +606,14 @@ mod tests { use super::*; use actix::Message; use actix::System; + use std::any::TypeId; #[derive(Debug, PartialEq, Eq, Message)] struct FirstMessageType { string: String, } - recorder_message_handler!(FirstMessageType); + recorder_message_handler_t_m_p!(FirstMessageType); #[derive(Debug, PartialEq, Eq, Message)] struct SecondMessageType { @@ -566,7 +621,7 @@ mod tests { flag: bool, } - recorder_message_handler!(SecondMessageType); + recorder_message_handler_t_m_p!(SecondMessageType); #[test] fn recorder_records_different_messages() { @@ -607,4 +662,20 @@ mod tests { ); assert_eq!(recording.len(), 2); } + + struct ExampleMsgA; + + struct ExampleMsgB; + + #[test] + fn different_messages_in_pretending_matchable_have_different_type_ids() { + assert_eq!( + TypeId::of::>(), + TypeId::of::>() + ); + assert_ne!( + TypeId::of::>(), + TypeId::of::>() + ) + } } diff --git a/node/src/test_utils/recorder_stop_conditions.rs b/node/src/test_utils/recorder_stop_conditions.rs index c9a18cbcb..b3dca287d 100644 --- a/node/src/test_utils/recorder_stop_conditions.rs +++ b/node/src/test_utils/recorder_stop_conditions.rs @@ -24,14 +24,17 @@ pub type BoxedMsgExpected = Box; pub type RefMsgExpected<'a> = &'a (dyn Any + Send); impl StopConditions { - pub fn resolve_stop_conditions(&mut self, msg: &T) -> bool { + pub fn resolve_stop_conditions + Send + 'static>( + &mut self, + msg: &T, + ) -> bool { match self { StopConditions::Any(conditions) => Self::resolve_any::(conditions, msg), StopConditions::All(conditions) => Self::resolve_all::(conditions, msg), } } - fn resolve_any( + fn resolve_any + Send + 'static>( conditions: &Vec, msg: &T, ) -> bool { @@ -40,7 +43,7 @@ impl StopConditions { .any(|condition| condition.resolve_condition::(msg)) } - fn resolve_all( + fn resolve_all + Send + 'static>( conditions: &mut Vec, msg: &T, ) -> bool { @@ -49,7 +52,7 @@ impl StopConditions { conditions.is_empty() } - fn indexes_of_matched_conditions( + fn indexes_of_matched_conditions + Send + 'static>( conditions: &[StopCondition], msg: &T, ) -> Vec { @@ -82,9 +85,9 @@ impl StopConditions { } impl StopCondition { - fn resolve_condition(&self, msg: &T) -> bool { + fn resolve_condition + Send + 'static>(&self, msg: &T) -> bool { match self { - StopCondition::StopOnType(type_id) => Self::matches_stop_on_type::(*type_id), + StopCondition::StopOnType(type_id) => Self::matches_stop_on_type::(msg, *type_id), StopCondition::StopOnMatch { exemplar } => { Self::matches_stop_on_match::(exemplar, msg) } @@ -94,12 +97,12 @@ impl StopCondition { } } - fn matches_stop_on_type(expected_type_id: TypeId) -> bool { - let msg_type_id = TypeId::of::(); - msg_type_id == expected_type_id + fn matches_stop_on_type>(msg: &T, expected_type_id: TypeId) -> bool { + let correct_msg_type_id = msg.correct_msg_type_id(); + correct_msg_type_id == expected_type_id } - fn matches_stop_on_match( + fn matches_stop_on_match + 'static + Send>( exemplar: &BoxedMsgExpected, msg: &T, ) -> bool { @@ -117,6 +120,33 @@ impl StopCondition { } } +pub trait ForcedMatchable: PartialEq + Send { + fn correct_msg_type_id(&self) -> TypeId; +} + +pub struct PretendedMatchableWrapper(pub M); + +impl ForcedMatchable for PretendedMatchableWrapper +where + OuterM: PartialEq, + InnerM: Send, +{ + fn correct_msg_type_id(&self) -> TypeId { + TypeId::of::() + } +} + +impl PartialEq for PretendedMatchableWrapper { + fn eq(&self, _other: &Self) -> bool { + panic!( + r#"You requested StopCondition::StopOnMatch for message + that does not implement PartialEq. Consider two other + options: matching the type simply by its TypeId or using + a predicate."# + ) + } +} + #[macro_export] macro_rules! match_every_type_id{ ($($single_message: ident),+) => { diff --git a/node/tests/dump_configuration_test.rs b/node/tests/dump_configuration_test.rs index 019bc3ebc..044eb2043 100644 --- a/node/tests/dump_configuration_test.rs +++ b/node/tests/dump_configuration_test.rs @@ -19,7 +19,11 @@ fn dump_configuration_with_an_existing_database_integration() { let port = find_free_port(); let mut node = utils::MASQNode::start_standard( test_name, - Some(CommandConfig::new().pair("--ui-port", &port.to_string())), + Some( + CommandConfig::new() + .pair("--ui-port", &port.to_string()) + .pair("--chain", "polygon-mumbai"), + ), true, true, false, @@ -34,7 +38,7 @@ fn dump_configuration_with_an_existing_database_integration() { let mut node = utils::MASQNode::run_dump_config( test_name, - Some(CommandConfig::new().pair("--chain", DEFAULT_CHAIN.rec().literal_identifier)), + Some(CommandConfig::new().pair("--chain", "polygon-mumbai")), false, true, true, diff --git a/node/tests/financials_test.rs b/node/tests/financials_test.rs index f71d19bce..0874cb39b 100644 --- a/node/tests/financials_test.rs +++ b/node/tests/financials_test.rs @@ -10,11 +10,9 @@ use masq_lib::messages::{ use masq_lib::test_utils::ui_connection::UiConnection; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::find_free_port; -use node_lib::accountant::database_access_objects::payable_dao::{PayableDao, PayableDaoReal}; -use node_lib::accountant::database_access_objects::receivable_dao::{ - ReceivableDao, ReceivableDaoReal, -}; -use node_lib::accountant::database_access_objects::utils::{from_time_t, to_time_t}; +use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; +use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; +use node_lib::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use node_lib::accountant::gwei_to_wei; use node_lib::test_utils::make_wallet; use std::time::SystemTime; diff --git a/node/tests/initialization_test.rs b/node/tests/initialization_test.rs index 6af485458..e6b88708f 100644 --- a/node/tests/initialization_test.rs +++ b/node/tests/initialization_test.rs @@ -9,7 +9,6 @@ use masq_lib::messages::{ }; use masq_lib::messages::{UiFinancialsRequest, UiRedirect, UiStartOrder, UiStartResponse}; use masq_lib::test_utils::ui_connection::UiConnection; -use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, node_home_directory}; use masq_lib::utils::find_free_port; use node_lib::daemon::launch_verifier::{VerifierTools, VerifierToolsReal}; @@ -150,15 +149,14 @@ fn wait_for_process_end(process_id: u32) { #[test] fn incomplete_node_descriptor_is_refused_integration() { - let test_default_chain_identifier = TEST_DEFAULT_CHAIN.rec().literal_identifier; + let chain_identifier = "polygon-mainnet"; let mut node = utils::MASQNode::start_standard( "incomplete_node_descriptor_is_refused_integration", Some( CommandConfig::new() .pair( "--neighbors", - &format!("masq://{}:12345vhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@12.23.34.45:5678,masq://{}:abJ5XvhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@:", - test_default_chain_identifier,test_default_chain_identifier) + &format!("masq://{chain_identifier}:12345vhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@12.23.34.45:5678,masq://{chain_identifier}:abJ5XvhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@:") ), ), true, @@ -176,8 +174,7 @@ fn incomplete_node_descriptor_is_refused_integration() { stdout ); let stderr = String::from_utf8_lossy(&output.stderr); - assert!(stderr.contains(&format!("neighbors - Neighbors supplied without ip addresses and ports are not valid: 'masq://{}:abJ5XvhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@:", - test_default_chain_identifier) + assert!(stderr.contains(&format!("neighbors - Neighbors supplied without ip addresses and ports are not valid: 'masq://{chain_identifier}:abJ5XvhVbmVyGejkYUkmftF09pmGZGKg_PzRNnWQxFw@:") ), "instead we got: {}",stderr) } }; @@ -214,13 +211,18 @@ fn started_without_explicit_chain_parameter_runs_fine_integration() { #[test] fn requested_chain_meets_different_db_chain_and_panics_integration() { + let chain_literal = DEFAULT_CHAIN.rec().literal_identifier; let test_name = "requested_chain_meets_different_db_chain_and_panics_integration"; { //running Node just in order to create a new database which we can do testing on let port = find_free_port(); let mut node = utils::MASQNode::start_standard( test_name, - Some(CommandConfig::new().pair("--ui-port", &port.to_string())), + Some( + CommandConfig::new() + .pair("--ui-port", &port.to_string()) + .pair("--chain", &chain_literal), + ), true, true, false, @@ -233,6 +235,7 @@ fn requested_chain_meets_different_db_chain_and_panics_integration() { node.wait_for_exit(); } let db_dir = node_home_directory("integration", test_name); + let conn = Connection::open_with_flags( &db_dir.join(DATABASE_FILE), OpenFlags::SQLITE_OPEN_READ_WRITE, @@ -243,11 +246,20 @@ fn requested_chain_meets_different_db_chain_and_panics_integration() { [], ) .unwrap(); + let mut node = MASQNode::start_standard( + test_name, + Some(CommandConfig::new().pair("--chain", &chain_literal)), + false, + true, + false, + false, + ); - let mut node = MASQNode::start_standard(test_name, None, false, true, false, false); - - let regex_pattern = r"ERROR: PanicHandler: src(/|\\)actor_system_factory\.rs.*- Database with a wrong chain name detected; expected: eth-ropsten, was: eth-mainnet"; - node.wait_for_log(regex_pattern, Some(1000)); + let regex_pattern = &format!( + r"ERROR: PanicHandler: src(/|\\)actor_system_factory\.rs.*- Database with a wrong chain name detected; expected: {}, was: eth-mainnet", + &chain_literal + ); + node.wait_for_log(®ex_pattern, Some(1000)); } #[test] @@ -259,7 +271,7 @@ fn node_creates_log_file_with_heading_integration() { "--neighbors", &format!( "masq://{}:UJNoZW5p_PDVqEjpr3b-8jZ_93yPG8i5dOAgE1bhK-A@12.23.34.45:5678", - TEST_DEFAULT_CHAIN.rec().literal_identifier + DEFAULT_CHAIN.rec().literal_identifier ), ); diff --git a/node/tests/node_exits_from_future_panic_test.rs b/node/tests/node_exits_from_future_panic_test.rs index b2c11e66b..1419dc0fe 100644 --- a/node/tests/node_exits_from_future_panic_test.rs +++ b/node/tests/node_exits_from_future_panic_test.rs @@ -3,18 +3,23 @@ pub mod utils; use crate::utils::CommandConfig; +use masq_lib::constants::DEFAULT_CHAIN; +use masq_lib::test_utils::utils::ensure_node_home_directory_exists; +use masq_lib::utils::add_chain_specific_directory; +use std::fs::create_dir_all; #[cfg(not(target_os = "windows"))] use std::process; #[cfg(not(target_os = "windows"))] use std::thread; #[cfg(not(target_os = "windows"))] use std::time::Duration; +use utils::MASQNode; #[test] fn node_exits_from_future_panic_integration() { let panic_config = CommandConfig::new().pair("--crash-point", "panic"); - let mut node = utils::MASQNode::start_standard( + let mut node = MASQNode::start_standard( "node_exits_from_future_panic_integration", Some(panic_config), true, @@ -29,9 +34,17 @@ fn node_exits_from_future_panic_integration() { #[test] fn node_logs_panic_integration() { - let panic_config = CommandConfig::new().pair("--crash-point", "panic"); - - let mut node = utils::MASQNode::start_standard( + let data_directory = + ensure_node_home_directory_exists("integration", "node_logs_panic_integration"); + let data_dir_chain_path = add_chain_specific_directory(DEFAULT_CHAIN, &data_directory); + create_dir_all(&data_dir_chain_path).expect( + "Could not create chain directory inside node_logs_panic_integration home/MASQ directory", + ); + let panic_config = CommandConfig::new() + .pair("--crash-point", "panic") + .pair("--chain", "polygon-mainnet") + .pair("--data-directory", data_directory.to_str().unwrap()); + let mut node = MASQNode::start_standard( "node_logs_panic_integration", Some(panic_config), true, @@ -52,15 +65,15 @@ const STAT_FORMAT_PARAM_NAME: &str = "-f"; #[cfg(not(target_os = "windows"))] #[test] fn node_logfile_does_not_belong_to_root_integration() { - let mut node = utils::MASQNode::start_standard( + let mut node = MASQNode::start_standard( "node_logfile_does_not_belong_to_root_integration", - None, + Some(CommandConfig::new().pair("--chain", "polygon-mumbai")), true, true, false, true, ); - let logfile_path = utils::MASQNode::path_to_logfile(&node.data_dir); + let logfile_path = MASQNode::path_to_logfile(&node.data_dir); thread::sleep(Duration::from_secs(2)); node.kill().unwrap(); diff --git a/node/tests/ui_gateway_test.rs b/node/tests/ui_gateway_test.rs index 4dfbadfba..4398a3c20 100644 --- a/node/tests/ui_gateway_test.rs +++ b/node/tests/ui_gateway_test.rs @@ -3,6 +3,7 @@ pub mod utils; use crate::utils::MASQNode; +use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::messages::SerializableLogLevel::Warn; use masq_lib::messages::{ UiChangePasswordRequest, UiCheckPasswordRequest, UiCheckPasswordResponse, UiLogBroadcast, @@ -11,7 +12,7 @@ use masq_lib::messages::{ }; use masq_lib::test_utils::ui_connection::UiConnection; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; -use masq_lib::utils::find_free_port; +use masq_lib::utils::{add_chain_specific_directory, find_free_port}; use utils::CommandConfig; #[test] @@ -57,7 +58,11 @@ fn log_broadcasts_are_correctly_received_integration() { let port = find_free_port(); let mut node = utils::MASQNode::start_standard( "log_broadcasts_are_correctly_received", - Some(CommandConfig::new().pair("--ui-port", &port.to_string())), + Some( + CommandConfig::new() + .pair("--ui-port", &port.to_string()) + .pair("--chain", "polygon-mainnet"), + ), true, true, false, @@ -97,6 +102,7 @@ fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { "ui_gateway_test", "daemon_does_not_allow_node_to_keep_his_client_alive_integration", ); + let expected_chain_data_dir = add_chain_specific_directory(DEFAULT_CHAIN, &data_directory); let daemon_port = find_free_port(); let mut daemon = utils::MASQNode::start_daemon( "daemon_does_not_allow_node_to_keep_his_client_alive_integration", @@ -111,7 +117,7 @@ fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { let _: UiSetupResponse = daemon_client .transact(UiSetupRequest::new(vec![ ("ip", Some("100.80.1.1")), - ("chain", Some("eth-mainnet")), + ("chain", Some("polygon-mainnet")), ("neighborhood-mode", Some("standard")), ("log-level", Some("trace")), ("data-directory", Some(&data_directory.to_str().unwrap())), @@ -121,11 +127,10 @@ fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { let _: UiStartResponse = daemon_client.transact(UiStartOrder {}).unwrap(); let connected_and_disconnected_assertion = - |how_many_occurrences_we_look_for: usize, pattern_in_log: fn(port_spec: &str) -> String| { + |how_many_occurrences_we_look_for: usize, + make_regex_searching_for_port_in_logs: fn(port_spec: &str) -> String| { let port_number_regex_str = r"UI connected at 127\.0\.0\.1:([\d]*)"; - //TODO fix this when GH-580 is being played - // let log_file_directory = data_directory.join("eth-mainnet"); - let log_file_directory = data_directory.clone(); + let log_file_directory = expected_chain_data_dir.clone(); let all_uis_connected_so_far = MASQNode::capture_pieces_of_log_at_directory( port_number_regex_str, &log_file_directory.as_path(), @@ -136,7 +141,7 @@ fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { let searched_port_of_ui = all_uis_connected_so_far[how_many_occurrences_we_look_for - 1][1].as_str(); MASQNode::wait_for_match_at_directory( - pattern_in_log(searched_port_of_ui).as_str(), + make_regex_searching_for_port_in_logs(searched_port_of_ui).as_str(), log_file_directory.as_path(), Some(1500), ); @@ -169,7 +174,11 @@ fn cleanup_after_deceased_clients_integration() { let port = find_free_port(); let mut node = utils::MASQNode::start_standard( "cleanup_after_deceased_clients_integration", - Some(CommandConfig::new().pair("--ui-port", &port.to_string())), + Some( + CommandConfig::new() + .pair("--chain", DEFAULT_CHAIN.rec().literal_identifier) + .pair("--ui-port", &port.to_string()), + ), true, true, false, diff --git a/node/tests/utils.rs b/node/tests/utils.rs index 7ced99d95..b60017063 100644 --- a/node/tests/utils.rs +++ b/node/tests/utils.rs @@ -1,10 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use itertools::Itertools; -use masq_lib::constants::{CURRENT_LOGFILE_NAME, DEFAULT_UI_PORT}; -use masq_lib::test_utils::utils::node_home_directory; -use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; -use masq_lib::utils::localhost; +use masq_lib::blockchains::chains::Chain; +use masq_lib::constants::{CURRENT_LOGFILE_NAME, DEFAULT_CHAIN, DEFAULT_UI_PORT}; +use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, node_home_directory}; +use masq_lib::utils::{add_masq_and_chain_directories, localhost}; use node_lib::database::connection_wrapper::ConnectionWrapper; use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, @@ -25,13 +25,14 @@ use std::thread; use std::time::Duration; use std::time::Instant; +#[derive(Debug)] pub struct MASQNode { pub data_dir: PathBuf, child: Option, output: Option, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CommandConfig { pub args: Vec, } @@ -166,8 +167,8 @@ impl MASQNode { } #[allow(dead_code)] - pub fn wait_for_log(&mut self, pattern: &str, limit_ms: Option) { - Self::wait_for_match_at_directory(pattern, self.data_dir.as_path(), limit_ms); + pub fn wait_for_log(&mut self, regex_pattern: &str, limit_ms: Option) { + Self::wait_for_match_at_directory(regex_pattern, &self.data_dir.as_path(), limit_ms); } pub fn wait_for_match_at_directory(pattern: &str, logfile_dir: &Path, limit_ms: Option) { @@ -334,8 +335,9 @@ impl MASQNode { logfile_path } - pub fn remove_database(data_dir: &PathBuf) { - let database = Self::path_to_database(data_dir); + pub fn remove_database(data_dir: &PathBuf, chain: Chain) { + let data_dir_chain_path = add_masq_and_chain_directories(chain, data_dir); + let database = Self::path_to_database(&data_dir_chain_path); match std::fs::remove_file(&database) { Ok(_) => (), Err(ref e) if e.kind() == io::ErrorKind::NotFound => (), @@ -365,13 +367,13 @@ impl MASQNode { } let ui_port = Self::ui_port_from_config_opt(&config_opt); let mut command = command_getter(&data_dir, config_opt, sterile_database); - eprintln!("{:?}", command); + eprintln!("Launching MASQNode with this command: {:?}", command); let command = if piped_streams { command.stdout(Stdio::piped()).stderr(Stdio::piped()) } else { &mut command }; - let mut result = Self::spawn_process(command, data_dir); + let mut result = Self::spawn_process(command, data_dir.to_owned()); if ensure_start { result.wait_for_node(ui_port).unwrap(); } @@ -387,6 +389,16 @@ impl MASQNode { } } + fn get_chain_from_config(config_opt: &Option) -> Chain { + match config_opt { + Some(config) => match config.value_of("--chain") { + Some(chain_str) => Chain::from(chain_str.as_str()), + None => DEFAULT_CHAIN, + }, + None => DEFAULT_CHAIN, + } + } + fn millis_since(started_at: Instant) -> u64 { let interval = Instant::now().duration_since(started_at); let second_milliseconds = interval.as_secs() * 1000; @@ -396,57 +408,62 @@ impl MASQNode { fn make_daemon_command( data_dir: &PathBuf, - config: Option, + config_opt: Option, remove_database: bool, ) -> process::Command { + let chain = Self::get_chain_from_config(&config_opt); let args = Self::daemon_args(); let args = Self::extend_args_without_duplication( args, - match config { + match config_opt { Some(c) => c.args, None => vec![], }, ); - Self::start_with_args_extension(data_dir, args, remove_database) + Self::start_with_args_extension(chain, data_dir, args, remove_database) } fn make_node_command( data_dir: &PathBuf, - config: Option, + config_opt: Option, remove_database: bool, ) -> process::Command { + let chain = Self::get_chain_from_config(&config_opt); let args = Self::standard_args(); let args = - Self::extend_args_without_duplication(args, Self::get_extra_args(data_dir, config)); - Self::start_with_args_extension(data_dir, args, remove_database) + Self::extend_args_without_duplication(args, Self::get_extra_args(data_dir, config_opt)); + Self::start_with_args_extension(chain, data_dir, args, remove_database) } fn make_node_without_initial_config( data_dir: &PathBuf, - config: Option, + config_opt: Option, remove_database: bool, ) -> process::Command { - let args = Self::get_extra_args(data_dir, config); - Self::start_with_args_extension(data_dir, args, remove_database) + let chain = Self::get_chain_from_config(&config_opt); + let args = Self::get_extra_args(data_dir, config_opt); + Self::start_with_args_extension(chain, data_dir, args, remove_database) } fn make_dump_config_command( data_dir: &PathBuf, - config: Option, + config_opt: Option, remove_database: bool, ) -> process::Command { + let chain = Self::get_chain_from_config(&config_opt); let mut args = Self::dump_config_args(); - args.extend(Self::get_extra_args(data_dir, config)); - Self::start_with_args_extension(data_dir, args, remove_database) + args.extend(Self::get_extra_args(data_dir, config_opt)); + Self::start_with_args_extension(chain, data_dir, args, remove_database) } fn start_with_args_extension( + chain: Chain, data_dir: &PathBuf, additional_args: Vec, remove_database: bool, ) -> process::Command { if remove_database { - Self::remove_database(data_dir) + Self::remove_database(data_dir, chain) } let mut command = command_to_start(); command.args(apply_prefix_parameters()); @@ -465,7 +482,6 @@ impl MASQNode { "--consuming-private-key", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", ) - .pair("--chain", TEST_DEFAULT_CHAIN.rec().literal_identifier) .pair("--log-level", "trace") .args } From feed21ee2e50ea91586411196b07f13989a80b20 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 10 Oct 2023 00:33:43 +0200 Subject: [PATCH 081/250] GH-711: all the remaining tests fixed (located only in the PaymentAdjuster) --- node/src/accountant/payment_adjuster/mod.rs | 1356 +++++++++---------- 1 file changed, 609 insertions(+), 747 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e88ba8c99..11ba4204a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -684,112 +684,6 @@ impl Display for PaymentAdjusterError { } } -// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -// -// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; -// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; -// use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; -// use masq_lib::logger::Logger; -// use std::time::SystemTime; -// -// pub trait PaymentAdjuster { -// fn search_for_indispensable_adjustment( -// &self, -// msg: &BlockchainAgentWithContextMessage, -// logger: &Logger, -// ) -> Result, AnalysisError>; -// -// fn adjust_payments( -// &self, -// setup: PreparedAdjustment, -// now: SystemTime, -// logger: &Logger, -// ) -> OutboundPaymentsInstructions; -// -// as_any_in_trait!(); -// } -// -// pub struct PaymentAdjusterReal {} -// -// impl PaymentAdjuster for PaymentAdjusterReal { -// fn search_for_indispensable_adjustment( -// &self, -// _msg: &BlockchainAgentWithContextMessage, -// _logger: &Logger, -// ) -> Result, AnalysisError> { -// Ok(None) -// } -// -// fn adjust_payments( -// &self, -// _setup: PreparedAdjustment, -// _now: SystemTime, -// _logger: &Logger, -// ) -> OutboundPaymentsInstructions { -// todo!("this function is dead until the card GH-711 is played") -// } -// -// as_any_in_trait_impl!(); -// } -// -// impl PaymentAdjusterReal { -// pub fn new() -> Self { -// Self {} -// } -// } -// -// impl Default for PaymentAdjusterReal { -// fn default() -> Self { -// Self::new() -// } -// } -// -// #[derive(Debug, PartialEq, Eq, Clone, Copy)] -// pub enum Adjustment { -// MasqToken, -// TransactionFeeCurrency { limiting_count: u16 }, -// Both, -// } -// -// #[derive(Debug, PartialEq, Eq)] -// pub enum AnalysisError {} -// -// #[cfg(test)] -// mod tests { -// use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; -// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; -// use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; -// use crate::accountant::scanners::test_utils::protect_payables_in_test; -// use crate::accountant::test_utils::make_payable_account; -// use masq_lib::logger::Logger; -// use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; -// -// #[test] -// fn search_for_indispensable_adjustment_always_returns_none() { -// init_test_logging(); -// let test_name = "is_adjustment_required_always_returns_none"; -// let mut payable = make_payable_account(111); -// payable.balance_wei = 100_000_000; -// let agent = BlockchainAgentMock::default(); -// let setup_msg = BlockchainAgentWithContextMessage { -// protected_qualified_payables: protect_payables_in_test(vec![payable]), -// agent: Box::new(agent), -// response_skeleton_opt: None, -// }; -// let logger = Logger::new(test_name); -// let subject = PaymentAdjusterReal::new(); -// -// let result = subject.search_for_indispensable_adjustment(&setup_msg, &logger); -// -// assert_eq!(result, Ok(None)); -// TestLogHandler::default().exists_no_log_containing(test_name); -// // Nobody in this test asked about the wallet balances and the transaction fee -// // requirement, yet we got through with the final None. -// // How do we know? The mock agent didn't blow up while missing these -// // results -// } -// } - #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; @@ -820,6 +714,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; #[test] #[should_panic( @@ -1308,406 +1203,381 @@ mod tests { } #[test] - fn loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers( + fn loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_overly_large_numbers( ) { init_test_logging(); - todo!("fix me") - // let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; - // let now = SystemTime::now(); - // // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria - // let qualified_payables = { - // let debt_age_in_months = vec![120, 120, 120]; - // make_extreme_accounts( - // Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), - // now, - // ) - // }; - // let mut subject = PaymentAdjusterReal::new(); - // subject.logger = Logger::new(test_name); - // // for change extremely small cw balance - // let cw_masq_balance = 1_000; - // let setup_msg = todo!() - // - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: u32::MAX as u128, - // // masq_tokens_minor: cw_masq_balance, - // // }, - // // estimated_gas_limit_per_transaction: 70_000, - // // agreed_transaction_fee_per_computed_unit_major: 120, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::MasqToken, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; - // // normally, the initial feasibility check wouldn't allow this - // assert_eq!(result.accounts, vec![]); - // let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { - // format!( - // "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ - // Seems unavoidable to disregard payable {wallet} at the moment. \ - // Reason is the computed possible payment of {} wei \ - // would not be at least half of the original debt {}", - // proposed_adjusted_balance_in_this_iteration.separate_with_commas(), - // (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() - // ) - // }; - // let log_handler = TestLogHandler::new(); - // // Notice that the proposals grow as one disqualified account drops in each iteration - // log_handler.exists_log_containing(&expected_log( - // "0x000000000000000000000000000000626c616830", - // 333, - // )); - // log_handler.exists_log_containing(&expected_log( - // "0x000000000000000000000000000000626c616831", - // 499, - // )); - // log_handler.exists_log_containing(&expected_log( - // "0x000000000000000000000000000000626c616832", - // 1000, - // )); + let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; + let now = SystemTime::now(); + // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria + let qualified_payables = { + let debt_age_in_months = vec![120, 120, 120]; + make_extreme_accounts( + Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), + now, + ) + }; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + // for change extremely small cw balance + let cw_masq_balance = 1_000; + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::MAX, + service_fee_balance_in_minor_units: cw_masq_balance, + }; + let agent = { + let mock = BlockchainAgentMock::default() + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::MasqToken, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; + // normally, the initial feasibility check wouldn't allow this + assert_eq!(result.affordable_accounts, vec![]); + let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { + format!( + "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ + Seems unavoidable to disregard payable {wallet} at the moment. \ + Reason is the computed possible payment of {} wei \ + would not be at least half of the original debt {}", + proposed_adjusted_balance_in_this_iteration.separate_with_commas(), + (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() + ) + }; + let log_handler = TestLogHandler::new(); + // Notice that the proposals grow as one disqualified account drops in each iteration + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616830", + 333, + )); + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616831", + 499, + )); + log_handler.exists_log_containing(&expected_log( + "0x000000000000000000000000000000626c616832", + 1000, + )); } #[test] fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { init_test_logging(); - todo!("fix me") - // let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; - // let now = SystemTime::now(); - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 4_444_444_444_444_444_444, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 6_000_000_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghi"), - // balance_wei: 6_666_666_666_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - // let mut subject = PaymentAdjusterReal::new(); - // subject.logger = Logger::new(test_name); - // let accounts_sum: u128 = - // 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; - // let consuming_wallet_masq_balance_minor = accounts_sum - 2_000_000_000_000_000_000; - // let setup_msg = todo!() - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: u32::MAX as u128, - // // masq_tokens_minor: consuming_wallet_masq_balance_minor, - // // }, - // // estimated_gas_limit_per_transaction: 70_000, - // // agreed_transaction_fee_per_computed_unit_major: 120, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // let expected_criteria_computation_output = { - // let account_1_adjusted = PayableAccount { - // balance_wei: 3_918_231_688_187_775_576, - // ..account_1 - // }; - // let account_2_adjusted = PayableAccount { - // balance_wei: 5_921_593_128_688_275_336, - // ..account_2 - // }; - // let account_3_adjusted = PayableAccount { - // balance_wei: 5_271_286_293_568_393_532, - // ..account_3 - // }; - // vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] - // }; - // assert_eq!( - // result, - // OutboundPaymentsInstructions { - // accounts: expected_criteria_computation_output, - // response_skeleton_opt: None - // } - // ); - // let log_msg = format!( - // "DEBUG: {test_name}: \n\ - // |Payable Account Balance Wei - // |---------------------------------------------------------- - // |Successfully Adjusted Original - // | Adjusted - // | - // |0x0000000000000000000000000000000000646566 6000000000000000000 - // | 5921593128688275336 - // |0x0000000000000000000000000000000000676869 6666666666000000000 - // | 5271286293568393532 - // |0x0000000000000000000000000000000000616263 4444444444444444444 - // | 3918231688187775576" - // ); - // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 4_444_444_444_444_444_444, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 6_000_000_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 6_666_666_666_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + let agent_id_stamp = ArbitraryIdStamp::new(); + let accounts_sum: u128 = + 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; + let service_fee_balance_in_minor_units = accounts_sum - 2_000_000_000_000_000_000; + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::MAX, + service_fee_balance_in_minor_units, + }; + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + let expected_criteria_computation_output = { + let account_1_adjusted = PayableAccount { + balance_wei: 3_918_231_688_187_775_576, + ..account_1 + }; + let account_2_adjusted = PayableAccount { + balance_wei: 5_921_593_128_688_275_336, + ..account_2 + }; + let account_3_adjusted = PayableAccount { + balance_wei: 5_271_286_293_568_393_532, + ..account_3 + }; + vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] + }; + assert_eq!( + result.affordable_accounts, + expected_criteria_computation_output + ); + assert_eq!(result.response_skeleton_opt, None); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original +| Adjusted +| +|0x0000000000000000000000000000000000646566 6000000000000000000 +| 5921593128688275336 +|0x0000000000000000000000000000000000676869 6666666666000000000 +| 5271286293568393532 +|0x0000000000000000000000000000000000616263 4444444444444444444 +| 3918231688187775576" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large( ) { init_test_logging(); - todo!("fix me") - // let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; - // let now = SystemTime::now(); - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 111_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghi"), - // balance_wei: 222_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - // let mut subject = PaymentAdjusterReal::new(); - // subject.logger = Logger::new(test_name); - // let setup_msg = todo!() - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // // masq_tokens_minor: 10_u128.pow(22), - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::PriorityTransactionFee { - // affordable_transaction_count: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // assert_eq!( - // result, - // OutboundPaymentsInstructions { - // accounts: vec![account_3, account_2], - // response_skeleton_opt: None - // } - // ); - // let log_msg = format!( - // "DEBUG: {test_name}: \n\ - // |Payable Account Balance Wei - // |---------------------------------------------------------- - // |Successfully Adjusted Original - // | Adjusted - // | - // |0x0000000000000000000000000000000000646566 333000000000000 - // | 333000000000000 - // |0x0000000000000000000000000000000000676869 222000000000000 - // | 222000000000000 - // | - // |Ruled Out Original - // | - // |0x0000000000000000000000000000000000616263 111000000000000" - // ); - // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + let agent_id_stamp = ArbitraryIdStamp::new(); + let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units: 10_u128.pow(22), + }; + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2, + }, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + assert_eq!(result.affordable_accounts, vec![account_3, account_2]); + assert_eq!(result.response_skeleton_opt, None); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original +| Adjusted +| +|0x0000000000000000000000000000000000646566 333000000000000 +| 333000000000000 +|0x0000000000000000000000000000000000676869 222000000000000 +| 222000000000000 +| +|Ruled Out Original +| +|0x0000000000000000000000000000000000616263 111000000000000" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn both_balances_are_insufficient_but_adjustment_by_masq_will_not_affect_the_accounts_count() { init_test_logging(); - todo!("fix me") - // let now = SystemTime::now(); - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 111_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghk"), - // balance_wei: 222_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; - // let mut subject = PaymentAdjusterReal::new(); - // let consuming_wallet_masq_balance = 111_000_000_000_000_u128 + 333_000_000_000_000; - // let response_skeleton_opt = Some(ResponseSkeleton { - // client_id: 123, - // context_id: 321, - // }); //just hardening, not so important - // let setup_msg = todo!() - // - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // // masq_tokens_minor: consuming_wallet_masq_balance, - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::PriorityTransactionFee { - // affordable_transaction_count: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // // account_1, the least important one, was eliminated for missing enough - // // transaction fee balance - // let expected_accounts = { - // let account_2_adjusted = PayableAccount { - // balance_wei: 222_000_000_000_000, - // ..account_2 - // }; - // vec![account_3, account_2_adjusted] - // }; - // assert_eq!( - // result, - // OutboundPaymentsInstructions { - // accounts: expected_accounts, - // response_skeleton_opt - // } - // ); + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + let service_fee_balance_in_minor_units = 111_000_000_000_000_u128 + 333_000_000_000_000; + let agent_id_stamp = ArbitraryIdStamp::new(); + //TODO this has no effect!!!!! + let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units, + }; + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let response_skeleton_opt = Some(ResponseSkeleton { + client_id: 123, + context_id: 321, + }); //just hardening, not so important + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2, + }, + response_skeleton_opt, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + // account_1, the least important one, was eliminated for missing enough + // transaction fee balance + let expected_accounts = { + let account_2_adjusted = PayableAccount { + balance_wei: 222_000_000_000_000, + ..account_2 + }; + vec![account_3, account_2_adjusted] + }; + assert_eq!(result.affordable_accounts, expected_accounts); + assert_eq!(result.response_skeleton_opt, response_skeleton_opt); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); } #[test] fn adjust_payments_when_only_masq_balance_limits_the_final_transaction_count() { init_test_logging(); - todo!("fix me") - // let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; - // let now = SystemTime::now(); - // let wallet_1 = make_wallet("def"); - // // account to be adjusted up to maximum - // let account_1 = PayableAccount { - // wallet: wallet_1.clone(), - // balance_wei: 333_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), - // pending_payable_opt: None, - // }; - // // account to be outweighed and fully taken - // let wallet_2 = make_wallet("abc"); - // let account_2 = PayableAccount { - // wallet: wallet_2.clone(), - // balance_wei: 111_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), - // pending_payable_opt: None, - // }; - // // account to be disqualified - // let wallet_3 = make_wallet("ghk"); - // let balance_3 = 600_000_000_000; - // let account_3 = PayableAccount { - // wallet: wallet_3.clone(), - // balance_wei: balance_3, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; - // let mut subject = PaymentAdjusterReal::new(); - // subject.logger = Logger::new(test_name); - // let consuming_wallet_masq_balance_minor = 333_000_000_000 + 50_000_000_000; - // let setup_msg = todo!() - // - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: 5_000_000_000_000_000_000_000_000_u128, - // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // // masq_tokens_minor: consuming_wallet_masq_balance_minor, - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt: Some(ResponseSkeleton { - // // client_id: 111, - // // context_id: 234, - // // }), - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::MasqToken, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // let expected_accounts = { - // let account_1_adjusted = PayableAccount { - // balance_wei: 272_000_000_000, - // ..account_1 - // }; - // vec![account_1_adjusted, account_2] - // }; - // assert_eq!(result.accounts, expected_accounts); - // assert_eq!( - // result.response_skeleton_opt, - // Some(ResponseSkeleton { - // client_id: 111, - // context_id: 234 - // }) - // ); - // TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ - // being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ - // at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ - // would not be at least half of the original debt 600,000,000,000")); + let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; + let now = SystemTime::now(); + let wallet_1 = make_wallet("def"); + // account to be adjusted up to maximum + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), + pending_payable_opt: None, + }; + // account to be outweighed and fully taken + let wallet_2 = make_wallet("abc"); + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 111_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), + pending_payable_opt: None, + }; + // account to be disqualified + let wallet_3 = make_wallet("ghk"); + let balance_3 = 600_000_000_000; + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + //TODO no effect!!!! + let transaction_fee_balance_in_minor_units = + U256::from(5_000_000_000_000_000_000_000_000_u128); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = .... wei + let service_fee_balance_in_minor_units = 333_000_000_000 + 50_000_000_000; + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units, + }; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let response_skeleton_opt = Some(ResponseSkeleton { + client_id: 111, + context_id: 234, + }); + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::MasqToken, + response_skeleton_opt, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + let expected_accounts = { + let account_1_adjusted = PayableAccount { + balance_wei: 272_000_000_000, + ..account_1 + }; + vec![account_1_adjusted, account_2] + }; + assert_eq!(result.affordable_accounts, expected_accounts); + assert_eq!(result.response_skeleton_opt, response_skeleton_opt); + assert_eq!( + result.response_skeleton_opt, + Some(ResponseSkeleton { + client_id: 111, + context_id: 234 + }) + ); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ + being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ + at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ + would not be at least half of the original debt 600,000,000,000")); } struct CompetitiveAccountsTestInputs<'a> { @@ -1730,7 +1600,7 @@ mod tests { expected_winning_account_wallet: &'a Wallet, ) { let now = SystemTime::now(); - let consuming_wallet_balance_minor = 100_000_000_000_000_u128 - 1; + let service_fee_balance_in_minor_units = 100_000_000_000_000_u128 - 1; let standard_balance_per_account = 100_000_000_000_000; let standard_age_per_account = 12000; let account_1 = PayableAccount { @@ -1755,32 +1625,23 @@ mod tests { }; let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); - let setup_msg = todo!() - // PayablePaymentSetup { - // qualified_payables, - // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // transaction_fee_minor: u128::MAX, - // masq_tokens_minor: consuming_wallet_balance_minor, - // }, - // estimated_gas_limit_per_transaction: 55_000, - // agreed_transaction_fee_per_computed_unit_major: 150, - // }, - // )), - // response_skeleton_opt: None, - // } - ; - - let adjustment_setup = - todo!() - // PreparedAdjustment { - // qualified_payables, - // agent: Box::new(()), - // adjustment: Adjustment::MasqToken, - // response_skeleton_opt: None, - // ) - ; + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: U256::from(u128::MAX), + service_fee_balance_in_minor_units, + }; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::MasqToken, + response_skeleton_opt: None, + }; let mut result = subject .adjust_payments(adjustment_setup, now) @@ -1794,9 +1655,9 @@ mod tests { test_scenario_name, winning_account.wallet, expected_winning_account_wallet ); assert_eq!( - winning_account.balance_wei, consuming_wallet_balance_minor, + winning_account.balance_wei, service_fee_balance_in_minor_units, "{}: expected full cw balance {}, but the account had {}", - test_scenario_name, winning_account.balance_wei, consuming_wallet_balance_minor + test_scenario_name, winning_account.balance_wei, service_fee_balance_in_minor_units ); assert!( result.is_empty(), @@ -1873,242 +1734,241 @@ mod tests { #[test] fn adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count() { init_test_logging(); - todo!("fix me") - // let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; - // let now = SystemTime::now(); - // // Thrown away as the second for the proposed balance insignificance - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 10_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - // pending_payable_opt: None, - // }; - // // Thrown away as the first one for the proposed balance insignificance - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 55_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - // pending_payable_opt: None, - // }; - // let wallet_3 = make_wallet("ghi"); - // let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - // let account_3 = PayableAccount { - // wallet: wallet_3.clone(), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: last_paid_timestamp_3, - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2, account_3.clone()]; - // let mut subject = PaymentAdjusterReal::new(); - // subject.logger = Logger::new(test_name); - // let consuming_wallet_masq_balance = 300_000_000_000_000_u128; - // let setup_msg = todo!() - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - // // //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // // masq_tokens_minor: consuming_wallet_masq_balance, - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::PriorityTransactionFee { - // affordable_transaction_count: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // assert_eq!( - // result.accounts, - // vec![PayableAccount { - // wallet: wallet_3, - // balance_wei: consuming_wallet_masq_balance, - // last_paid_timestamp: last_paid_timestamp_3, - // pending_payable_opt: None, - // }] - // ); - // assert_eq!(result.response_skeleton_opt, None); - // let log_msg = format!( - // "DEBUG: {test_name}: \n\ - // |Payable Account Balance Wei - // |---------------------------------------------------------- - // |Successfully Adjusted Original - // | Adjusted - // | - // |0x0000000000000000000000000000000000676869 333000000000000 - // | 300000000000000 - // | - // |Ruled Out Original - // | - // |0x0000000000000000000000000000000000616263 10000000000000 - // |0x0000000000000000000000000000000000646566 55000000000" - // ); - // TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; + let now = SystemTime::now(); + // Thrown away as the second for the proposed balance insignificance + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 10_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + // Thrown away as the first one for the proposed balance insignificance + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 55_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + let service_fee_balance_in_minor_units = 300_000_000_000_000_u128; + //TODO this is not affecting anything!!! + let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units, + }; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2, + }, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + assert_eq!( + result.affordable_accounts, + vec![PayableAccount { + wallet: wallet_3, + balance_wei: service_fee_balance_in_minor_units, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }] + ); + assert_eq!(result.response_skeleton_opt, None); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + let log_msg = format!( + "DEBUG: {test_name}: \n\ +|Payable Account Balance Wei +|---------------------------------------------------------- +|Successfully Adjusted Original +| Adjusted +| +|0x0000000000000000000000000000000000676869 333000000000000 +| 300000000000000 +| +|Ruled Out Original +| +|0x0000000000000000000000000000000000616263 10000000000000 +|0x0000000000000000000000000000000000646566 55000000000" + ); + TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] fn error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient( ) { init_test_logging(); - todo!("fix me") - // let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; - // let now = SystemTime::now(); - // //this account gets eliminated in the transaction-fee cut - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 111_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 333_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - // pending_payable_opt: None, - // }; - // let account_3 = PayableAccount { - // wallet: make_wallet("ghi"), - // balance_wei: 222_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2, account_3]; - // let mut subject = PaymentAdjusterReal::new(); - // // This is exactly the amount which will provoke an error - // let cw_masq_balance_minor = (111_000_000_000_000 / 2) - 1; - // subject.logger = Logger::new(test_name); - // let setup_msg = todo!() - // // PayablePaymentSetup { - // // qualified_payables, - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: 5_544_000_000_000_000_u128 - 1, - // // // Gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - // // masq_tokens_minor: cw_masq_balance_minor, - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::PriorityTransactionFee { - // affordable_transaction_count: 2, - // }, - // }; - // - // let result = subject.adjust_payments(adjustment_setup, now); - // - // assert_eq!( - // result, - // Err(PaymentAdjusterError::AnalysisError( - // AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - // number_of_accounts: 2, - // cw_masq_balance_minor - // } - // )) - // ); - // TestLogHandler::new().exists_log_containing(&format!( - // "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ - // facing critical scarcity of MASQ balance. Operation will abort." - // )); + let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; + let now = SystemTime::now(); + //this account gets eliminated in the transaction-fee cut + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3]; + let mut subject = PaymentAdjusterReal::new(); + // This is exactly the amount which will provoke an error + subject.logger = Logger::new(test_name); + let service_fee_balance_in_minor_units = (111_000_000_000_000 / 2) - 1; + //TODO this is not affecting anything!!! + let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units, + }; + let agent = { + let mock = BlockchainAgentMock::default() + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::PriorityTransactionFee { + affordable_transaction_count: 2, + }, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now); + + let err = match result { + Ok(_) => panic!("expected an error but got Ok()"), + Err(e) => e, + }; + assert_eq!( + err, + PaymentAdjusterError::AnalysisError( + AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts: 2, + cw_masq_balance_minor: service_fee_balance_in_minor_units + } + ) + ); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ + facing critical scarcity of MASQ balance. Operation will abort." + )); } #[test] - fn the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies( + fn entry_check_predicts_worth_of_trying_for_potential_adjustment_aptly_despite_many_eliminated_accounts( ) { - todo!() - // let test_name = "the_entry_check_and_the_account_eliminating_adjustment_algorithm_use_opposite_evaluation_strategies"; - // let now = SystemTime::now(); - // // Disqualified in the first iteration - // let account_1 = PayableAccount { - // wallet: make_wallet("abc"), - // balance_wei: 10_000_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - // pending_payable_opt: None, - // }; - // // Disqualified in the second iteration - // let account_2 = PayableAccount { - // wallet: make_wallet("def"), - // balance_wei: 550_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), - // pending_payable_opt: None, - // }; - // // Eventually picked fulfilling the keep condition and returned - // let wallet_3 = make_wallet("ghi"); - // let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - // let balance_3 = 100_000_000_000; - // let account_3 = PayableAccount { - // wallet: wallet_3.clone(), - // balance_wei: balance_3, - // last_paid_timestamp: last_paid_timestamp_3, - // pending_payable_opt: None, - // }; - // let qualified_payables = vec![account_1, account_2, account_3.clone()]; - // let mut subject = PaymentAdjusterReal::new(); - // let logger = Logger::new(test_name); - // subject.logger = logger.clone(); - // // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, - // // the final winning resolution of the third account will work out because of the only - // // additional one. - // // The strategies advance reversed. The initial check seeks the smallest account, - // // the disqualification strategy always takes from the largest accounts first. - // // As a result, we can forecast the chances if the adjustment would succeed, not having to - // // move forward beyond the entry check. - // let consuming_wallet_masq_balance = (balance_3 / 2) + 1; - // let setup_msg = todo!() - // // PayablePaymentSetup { - // // qualified_payables: qualified_payables.clone(), - // // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // // FinancialAndTechDetails { - // // consuming_wallet_balances: ConsumingWalletBalances { - // // transaction_fee_minor: u128::MAX, - // // masq_tokens_minor: consuming_wallet_masq_balance, - // // }, - // // estimated_gas_limit_per_transaction: 77_000, - // // agreed_transaction_fee_per_computed_unit_major: 24, - // // }, - // // )), - // // response_skeleton_opt: None, - // // } - // ; - // let adjustment_setup = PreparedAdjustment { - // original_setup_msg: setup_msg, - // adjustment: Adjustment::MasqToken, - // }; - // - // let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( - // &logger, - // Either::Left(&qualified_payables), - // consuming_wallet_masq_balance, - // ); - // let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // - // assert_eq!(check_result, Ok(true)); - // assert_eq!( - // adjustment_result.accounts, - // vec![PayableAccount { - // wallet: wallet_3, - // balance_wei: consuming_wallet_masq_balance, - // last_paid_timestamp: last_paid_timestamp_3, - // pending_payable_opt: None, - // }] - // ); - // assert_eq!(adjustment_result.response_skeleton_opt, None); + let test_name = "entry_check_predicts_worth_of_trying_for_potential_adjustment_aptly_despite_many_eliminated_accounts"; + let now = SystemTime::now(); + // Disqualified in the first iteration + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 10_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }; + // Disqualified in the second iteration + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 550_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), + pending_payable_opt: None, + }; + // Eventually picked fulfilling the keep condition and returned + let wallet_3 = make_wallet("ghi"); + let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); + let balance_3 = 100_000_000_000; + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: balance_3, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + let logger = Logger::new(test_name); + subject.logger = logger.clone(); + // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, + // the final winning resolution of the third account will work out because of the only + // additional one. + // The strategies advance reversed. The initial check seeks the smallest account, + // the disqualification strategy always takes from the largest accounts first. + // As a result, we can forecast the chances if the adjustment would succeed, not having to + // move forward beyond the entry check. + let service_fee_balance_in_minor_units = (balance_3 / 2) + 1; + //TODO this is not affecting anything!!! + let transaction_fee_balance_in_minor_units = U256::from(u128::MAX); + //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units, + service_fee_balance_in_minor_units, + }; + let agent_id_stamp = ArbitraryIdStamp::new(); + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .consuming_wallet_balances_result(consuming_wallet_balances); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables: qualified_payables.clone(), + agent, + adjustment: Adjustment::MasqToken, + response_skeleton_opt: None, + }; + + let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( + &logger, + Either::Left(&qualified_payables), + service_fee_balance_in_minor_units, + ); + let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + assert_eq!(check_result, Ok(true)); + assert_eq!( + adjustment_result.affordable_accounts, + vec![PayableAccount { + wallet: wallet_3, + balance_wei: service_fee_balance_in_minor_units, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }] + ); + assert_eq!(adjustment_result.response_skeleton_opt, None); + assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp) } struct TransactionFeeTestConfig { @@ -2145,7 +2005,7 @@ mod tests { let ( desired_transaction_fee_price, number_of_payments, - estimated_transaction_fee_limit_per_tx, + estimated_transaction_fee_unit_limit_per_transaction, cw_balance_transaction_fee_major, ) = match transaction_fee_config_opt { Some(conditions) => ( @@ -2173,16 +2033,18 @@ mod tests { .collect() }; let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); - let estimated_transaction_fee_per_transaction = - (estimated_transaction_fee_limit_per_tx * desired_transaction_fee_price) as u128; - + let consuming_wallet_balances = ConsumingWalletBalances { + transaction_fee_balance_in_minor_units: cw_transaction_fee_minor, + service_fee_balance_in_minor_units: cw_service_fee_minor, + }; + let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( + estimated_transaction_fee_unit_limit_per_transaction * desired_transaction_fee_price, + ); let blockchain_agent = BlockchainAgentMock::default() - .consuming_wallet_balances_result(ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: cw_transaction_fee_minor, - service_fee_balance_in_minor_units: cw_service_fee_minor, - }) + .consuming_wallet_balances_result(consuming_wallet_balances) + .consuming_wallet_balances_result(consuming_wallet_balances) .estimated_transaction_fee_per_transaction_result( - estimated_transaction_fee_per_transaction, + estimated_transaction_fee_per_transaction_minor, ); // PayablePaymentSetup { // qualified_payables, From f77c59b95a2e87b3000463b87a6fbe1873c2fd6d Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:59:36 +0800 Subject: [PATCH 082/250] GH-711: split a method of the agent into two, allowing a more convenient use in tests --- node/src/accountant/payment_adjuster/mod.rs | 94 +++---------------- .../payable_scanner/agent_null.rs | 48 +++++----- .../payable_scanner/agent_web3.rs | 16 +++- .../payable_scanner/blockchain_agent.rs | 4 +- .../payable_scanner/test_utils.rs | 23 +++-- .../blockchain_interface_web3/mod.rs | 10 +- 6 files changed, 72 insertions(+), 123 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 80e8952c9..612d83aea 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -94,9 +94,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => return Err(e), }; - let service_fee_balance_minor = agent - .consuming_wallet_balances() - .service_fee_balance_in_minor_units; + let service_fee_balance_minor = agent.service_fee_balance(); match Self::check_need_of_masq_adjustment( &self.logger, Either::Left(qualified_payables), @@ -116,9 +114,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let qualified_payables = setup.qualified_payables; let response_skeleton_opt = setup.response_skeleton_opt; let agent = setup.agent; - let initial_service_fee_balance_minor = agent - .consuming_wallet_balances() - .service_fee_balance_in_minor_units; + let initial_service_fee_balance_minor = agent.service_fee_balance(); let required_adjustment = setup.adjustment; self.initialize_inner(initial_service_fee_balance_minor, required_adjustment, now); @@ -164,9 +160,7 @@ impl PaymentAdjusterReal { ) -> Result, PaymentAdjusterError> { let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction(); - let cw_transaction_fee_balance_minor = agent - .consuming_wallet_balances() - .transaction_fee_balance_in_minor_units; + let cw_transaction_fee_balance_minor = agent.transaction_fee_balance(); let max_doable_tx_count = u128::try_from( cw_transaction_fee_balance_minor / U256::from(per_transaction_requirement_minor), @@ -695,7 +689,6 @@ mod tests { }; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ResponseSkeleton}; - use crate::sub_lib::blockchain_bridge::{ConsumingWalletBalances}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; @@ -1214,13 +1207,8 @@ mod tests { subject.logger = Logger::new(test_name); // for change extremely small cw balance let cw_masq_balance = 1_000; - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: U256::MAX, - service_fee_balance_in_minor_units: cw_masq_balance, - }; let agent = { - let mock = BlockchainAgentMock::default() - .consuming_wallet_balances_result(consuming_wallet_balances); + let mock = BlockchainAgentMock::default().service_fee_balance_result(cw_masq_balance); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1291,14 +1279,10 @@ mod tests { let accounts_sum: u128 = 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; let service_fee_balance_in_minor_units = accounts_sum - 2_000_000_000_000_000_000; - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: U256::MAX, - service_fee_balance_in_minor_units, - }; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1376,16 +1360,10 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units: 10_u128.pow(22), - }; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(10_u128.pow(22)); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1447,17 +1425,10 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); let service_fee_balance_in_minor_units = 111_000_000_000_000_u128 + 333_000_000_000_000; let agent_id_stamp = ArbitraryIdStamp::new(); - //TODO this has no effect!!!!! - let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units, - }; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let response_skeleton_opt = Some(ResponseSkeleton { @@ -1522,20 +1493,12 @@ mod tests { let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - //TODO no effect!!!! - let transaction_fee_balance_in_minor_units = - U256::from(5_000_000_000_000_000_000_000_000_u128); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = .... wei let service_fee_balance_in_minor_units = 333_000_000_000 + 50_000_000_000; - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units, - }; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let response_skeleton_opt = Some(ResponseSkeleton { @@ -1619,15 +1582,11 @@ mod tests { }; let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: U256::from(u128::MAX), - service_fee_balance_in_minor_units, - }; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1756,18 +1715,11 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let service_fee_balance_in_minor_units = 300_000_000_000_000_u128; - //TODO this is not affecting anything!!! - let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units, - }; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1840,16 +1792,9 @@ mod tests { // This is exactly the amount which will provoke an error subject.logger = Logger::new(test_name); let service_fee_balance_in_minor_units = (111_000_000_000_000 / 2) - 1; - //TODO this is not affecting anything!!! - let transaction_fee_balance_in_minor_units = U256::from(5_544_000_000_000_000_u128 - 1); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units, - }; let agent = { let mock = BlockchainAgentMock::default() - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1923,18 +1868,11 @@ mod tests { // As a result, we can forecast the chances if the adjustment would succeed, not having to // move forward beyond the entry check. let service_fee_balance_in_minor_units = (balance_3 / 2) + 1; - //TODO this is not affecting anything!!! - let transaction_fee_balance_in_minor_units = U256::from(u128::MAX); - //gas amount to spent = 3 * 77_000 * 24 [gwei] = 5_544_000_000_000_000 wei - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units, - service_fee_balance_in_minor_units, - }; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .consuming_wallet_balances_result(consuming_wallet_balances); + .service_fee_balance_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -2027,16 +1965,12 @@ mod tests { .collect() }; let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); - let consuming_wallet_balances = ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: cw_transaction_fee_minor, - service_fee_balance_in_minor_units: cw_service_fee_minor, - }; let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( estimated_transaction_fee_unit_limit_per_transaction * desired_transaction_fee_price, ); let blockchain_agent = BlockchainAgentMock::default() - .consuming_wallet_balances_result(consuming_wallet_balances) - .consuming_wallet_balances_result(consuming_wallet_balances) + .transaction_fee_balance_result(cw_transaction_fee_minor) + .service_fee_balance_result(cw_service_fee_minor) .estimated_transaction_fee_per_transaction_result( estimated_transaction_fee_per_transaction_minor, ); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 0a4cad1c9..22e3e680a 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -1,8 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; - -use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use ethereum_types::U256; use masq_lib::logger::Logger; @@ -19,12 +17,14 @@ impl BlockchainAgent for BlockchainAgentNull { 0 } - fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { - self.log_function_call("consuming_wallet_balances()"); - ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: U256::zero(), - service_fee_balance_in_minor_units: 0, - } + fn transaction_fee_balance(&self) -> U256 { + self.log_function_call("transaction_fee_balance()"); + U256::zero() + } + + fn service_fee_balance(&self) -> u128 { + self.log_function_call("service_fee_balance()"); + 0 } fn agreed_fee_per_computation_unit(&self) -> u64 { @@ -77,10 +77,7 @@ impl Default for BlockchainAgentNull { mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::agent_null::BlockchainAgentNull; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; - - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; - use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use web3::types::U256; @@ -133,22 +130,29 @@ mod tests { } #[test] - fn null_agent_consuming_wallet_balances() { + fn null_agent_consuming_transaction_fee_balance() { init_test_logging(); - let test_name = "null_agent_consuming_wallet_balances"; + let test_name = "null_agent_consuming_transaction_fee_balance"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.consuming_wallet_balances(); + let result = subject.transaction_fee_balance(); - assert_eq!( - result, - ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: U256::zero(), - service_fee_balance_in_minor_units: 0 - } - ); - assert_error_log(test_name, "consuming_wallet_balances") + assert_eq!(result, U256::zero()); + assert_error_log(test_name, "transaction_fee_balance") + } + + #[test] + fn null_agent_service_fee_balance() { + init_test_logging(); + let test_name = "null_agent_service_fee_balance"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.service_fee_balance(); + + assert_eq!(result, 0); + assert_error_log(test_name, "service_fee_balance") } #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index c60689122..7b0cc20a1 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -24,8 +24,14 @@ impl BlockchainAgent for BlockchainAgentWeb3 { gas_price * max_gas_limit } - fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { + fn transaction_fee_balance(&self) -> U256 { self.consuming_wallet_balances + .transaction_fee_balance_in_minor_units + } + + fn service_fee_balance(&self) -> u128 { + self.consuming_wallet_balances + .service_fee_balance_in_minor_units } fn agreed_fee_per_computation_unit(&self) -> u64 { @@ -103,8 +109,12 @@ mod tests { assert_eq!(subject.agreed_fee_per_computation_unit(), gas_price_gwei); assert_eq!(subject.consuming_wallet(), &consuming_wallet); assert_eq!( - subject.consuming_wallet_balances(), - consuming_wallet_balances + subject.transaction_fee_balance(), + consuming_wallet_balances.transaction_fee_balance_in_minor_units + ); + assert_eq!( + subject.service_fee_balance(), + consuming_wallet_balances.service_fee_balance_in_minor_units ); assert_eq!(subject.pending_transaction_id(), pending_transaction_id) } diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index e9779f891..e24abc8c1 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::arbitrary_id_stamp_in_trait; -use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use web3::types::U256; @@ -23,7 +22,8 @@ use web3::types::U256; pub trait BlockchainAgent: Send { fn estimated_transaction_fee_per_transaction(&self) -> u128; - fn consuming_wallet_balances(&self) -> ConsumingWalletBalances; + fn transaction_fee_balance(&self) -> U256; + fn service_fee_balance(&self) -> u128; fn agreed_fee_per_computation_unit(&self) -> u64; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index a26e2d440..8e10a476d 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -3,7 +3,6 @@ #![cfg(test)] use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; -use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; @@ -13,7 +12,8 @@ use std::cell::RefCell; #[derive(Default)] pub struct BlockchainAgentMock { estimated_transaction_fee_per_transaction_results: RefCell>, - consuming_wallet_balances_results: RefCell>, + transaction_fee_balance_results: RefCell>, + service_fee_balance_results: RefCell>, agreed_fee_per_computation_unit_results: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, @@ -27,10 +27,12 @@ impl BlockchainAgent for BlockchainAgentMock { .remove(0) } - fn consuming_wallet_balances(&self) -> ConsumingWalletBalances { - self.consuming_wallet_balances_results - .borrow_mut() - .remove(0) + fn transaction_fee_balance(&self) -> U256 { + self.transaction_fee_balance_results.borrow_mut().remove(0) + } + + fn service_fee_balance(&self) -> u128 { + self.service_fee_balance_results.borrow_mut().remove(0) } fn agreed_fee_per_computation_unit(&self) -> u64 { @@ -62,13 +64,18 @@ impl BlockchainAgentMock { self } - pub fn consuming_wallet_balances_result(self, result: ConsumingWalletBalances) -> Self { - self.consuming_wallet_balances_results + pub fn transaction_fee_balance_result(self, result: U256) -> Self { + self.transaction_fee_balance_results .borrow_mut() .push(result); self } + pub fn service_fee_balance_result(self, result: u128) -> Self { + self.service_fee_balance_results.borrow_mut().push(result); + self + } + pub fn agreed_fee_per_computation_unit_result(self, result: u64) -> Self { self.agreed_fee_per_computation_unit_results .borrow_mut() 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 22876c4dc..dd77bb873 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -644,7 +644,6 @@ mod tests { all_chains, make_fake_event_loop_handle, make_tx_hash, TestTransport, }; use crate::db_config::persistent_configuration::PersistentConfigError; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::test_utils::assert_string_contains; use crate::test_utils::http_test_server::TestServer; @@ -1088,13 +1087,8 @@ mod tests { assert_eq!(*get_transaction_id_params, vec![wallet.clone()]); assert_eq!(result.consuming_wallet(), &wallet); assert_eq!(result.pending_transaction_id(), transaction_id); - assert_eq!( - result.consuming_wallet_balances(), - ConsumingWalletBalances { - transaction_fee_balance_in_minor_units: transaction_fee_balance, - service_fee_balance_in_minor_units: masq_balance.as_u128() - } - ); + assert_eq!(result.transaction_fee_balance(), transaction_fee_balance); + assert_eq!(result.service_fee_balance(), masq_balance.as_u128()); assert_eq!(result.agreed_fee_per_computation_unit(), 50); let expected_fee_estimation = ((BlockchainInterfaceWeb3::::web3_gas_limit_const_part(chain) From 0ce8c3cc3dbb6cacf1aafbe28f4389fcc290e086 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:25:30 +0800 Subject: [PATCH 083/250] GH-711: payment adjuster doesn't require the scanner to be mutable --- .../mid_scan_msg_handling/payable_scanner/mod.rs | 2 +- node/src/accountant/scanners/mod.rs | 10 +++++----- node/src/accountant/test_utils.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 0b62c772e..6f6b2acec 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -33,7 +33,7 @@ pub trait SolvencySensitivePaymentInstructor { ) -> Option>; fn perform_payment_adjustment( - &mut self, + &self, setup: PreparedAdjustment, logger: &Logger, ) -> Option; diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index fe64e612b..86fd25490 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -179,7 +179,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_threshold_gauge: Box, - pub payment_adjuster: Box, + pub payment_adjuster: RefCell>, } impl Scanner for PayableScanner { @@ -263,6 +263,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { match self .payment_adjuster + .borrow() .search_for_indispensable_adjustment(&unprotected, &*msg.agent) { Ok(adjustment_opt) => match adjustment_opt { @@ -293,13 +294,12 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { } fn perform_payment_adjustment( - &mut self, + &self, setup: PreparedAdjustment, logger: &Logger, ) -> Option { let now = SystemTime::now(); - //TODO should be PaymentAdjuster behind RefCell? - match self.payment_adjuster.adjust_payments(setup, now) { + match self.payment_adjuster.borrow_mut().adjust_payments(setup, now) { Ok(instructions) => Some(instructions), Err(e) => { warning!( @@ -331,7 +331,7 @@ impl PayableScanner { payable_dao, pending_payable_dao, payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), - payment_adjuster, + payment_adjuster: RefCell::new(payment_adjuster), } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 9fa21d82e..7823c87df 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1488,7 +1488,7 @@ macro_rules! formal_traits_for_payable_mid_scan_msg_handling { } fn perform_payment_adjustment( - &mut self, + &self, _setup: PreparedAdjustment, _logger: &Logger, ) -> Option { From 3e848798ee612ecb548c163210db9d4cd8655c07 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 30 Oct 2023 21:29:33 +0800 Subject: [PATCH 084/250] GH-711: interim commit --- .../payment_adjuster/adjustment_runners.rs | 36 ++++++++++++------- .../age_criterion_calculator.rs | 10 +++--- .../balance_criterion_calculator.rs | 10 +++--- .../criteria_calculators/mod.rs | 33 ++++++++--------- .../payment_adjuster/diagnostics.rs | 6 ++-- node/src/accountant/payment_adjuster/mod.rs | 14 ++++---- 6 files changed, 60 insertions(+), 49 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index d1c4b00e7..f40e2e862 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -8,13 +8,23 @@ use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterR use itertools::Either; use std::vec; +// There are just two runners. Different by the required adjustment, +// either capable of adjusting by the transaction fee and also the service fee, +// or only by the transaction fee. The idea is that only in the initial iteration +// of the possible recursion the adjustment by the transaction fee could be performed. +// In any of those next iterations this feature becomes useless. +// The more featured one also allows to short-circuit for an adjustment with no need +// to adjust accounts by the service fee, therefore each runner can return slightly +// different data types, even though the resulting type of the service fee adjustment +// is always the same. pub trait AdjustmentRunner { type ReturnType; - // This method: - // a) helps with writing tests aimed at edge cases - // b) avoids performing an unnecessary computation for an obvious result - // c) makes the condition in the initial check for adjustment possibility achievable in its pureness + // This special method: + // a) helps with writing tests aimed at edge cases, + // b) avoids performing an unnecessary computation for an obvious result, + // c) makes the condition in the initial check for adjustment possibility achievable + // in its pureness fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, @@ -28,9 +38,9 @@ pub trait AdjustmentRunner { ) -> Self::ReturnType; } -pub struct MasqAndTransactionFeeRunner {} +pub struct TransactionAndServiceFeeRunner {} -impl AdjustmentRunner for MasqAndTransactionFeeRunner { +impl AdjustmentRunner for TransactionAndServiceFeeRunner { type ReturnType = Result< Either, Vec>, PaymentAdjusterError, @@ -69,9 +79,9 @@ impl AdjustmentRunner for MasqAndTransactionFeeRunner { } } -pub struct MasqOnlyRunner {} +pub struct ServiceFeeOnlyRunner {} -impl AdjustmentRunner for MasqOnlyRunner { +impl AdjustmentRunner for ServiceFeeOnlyRunner { type ReturnType = Vec; fn adjust_last_one( @@ -134,8 +144,8 @@ fn empty_or_single_element( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one, empty_or_single_element, AdjustmentRunner, MasqAndTransactionFeeRunner, - MasqOnlyRunner, + adjust_last_one, empty_or_single_element, AdjustmentRunner, TransactionAndServiceFeeRunner, + ServiceFeeOnlyRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; @@ -185,14 +195,14 @@ mod tests { #[test] fn masq_and_transaction_fee_adjust_single_works() { - test_adjust_last_one(MasqAndTransactionFeeRunner {}, |expected_vec| { + test_adjust_last_one(TransactionAndServiceFeeRunner {}, |expected_vec| { Ok(Either::Left(expected_vec)) }) } #[test] fn masq_only_adjust_single_works() { - test_adjust_last_one(MasqOnlyRunner {}, |expected_vec| expected_vec) + test_adjust_last_one(ServiceFeeOnlyRunner {}, |expected_vec| expected_vec) } #[test] @@ -279,7 +289,7 @@ mod tests { // estimated_gas_limit_per_transaction: 100, // }; payment_adjuster.initialize_inner(cw_balance, adjustment, now); - let subject = MasqOnlyRunner {}; + let subject = ServiceFeeOnlyRunner {}; let criteria_and_accounts = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 90b546d39..282ece4cd 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -2,7 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, CalculatorWithNamedMainParameter, + CriterionCalculator, ParameterCriterionCalculator, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ DiagnosticsConfig, @@ -111,8 +111,8 @@ impl CriterionCalculator for AgeCriterionCalculator { } } -impl CalculatorWithNamedMainParameter for AgeCriterionCalculator { - fn main_parameter_name(&self) -> &'static str { +impl ParameterCriterionCalculator for AgeCriterionCalculator { + fn parameter_name(&self) -> &'static str { "AGE" } } @@ -192,7 +192,7 @@ mod tests { AGE_MAIN_EXPONENT, AGE_MULTIPLIER, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorWithNamedMainParameter, CriterionCalculator, + ParameterCriterionCalculator, CriterionCalculator, }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; @@ -270,7 +270,7 @@ mod tests { let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); let subject = AgeCriterionCalculator::new(&payment_adjuster); - let result = subject.main_parameter_name(); + let result = subject.parameter_name(); assert_eq!(result, "AGE") } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 2a807f789..e5f3142a6 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -2,7 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, CalculatorWithNamedMainParameter, + CriterionCalculator, ParameterCriterionCalculator, }; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ DiagnosticsConfig, @@ -57,8 +57,8 @@ impl CriterionCalculator for BalanceCriterionCalculator { } } -impl CalculatorWithNamedMainParameter for BalanceCriterionCalculator { - fn main_parameter_name(&self) -> &'static str { +impl ParameterCriterionCalculator for BalanceCriterionCalculator { + fn parameter_name(&self) -> &'static str { "BALANCE" } } @@ -101,7 +101,7 @@ mod tests { BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorWithNamedMainParameter, CriterionCalculator, + ParameterCriterionCalculator, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; @@ -151,7 +151,7 @@ mod tests { fn calculator_returns_the_right_main_param_name() { let subject = BalanceCriterionCalculator::new(); - let result = subject.main_parameter_name(); + let result = subject.parameter_name(); assert_eq!(result, "BALANCE") } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index a30892bd5..462a92233 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -12,17 +12,18 @@ use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnos use std::fmt::Debug; use std::sync::Mutex; -// Caution: always remember to use checked math operations in the formula! -pub trait CriterionCalculator: CalculatorWithNamedMainParameter { - // The additional trait constrain comes from efforts convert write the API more Rust-like. - // This implementation has its own pros and cons; the little cons for you are that whenever - // you must see the pattern of defining a wrapper for the input of your calculator. Refrain - // from writing a From implementation for third part types to satisfy the requirement. +// Caution: always remember to use checked math operations in the criteria formulas! +pub trait CriterionCalculator: ParameterCriterionCalculator { + // The additional trait constrain comes from efforts to write the API more Rust-like. + // This implementation has its own pros and cons; the little cons are we must learn to + // understand the need to have a special wrapper, for the input of any additional calculator. + // Don't be fooled to try writing a From implementation for third-part data types to satisfy + // the requirements. Because it is disallowed, this specific design has arisen. type Input: for<'a> From<&'a PayableAccount>; // This is the only function you are supposed to implement for your calculator. - // All it does is linking the formula from inside of your calculator (see implementations), - // and exposes it to outside + // All it does is to link the formula from inside of your calculator (see the existing + // implementations), and expose it to outside fn formula(&self) -> &dyn Fn(Self::Input) -> u128; fn calculate_and_add_to_criteria_sum( @@ -59,7 +60,7 @@ pub trait CriterionCalculator: CalculatorWithNamedMainParameter { { if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { compute_progressive_characteristics( - self.main_parameter_name(), + self.parameter_name(), self.diagnostics_config_opt(), self.formula(), ) @@ -78,11 +79,11 @@ impl CriteriaIterator { } } -impl Iterator for CriteriaIterator +impl Iterator for CriteriaIterator where I: Iterator, - C: CriterionCalculator, - ::Input: Debug, + Calculator: CriterionCalculator, + ::Input: Debug, { type Item = (u128, PayableAccount); @@ -94,17 +95,17 @@ where } pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { - fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator + fn iterate_through_payables(self, calculator: C) -> CriteriaIterator where Self: Sized; } impl CriteriaIteratorAdaptor for I { - fn iterate_for_criteria(self, calculator: C) -> CriteriaIterator { + fn iterate_through_payables(self, calculator: C) -> CriteriaIterator { CriteriaIterator::new(self, calculator) } } -pub trait CalculatorWithNamedMainParameter { - fn main_parameter_name(&self) -> &'static str; +pub trait ParameterCriterionCalculator { + fn parameter_name(&self) -> &'static str; } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index ed1c93ba6..5c7b104c8 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -53,7 +53,7 @@ pub fn collection_diagnostics(label: &str, accounts: &[D]) { pub mod separately_defined_diagnostic_functions { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::CalculatorWithNamedMainParameter; + use crate::accountant::payment_adjuster::criteria_calculators::ParameterCriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::sub_lib::wallet::Wallet; @@ -139,7 +139,7 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn calculator_local_diagnostics( + pub fn calculator_local_diagnostics( wallet_ref: &Wallet, calculator: &N, criterion: u128, @@ -150,7 +150,7 @@ pub mod separately_defined_diagnostic_functions { wallet_ref, "PARTIAL CRITERION CALCULATED", "{: Result, PaymentAdjusterError> { let accounts = self.calculate_criteria_and_propose_adjustments_recursively( qualified_accounts, - MasqAndTransactionFeeRunner {}, + TransactionAndServiceFeeRunner {}, )?; match accounts { Either::Left(non_exhausted_accounts) => { @@ -404,7 +404,7 @@ impl PaymentAdjusterReal { let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( remaining, - MasqOnlyRunner {}, + ServiceFeeOnlyRunner {}, ); (here_decided_accounts, down_stream_decided_accounts) @@ -431,8 +431,8 @@ impl PaymentAdjusterReal { accounts_with_zero_criteria: impl Iterator, ) -> Vec<(u128, PayableAccount)> { let criteria_and_accounts = accounts_with_zero_criteria - .iterate_for_criteria(AgeCriterionCalculator::new(self)) - .iterate_for_criteria(BalanceCriterionCalculator::new()); + .iterate_through_payables(AgeCriterionCalculator::new(self)) + .iterate_through_payables(BalanceCriterionCalculator::new()); let collected_accounts_with_criteria = sort_in_descendant_order_by_criteria_sums(criteria_and_accounts); @@ -677,7 +677,7 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::adjustment_runners::MasqAndTransactionFeeRunner; + use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; @@ -1072,7 +1072,7 @@ mod tests { let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( qualified_payables.clone(), - MasqAndTransactionFeeRunner {}, + TransactionAndServiceFeeRunner {}, ) .unwrap() .left() From 85cb295072f89292d3d3838e4134e6aebe38e635 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 31 Oct 2023 01:01:19 +0800 Subject: [PATCH 085/250] GH-711: refactored iter implementations in calculators --- .../payment_adjuster/adjustment_runners.rs | 12 +-- .../age_criterion_calculator.rs | 73 +++++++------- .../balance_criterion_calculator.rs | 62 ++++++------ .../criteria_calculators/mod.rs | 97 +++++++++++++------ node/src/accountant/payment_adjuster/mod.rs | 8 +- .../accountant/payment_adjuster/test_utils.rs | 3 + node/src/accountant/scanners/mod.rs | 6 +- 7 files changed, 151 insertions(+), 110 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index f40e2e862..cf5552f1c 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -144,8 +144,8 @@ fn empty_or_single_element( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one, empty_or_single_element, AdjustmentRunner, TransactionAndServiceFeeRunner, - ServiceFeeOnlyRunner, + adjust_last_one, empty_or_single_element, AdjustmentRunner, ServiceFeeOnlyRunner, + TransactionAndServiceFeeRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; @@ -280,14 +280,6 @@ mod tests { }; let mut payment_adjuster = PaymentAdjusterReal::new(); let cw_balance = 9_000_000; - // let details = FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // transaction_fee_minor: 0, - // masq_tokens_minor: cw_balance, - // }, - // agreed_transaction_fee_per_computed_unit_major: 30, - // estimated_gas_limit_per_transaction: 100, - // }; payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = ServiceFeeOnlyRunner {}; let criteria_and_accounts = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 282ece4cd..ca49aebe2 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -12,6 +12,7 @@ use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::sync::Mutex; use std::time::SystemTime; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; +use crate::standard_impls_for_calculator; const AGE_MAIN_EXPONENT: u32 = 3; const AGE_MULTIPLIER: u128 = 150; @@ -21,12 +22,26 @@ const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; -pub struct AgeCriterionCalculator { +pub struct AgeCriterionCalculator +where + I: Iterator, +{ + iter: I, formula: Box u128>, } -impl AgeCriterionCalculator { - pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { +standard_impls_for_calculator!( + AgeCriterionCalculator, + AgeInput, + "AGE", + AGE_DIAGNOSTICS_CONFIG_OPT +); + +impl AgeCriterionCalculator +where + I: Iterator, +{ + pub fn new(iter: I, payment_adjuster: &PaymentAdjusterReal) -> Self { let now = payment_adjuster.inner.now(); let formula = Box::new(move |wrapped_last_paid_timestamp: AgeInput| { @@ -45,7 +60,7 @@ impl AgeCriterionCalculator { .checked_mul(log_multiplier) .expect("mul overflow") }); - Self { formula } + Self { iter, formula } } fn nonzero_elapsed(now: SystemTime, previous_timestamp: SystemTime) -> u64 { @@ -98,25 +113,6 @@ impl AgeCriterionCalculator { } } -impl CriterionCalculator for AgeCriterionCalculator { - type Input = AgeInput; - - fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { - self.formula.as_ref() - } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> { - &AGE_DIAGNOSTICS_CONFIG_OPT - } -} - -impl ParameterCriterionCalculator for AgeCriterionCalculator { - fn parameter_name(&self) -> &'static str { - "AGE" - } -} - #[derive(Debug, Clone, Copy)] pub struct AgeInput(pub SystemTime); @@ -192,9 +188,10 @@ mod tests { AGE_MAIN_EXPONENT, AGE_MULTIPLIER, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - ParameterCriterionCalculator, CriterionCalculator, + CriterionCalculator, ParameterCriterionCalculator, }; - use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::accountant::payment_adjuster::test_utils::{make_initialized_subject, Sentinel}; + use std::iter::empty; use std::time::{Duration, SystemTime}; #[test] @@ -212,7 +209,7 @@ mod tests { fn nonzero_compute_divisor_works() { let result: Vec<_> = [1, 100, 81, 82, 80] .into_iter() - .map(|secs| AgeCriterionCalculator::nonzero_compute_divisor(secs)) + .map(|secs| AgeCriterionCalculator::::nonzero_compute_divisor(secs)) .collect(); assert_eq!(result, vec![1, 10, 9, 10, 9]) @@ -228,7 +225,7 @@ mod tests { now.checked_sub(Duration::from_secs(2)).unwrap(), ] .into_iter() - .map(|timestamp| AgeCriterionCalculator::nonzero_elapsed(now, timestamp)) + .map(|timestamp| AgeCriterionCalculator::::nonzero_elapsed(now, timestamp)) .collect(); assert_eq!(result, vec![1, 1, 2]) @@ -241,8 +238,12 @@ mod tests { .take(12) .map(|exp| 10_u64.pow(exp)) .map(|seconds_elapsed| { - let divisor = AgeCriterionCalculator::nonzero_compute_divisor(seconds_elapsed); - AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) + let divisor = + AgeCriterionCalculator::::nonzero_compute_divisor(seconds_elapsed); + AgeCriterionCalculator::::compute_descending_multiplier( + seconds_elapsed, + divisor, + ) }) .collect(); @@ -259,7 +260,7 @@ mod tests { fn nonzero_log_works() { let result = vec![0.0, 0.6, 1.3, 1.99999, 2.0, 2.1, 5.0, 9.0] .into_iter() - .map(|num| AgeCriterionCalculator::nonzero_log_value(num)) + .map(|num| AgeCriterionCalculator::::nonzero_log_value(num)) .collect::>(); assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 2, 3]) @@ -268,7 +269,7 @@ mod tests { #[test] fn calculator_returns_the_right_main_param_name() { let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); - let subject = AgeCriterionCalculator::new(&payment_adjuster); + let subject = AgeCriterionCalculator::new(empty(), &payment_adjuster); let result = subject.parameter_name(); @@ -279,7 +280,7 @@ mod tests { fn age_criteria_calculation_works() { let now = SystemTime::now(); let payment_adjuster = make_initialized_subject(now, None, None); - let subject = AgeCriterionCalculator::new(&payment_adjuster); + let subject = AgeCriterionCalculator::new(empty(), &payment_adjuster); let last_paid_timestamp_wrapped = AgeInput( SystemTime::now() .checked_sub(Duration::from_secs(1500)) @@ -293,9 +294,11 @@ mod tests { .duration_since(last_paid_timestamp_wrapped.0) .unwrap() .as_secs(); - let divisor = AgeCriterionCalculator::nonzero_compute_divisor(elapsed_secs); - let log_multiplier = - AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); + let divisor = AgeCriterionCalculator::::nonzero_compute_divisor(elapsed_secs); + let log_multiplier = AgeCriterionCalculator::::compute_descending_multiplier( + elapsed_secs, + divisor, + ); (elapsed_secs as u128) .checked_pow(AGE_MAIN_EXPONENT) .unwrap() diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index e5f3142a6..e4a187032 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -10,16 +10,31 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2}; use std::sync::Mutex; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; +use crate::standard_impls_for_calculator; // This parameter affects the steepness (sensitivity to balance increase) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; -pub struct BalanceCriterionCalculator { +pub struct BalanceCriterionCalculator +where + I: Iterator, +{ + iter: I, formula: Box u128>, } -impl BalanceCriterionCalculator { - pub fn new() -> Self { +standard_impls_for_calculator!( + BalanceCriterionCalculator, + BalanceInput, + "BALANCE", + BALANCE_DIAGNOSTICS_CONFIG_OPT +); + +impl BalanceCriterionCalculator +where + I: Iterator, +{ + pub fn new(iter: I) -> Self { let formula = Box::new(|wrapped_balance_minor: BalanceInput| { let balance_minor = wrapped_balance_minor.0; let binary_weight = log_2(Self::calculate_binary_argument(balance_minor)); @@ -27,7 +42,7 @@ impl BalanceCriterionCalculator { .checked_mul(binary_weight as u128) .expect("mul overflow") }); - Self { formula } + Self { iter, formula } } fn nonzero_log2(balance_minor: u128) -> u32 { @@ -44,25 +59,6 @@ impl BalanceCriterionCalculator { } } -impl CriterionCalculator for BalanceCriterionCalculator { - type Input = BalanceInput; - - fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { - self.formula.as_ref() - } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> { - &BALANCE_DIAGNOSTICS_CONFIG_OPT - } -} - -impl ParameterCriterionCalculator for BalanceCriterionCalculator { - fn parameter_name(&self) -> &'static str { - "BALANCE" - } -} - #[derive(Debug, Clone, Copy)] pub struct BalanceInput(pub u128); @@ -101,9 +97,11 @@ mod tests { BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, }; use crate::accountant::payment_adjuster::criteria_calculators::{ - ParameterCriterionCalculator, CriterionCalculator, + CriterionCalculator, ParameterCriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; + use crate::accountant::payment_adjuster::test_utils::Sentinel; + use std::iter; #[test] fn constants_are_correct() { @@ -122,7 +120,7 @@ mod tests { let result: Vec<_> = arg_values .into_iter() - .map(|arg| BalanceCriterionCalculator::calculate_binary_argument(arg)) + .map(|arg| BalanceCriterionCalculator::::calculate_binary_argument(arg)) .collect(); assert_eq!( @@ -141,7 +139,7 @@ mod tests { fn nonzero_log2_works() { let result: Vec<_> = [0, 5, 66, 100, 131, 132] .into_iter() - .map(|balance| BalanceCriterionCalculator::nonzero_log2(balance)) + .map(|balance| BalanceCriterionCalculator::::nonzero_log2(balance)) .collect(); assert_eq!(result, vec![1, 1, 1, 1, 1, 2]) @@ -149,7 +147,7 @@ mod tests { #[test] fn calculator_returns_the_right_main_param_name() { - let subject = BalanceCriterionCalculator::new(); + let subject = BalanceCriterionCalculator::new(iter::empty()); let result = subject.parameter_name(); @@ -158,15 +156,17 @@ mod tests { #[test] fn balance_criteria_calculation_works() { - let subject = BalanceCriterionCalculator::new(); + let subject = BalanceCriterionCalculator::new(iter::empty()); let balance_wei_wrapped = BalanceInput(111_333_555_777); let result = subject.formula()(balance_wei_wrapped); let expected_result = { - let binary_weight = log_2(BalanceCriterionCalculator::calculate_binary_argument( - balance_wei_wrapped.0, - )); + let binary_weight = log_2( + BalanceCriterionCalculator::::calculate_binary_argument( + balance_wei_wrapped.0, + ), + ); balance_wei_wrapped .0 .checked_mul(binary_weight as u128) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 462a92233..a7972ebe2 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -4,16 +4,21 @@ pub mod age_criterion_calculator; pub mod balance_criterion_calculator; use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ compute_progressive_characteristics, DiagnosticsConfig, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::calculator_local_diagnostics; +use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; use std::sync::Mutex; // Caution: always remember to use checked math operations in the criteria formulas! -pub trait CriterionCalculator: ParameterCriterionCalculator { +pub trait CriterionCalculator: + ParameterCriterionCalculator + Iterator +{ // The additional trait constrain comes from efforts to write the API more Rust-like. // This implementation has its own pros and cons; the little cons are we must learn to // understand the need to have a special wrapper, for the input of any additional calculator. @@ -68,44 +73,80 @@ pub trait CriterionCalculator: ParameterCriterionCalculator { } } -pub(in crate::accountant::payment_adjuster) struct CriteriaIterator { - iter: I, - calculator: C, -} +pub trait CriteriaCalculators { + fn calculate_age_criteria( + self, + payment_adjuster: &PaymentAdjusterReal, + ) -> AgeCriterionCalculator + where + Self: Iterator + Sized; -impl CriteriaIterator { - fn new(iter: I, calculator: C) -> Self { - Self { iter, calculator } - } + fn calculate_balance_criteria(self) -> BalanceCriterionCalculator + where + Self: Iterator + Sized; } -impl Iterator for CriteriaIterator +impl CriteriaCalculators for I where I: Iterator, - Calculator: CriterionCalculator, - ::Input: Debug, { - type Item = (u128, PayableAccount); - - fn next(&mut self) -> Option { - self.iter - .next() - .map(|item| self.calculator.calculate_and_add_to_criteria_sum(item)) + fn calculate_age_criteria( + self, + payment_adjuster: &PaymentAdjusterReal, + ) -> AgeCriterionCalculator { + AgeCriterionCalculator::new(self, payment_adjuster) } -} -pub(in crate::accountant::payment_adjuster) trait CriteriaIteratorAdaptor { - fn iterate_through_payables(self, calculator: C) -> CriteriaIterator - where - Self: Sized; -} - -impl CriteriaIteratorAdaptor for I { - fn iterate_through_payables(self, calculator: C) -> CriteriaIterator { - CriteriaIterator::new(self, calculator) + fn calculate_balance_criteria(self) -> BalanceCriterionCalculator { + BalanceCriterionCalculator::new(self) } } pub trait ParameterCriterionCalculator { fn parameter_name(&self) -> &'static str; } + +#[macro_export] +macro_rules! standard_impls_for_calculator { + ($calculator: tt, $input_type: tt, $param_name: literal, $diagnostics_config_opt: expr) => { + impl Iterator for $calculator + where + I: Iterator, + { + type Item = (u128, PayableAccount); + + fn next(&mut self) -> Option { + self.iter.next().map(|criteria_sum_and_account| { + self.calculate_and_add_to_criteria_sum(criteria_sum_and_account.into()) + }) + } + } + + impl CriterionCalculator for $calculator + where + I: Iterator, + { + type Input = $input_type; + + fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { + self.formula.as_ref() + } + + #[cfg(test)] + fn diagnostics_config_location( + &self, + ) -> &Mutex>> { + &$diagnostics_config_opt + } + } + + impl ParameterCriterionCalculator for $calculator + where + I: Iterator, + { + fn parameter_name(&self) -> &'static str { + $param_name + } + } + }; +} diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 6249d52b9..b02b7dbae 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -14,9 +14,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeRunner, ServiceFeeOnlyRunner, }; -use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::CriteriaIteratorAdaptor; +use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculators}; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; @@ -431,8 +429,8 @@ impl PaymentAdjusterReal { accounts_with_zero_criteria: impl Iterator, ) -> Vec<(u128, PayableAccount)> { let criteria_and_accounts = accounts_with_zero_criteria - .iterate_through_payables(AgeCriterionCalculator::new(self)) - .iterate_through_payables(BalanceCriterionCalculator::new()); + .calculate_age_criteria(self) + .calculate_balance_criteria(); let collected_accounts_with_criteria = sort_in_descendant_order_by_criteria_sums(criteria_and_accounts); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 90141ffc2..98fa6a138 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -10,6 +10,7 @@ use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; +use std::iter::Empty; use std::time::{Duration, SystemTime}; lazy_static! { @@ -61,3 +62,5 @@ pub fn make_extreme_accounts( }) .collect() } + +pub type Sentinel = Empty<(u128, PayableAccount)>; diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 86fd25490..2ac637430 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -299,7 +299,11 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { logger: &Logger, ) -> Option { let now = SystemTime::now(); - match self.payment_adjuster.borrow_mut().adjust_payments(setup, now) { + match self + .payment_adjuster + .borrow_mut() + .adjust_payments(setup, now) + { Ok(instructions) => Some(instructions), Err(e) => { warning!( From be20408daaac71dc0b4469e6164d71b9a0e1a8b9 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:59:24 +0800 Subject: [PATCH 086/250] GH-711: further little refactoring --- node/src/accountant/mod.rs | 59 +++++++++-------- node/src/accountant/payment_adjuster/mod.rs | 19 +++--- node/src/accountant/scanners/mod.rs | 71 ++++++++++++--------- 3 files changed, 82 insertions(+), 67 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 4ba8259e0..4ff539085 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -658,20 +658,18 @@ impl Accountant { .payable .try_skipping_payment_adjustment(msg, &self.logger) { - Some(result) => match result { - Either::Left(complete_msg) => Some(complete_msg), - Either::Right(unaccepted_msg) => { - //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 - match self - .scanners - .payable - .perform_payment_adjustment(unaccepted_msg, &self.logger) - { - Some(instructions) => Some(instructions), - None => None, - } + Some(Either::Left(complete_msg)) => Some(complete_msg), + Some(Either::Right(unaccepted_msg)) => { + //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 + match self + .scanners + .payable + .perform_payment_adjustment(unaccepted_msg, &self.logger) + { + Some(instructions) => Some(instructions), + None => None, } - }, + } None => None, }; @@ -685,8 +683,8 @@ impl Accountant { None => { error!( self.logger, - "Payable scanner could not finish. Preventing to settle already mature \ - payables could have serious consequences" + "Payable scanner could not finish. If matured payables stay untreated long, your \ + creditors may impose a ban on you" ); self.scanners.payable.mark_as_ended(&self.logger); if let Some(response_skeleton) = response_skeleton_opt { @@ -1653,17 +1651,21 @@ mod tests { test_handling_payment_adjuster_error(test_name, payment_adjuster); let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: The current balances do not \ - suffice for a payment for any of the recently qualified payables by the larger part of each. \ - Please fund your consuming wallet in order to avoid being banned from your creditors. Failure \ - reason: Found smaller transaction fee balance than does for a single payment. Number of canceled \ + log_handler.exists_log_containing(&format!( + "WARN: {test_name}: Payment adjustment was \ + considered due to detected insolvency but the current balances are likely to suffice for \ + none of the recently qualified payables, not even by at least a half of any of them, which \ + would still lead to a launch of a payment. Please fund your consuming wallet in order to \ + avoid bans from your creditors. Failure reason: Found smaller transaction fee balance than \ + does for a single payment. Number of canceled \ payments: 1. Transaction fee for a single account: 3,300,000 wei. Current consuming wallet \ - balance: 123,000,000,000 wei.")); + balance: 123,000,000,000 wei." + )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ - Preventing to settle already mature payables could have serious consequences" + If matured payables stay untreated long, your creditors may impose a ban on you" )); } @@ -1679,16 +1681,17 @@ mod tests { test_handling_payment_adjuster_error(test_name, payment_adjuster); let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: Payment adjustment did not \ - succeed arranging executable payments burdened by balance insufficiency. Please fund your \ - consuming wallet in order to avoid being banned from your creditors. Failure reason: Despite \ - the preliminary analysis had expected a possibility to compute some executable adjusted \ - payments, the algorithm eventually rejected them all")); + log_handler.exists_log_containing(&format!( + "WARN: {test_name}: Payment adjustment has not \ + produced any executable payments. Please fund your consuming wallet in order to avoid bans \ + from your creditors. Failure reason: Despite the positive preliminary analysis, \ + no executable adjusted payments could be arranged, the algorithm rejected each payable" + )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ - Preventing to settle already mature payables could have serious consequences" + If matured payables stay untreated long, your creditors may impose a ban on you" )); } @@ -1726,7 +1729,7 @@ mod tests { // therefore we didn't attempt to send the NodeUiMessage TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ - Preventing to settle already mature payables could have serious consequences" + If matured payables stay untreated long, your creditors may impose a ban on you" )); } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b02b7dbae..26d4dced5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -643,8 +643,8 @@ impl Display for PaymentAdjusterError { } => write!( f, "Found smaller transaction fee balance than does for a single payment. \ - Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ - Current consuming wallet balance: {} wei", + Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ + Current consuming wallet balance: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas() @@ -655,18 +655,17 @@ impl Display for PaymentAdjusterError { } => write!( f, "Analysis has projected a likely unacceptable adjustment leaving each \ - of the payable accounts with too a low adjusted amount to pay. Please, proceed by \ - sending funds to your wallet. Number of canceled payments: {}. Current consuming \ - wallet balance: {} wei of MASQ", + of the payable accounts with too a low adjusted amount to pay. Please, proceed \ + by sending funds to your wallet. Number of canceled payments: {}. Current \ + consuming wallet balance: {} wei of MASQ", number_of_accounts.separate_with_commas(), cw_masq_balance_minor.separate_with_commas() ), }, PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( f, - "Despite \ - the preliminary analysis had expected a possibility to compute some executable \ - adjusted payments, the algorithm eventually rejected them all" + "Despite the positive preliminary analysis, no executable adjusted payments \ + could be arranged, the algorithm rejected each payable" ), } } @@ -941,8 +940,8 @@ mod tests { ), ( PaymentAdjusterError::AllAccountsUnexpectedlyEliminated, - "Despite the preliminary analysis had expected a possibility to compute some \ - executable adjusted payments, the algorithm eventually rejected them all", + "Despite the positive preliminary analysis, no executable adjusted payments \ + could be arranged, the algorithm rejected each payable", ), ] .into_iter() diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 2ac637430..9ed6dc969 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -7,7 +7,7 @@ pub mod test_utils; use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDao}; use crate::accountant::db_access_objects::pending_payable_dao::{PendingPayable, PendingPayableDao}; use crate::accountant::db_access_objects::receivable_dao::ReceivableDao; -use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; +use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; @@ -57,6 +57,7 @@ use time::OffsetDateTime; use web3::types::{TransactionReceipt, H256}; use masq_lib::type_obfuscation::Obfuscated; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::{PreparedAdjustment, MultistagePayableScanner, SolvencySensitivePaymentInstructor}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{BlockchainAgentWithContextMessage, QualifiedPayablesMessage}; use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; @@ -266,28 +267,22 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { .borrow() .search_for_indispensable_adjustment(&unprotected, &*msg.agent) { - Ok(adjustment_opt) => match adjustment_opt { - None => Some(Either::Left(OutboundPaymentsInstructions::new( - unprotected, - msg.agent, - msg.response_skeleton_opt, - ))), - Some(adjustment) => { - let prepared_adjustment = PreparedAdjustment::new( - unprotected, - msg.agent, - msg.response_skeleton_opt, - adjustment, - ); - Some(Either::Right(prepared_adjustment)) - } - }, + Ok(adjustment_opt) => Some(Self::handle_adjustment_opt( + adjustment_opt, + msg.agent, + msg.response_skeleton_opt, + unprotected, + )), Err(e) => { - warning!(logger, - "The current balances do not suffice for a payment for any of the recently qualified \ - payables by the larger part of each. Please fund your consuming wallet in order \ - to avoid being banned from your creditors. Failure reason: {}.", e); - + warning!( + logger, + "Payment adjustment was considered due to detected insolvency but the current \ + balances are likely to suffice for none of the recently qualified payables, \ + not even by at least a half of any of them, which would still lead to a launch \ + of a payment. Please fund your consuming wallet in order to avoid bans from \ + your creditors. Failure reason: {}.", + e + ); None } } @@ -308,13 +303,11 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { Err(e) => { warning!( logger, - "Payment adjustment did not succeed \ - arranging executable payments burdened by balance insufficiency. \ - Please fund your consuming wallet in order to avoid being banned \ - from your creditors. Failure reason: {}", + "Payment adjustment has not produced any executable payments. Please \ + fund your consuming wallet in order to avoid bans from your creditors. \ + Failure reason: {}", e ); - None } } @@ -582,6 +575,26 @@ impl PayableScanner { fn expose_payables(&self, obfuscated: Obfuscated) -> Vec { obfuscated.expose_vector() } + + fn handle_adjustment_opt( + adjustment_opt: Option, + agent: Box, + response_skeleton_opt: Option, + unprotected: Vec, + ) -> Either { + match adjustment_opt { + None => Either::Left(OutboundPaymentsInstructions::new( + unprotected, + agent, + response_skeleton_opt, + )), + Some(adjustment) => { + let prepared_adjustment = + PreparedAdjustment::new(unprotected, agent, response_skeleton_opt, adjustment); + Either::Right(prepared_adjustment) + } + } + } } pub struct PendingPayableScanner { @@ -780,8 +793,8 @@ impl PendingPayableScanner { Ok(_) => warning!( logger, "Broken transactions {} marked as an error. You should take over the care \ - of those to make sure your debts are going to be settled properly. At the moment, \ - there is no automated process fixing that without your assistance", + of those to make sure your debts are going to be settled properly. At the \ + moment, there is no automated process fixing that without your assistance", PendingPayableId::serialize_hashes_to_string(&ids) ), Err(e) => panic!( From dce0275f68de687290f15381edbde9e7ca21d763 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:23:58 +0800 Subject: [PATCH 087/250] GH-711: errors refactored --- node/src/accountant/mod.rs | 14 +-- node/src/accountant/payment_adjuster/mod.rs | 114 +++++++++--------- .../accountant/payment_adjuster/verifier.rs | 16 +-- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 4ff539085..6ff941e71 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1020,7 +1020,7 @@ mod tests { }; use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; - use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjusterError}; + use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterError}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_payables_in_test; use crate::accountant::scanners::BeginScanError; @@ -1640,13 +1640,13 @@ mod tests { init_test_logging(); let test_name = "payment_adjuster_throws_out_an_error_from_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + .search_for_indispensable_adjustment_result(Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: 1, per_transaction_requirement_minor: 60 * 55_000, cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), }, - ))); + )); test_handling_payment_adjuster_error(test_name, payment_adjuster); @@ -1702,13 +1702,13 @@ mod tests { let test_name = "error_from_payment_adjuster_is_not_sent_by_an_exclusive_message_to_ui_if_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Err(PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + .search_for_indispensable_adjustment_result(Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: 20, per_transaction_requirement_minor: 40_000_000_000, cw_transaction_fee_balance_minor: U256::from(123), }, - ))); + )); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 26d4dced5..fd70ae4fa 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -76,11 +76,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { qualified_payables: &[PayableAccount], agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError> { - let required_tx_count = qualified_payables.len(); + let number_of_counts = qualified_payables.len(); match Self::determine_transaction_count_limit_by_transaction_fee( agent, - required_tx_count, + number_of_counts, &self.logger, ) { Ok(None) => (), @@ -153,7 +153,7 @@ impl PaymentAdjusterReal { fn determine_transaction_count_limit_by_transaction_fee( agent: &dyn BlockchainAgent, - required_tx_count: usize, + number_of_accounts: usize, logger: &Logger, ) -> Result, PaymentAdjusterError> { let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction(); @@ -168,17 +168,17 @@ impl PaymentAdjusterReal { let (max_doable_tx_count_u16, required_tx_count_u16) = Self::put_bigger_unsigned_integers_under_u16_ceiling( max_doable_tx_count, - required_tx_count, + number_of_accounts, ); if max_doable_tx_count_u16 == 0 { - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: required_tx_count, + Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts, per_transaction_requirement_minor, cw_transaction_fee_balance_minor, }, - )) + ) } else if max_doable_tx_count_u16 >= required_tx_count_u16 { Ok(None) } else { @@ -194,11 +194,11 @@ impl PaymentAdjusterReal { fn put_bigger_unsigned_integers_under_u16_ceiling( max_doable_tx_count: u128, - required_tx_count: usize, + number_of_accounts: usize, ) -> (u16, u16) { ( u16::try_from(max_doable_tx_count).unwrap_or(u16::MAX), - u16::try_from(required_tx_count).unwrap_or(u16::MAX), + u16::try_from(number_of_accounts).unwrap_or(u16::MAX), ) } @@ -615,12 +615,6 @@ pub enum Adjustment { #[derive(Debug, PartialEq, Eq)] pub enum PaymentAdjusterError { - AnalysisError(AnalysisError), - AllAccountsUnexpectedlyEliminated, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum AnalysisError { NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: usize, per_transaction_requirement_minor: u128, @@ -630,38 +624,50 @@ pub enum AnalysisError { number_of_accounts: usize, cw_masq_balance_minor: u128, }, + AllAccountsUnexpectedlyEliminated, } +// +// #[derive(Debug, PartialEq, Eq)] +// pub enum AnalysisError { +// NotEnoughTransactionFeeBalanceForSingleTx { +// number_of_accounts: usize, +// per_transaction_requirement_minor: u128, +// cw_transaction_fee_balance_minor: U256, +// }, +// RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { +// number_of_accounts: usize, +// cw_masq_balance_minor: u128, +// }, +// } impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PaymentAdjusterError::AnalysisError(analysis_error) => match analysis_error { - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts, - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor, - } => write!( - f, - "Found smaller transaction fee balance than does for a single payment. \ + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts, + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, + } => write!( + f, + "Found smaller transaction fee balance than does for a single payment. \ Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ Current consuming wallet balance: {} wei", - number_of_accounts, - per_transaction_requirement_minor.separate_with_commas(), - cw_transaction_fee_balance_minor.separate_with_commas() - ), - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts, - cw_masq_balance_minor, - } => write!( - f, - "Analysis has projected a likely unacceptable adjustment leaving each \ + number_of_accounts, + per_transaction_requirement_minor.separate_with_commas(), + cw_transaction_fee_balance_minor.separate_with_commas() + ), + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts, + cw_masq_balance_minor, + } => write!( + f, + "Analysis has projected a likely unacceptable adjustment leaving each \ of the payable accounts with too a low adjusted amount to pay. Please, proceed \ by sending funds to your wallet. Number of canceled payments: {}. Current \ consuming wallet balance: {} wei of MASQ", - number_of_accounts.separate_with_commas(), - cw_masq_balance_minor.separate_with_commas() - ), - }, + number_of_accounts.separate_with_commas(), + cw_masq_balance_minor.separate_with_commas() + ), PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( f, "Despite the positive preliminary analysis, no executable adjusted payments \ @@ -682,7 +688,7 @@ mod tests { make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ - Adjustment, AnalysisError, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::test_utils::make_payable_account; use crate::accountant::{gwei_to_wei, ResponseSkeleton}; @@ -869,12 +875,12 @@ mod tests { assert_eq!( result, - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + Err( + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, cw_masq_balance_minor: gwei_to_wei(masq_too_low_major) } - )) + ) ); } @@ -900,14 +906,14 @@ mod tests { assert_eq!( result, - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts, per_transaction_requirement_minor: 55_000 * gwei_to_wei::(100), cw_transaction_fee_balance_minor: U256::from(54_000) * gwei_to_wei::(100) } - )) + ) ); } @@ -915,25 +921,21 @@ mod tests { fn payment_adjuster_error_implements_display() { vec![ ( - PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 5, cw_masq_balance_minor: 333_000_000, }, - ), "Analysis has projected a likely unacceptable adjustment leaving each of the payable \ accounts with too a low adjusted amount to pay. Please, proceed by sending funds to \ your wallet. Number of canceled payments: 5. Current consuming wallet balance: \ 333,000,000 wei of MASQ", ), ( - PaymentAdjusterError::AnalysisError( - AnalysisError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: 4, per_transaction_requirement_minor: 70_000_000_000_000, cw_transaction_fee_balance_minor: U256::from(90_000), }, - ), "Found smaller transaction fee balance than does for a single payment. \ Number of canceled payments: 4. Transaction fee for a single account: \ 70,000,000,000,000 wei. Current consuming wallet balance: 90,000 wei", @@ -1811,12 +1813,10 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: 2, - cw_masq_balance_minor: service_fee_balance_in_minor_units - } - ) + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts: 2, + cw_masq_balance_minor: service_fee_balance_in_minor_units + } ); TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index 54dd3745a..bc3192601 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -2,7 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; -use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; +use crate::accountant::payment_adjuster::PaymentAdjusterError; use itertools::Itertools; pub struct MasqAdjustmentPossibilityVerifier {} @@ -29,12 +29,12 @@ impl MasqAdjustmentPossibilityVerifier { Ok(()) } else { let number_of_accounts = accounts.len(); - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + Err( + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts, cw_masq_balance_minor, }, - )) + ) } } } @@ -44,7 +44,7 @@ mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; - use crate::accountant::payment_adjuster::{AnalysisError, PaymentAdjusterError}; + use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_payable_account; fn test_body_for_adjustment_possibility_nearly_rejected( @@ -105,12 +105,12 @@ mod tests { assert_eq!( result, - Err(PaymentAdjusterError::AnalysisError( - AnalysisError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + Err( + PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, cw_masq_balance_minor: cw_masq_balance } - )) + ) ) } } From 6685d5d41d862d0f04feafe82520dff02dfe86ee Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:31:56 +0800 Subject: [PATCH 088/250] GH-711: the main file of the implementation is cleaned up --- .../payment_adjuster/adjustment_runners.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 47 +- .../miscellaneous/helper_functions.rs | 4 +- node/src/accountant/payment_adjuster/mod.rs | 709 +++++++++--------- .../accountant/payment_adjuster/verifier.rs | 22 +- 5 files changed, 401 insertions(+), 383 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index cf5552f1c..586476b99 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -275,7 +275,7 @@ mod tests { let mut account_2 = account_1.clone(); account_2.wallet = wallet_2.clone(); let accounts = vec![account_1, account_2]; - let adjustment = Adjustment::PriorityTransactionFee { + let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 1ee6b8bb0..08415d470 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -14,11 +14,11 @@ use thousands::Separable; use web3::types::U256; const REFILL_RECOMMENDATION: &str = "\ -In order to continue consuming services from other Nodes and avoid delinquency bans it is necessary \ -to allocate more funds in your consuming wallet."; -const LATER_DETECTED_MASQ_SEVERE_SCARCITY: &str = "\ -Passing successful payment adjustment by the transaction fee, but facing critical scarcity of MASQ \ -balance. Operation will abort."; +Please be aware that ignoring your debts might result in delinquency bans. In order to consume \ +services without limitations, you will need to put more funds into your consuming wallet."; +const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ +Passed successfully adjustment by transaction fee but noticing critical scarcity of MASQ balance. \ +Operation will abort."; const BLANK_SPACE: &str = ""; @@ -124,20 +124,24 @@ pub fn info_log_for_disqualified_account( ) { info!( logger, - "Dealing with the consuming wallet being short of MASQ. \ - Seems unavoidable to disregard payable {} at the moment. Reason is the computed \ - possible payment of {} wei would not be at least half of the original debt {}.", + "Shortage of MASQ in your consuming wallet impacts on payable {}, ruled out from this \ + round of payments. The proposed adjustment {} wei was less than half of the recorded \ + debt, {} wei", account.original_account.wallet, account.proposed_adjusted_balance.separate_with_commas(), account.original_account.balance_wei.separate_with_commas() ) } -pub fn log_adjustment_by_masq_required(logger: &Logger, payables_sum: u128, cw_masq_balance: u128) { +pub fn log_adjustment_by_service_fee_is_required( + logger: &Logger, + payables_sum: u128, + cw_masq_balance: u128, +) { warning!( logger, "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ - the MASQ token. Adjustment in their count or the amounts is required.", + the MASQ token. Adjustment in their count or the amounts is required.", payables_sum.separate_with_commas(), cw_masq_balance.separate_with_commas() ); @@ -152,9 +156,9 @@ pub fn log_insufficient_transaction_fee_balance( ) { warning!( logger, - "Gas amount {} wei cannot cover anticipated fees from sending {} \ - transactions. Maximum is {}. The payments need to be adjusted in \ - their count.", + "Transaction fee amount {} wei from your wallet will not cover anticipated \ + fees to send {} transactions. Maximum is {}. The payments count needs to be \ + adjusted.", transaction_fee_minor.separate_with_commas(), required_transactions_count, limiting_count @@ -162,27 +166,28 @@ pub fn log_insufficient_transaction_fee_balance( info!(logger, "{}", REFILL_RECOMMENDATION) } -pub fn log_transaction_fee_adjustment_ok_but_masq_balance_undoable(logger: &Logger) { - error!(logger, "{}", LATER_DETECTED_MASQ_SEVERE_SCARCITY) +pub fn log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger: &Logger) { + error!(logger, "{}", LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY) } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::log_fns::{ - LATER_DETECTED_MASQ_SEVERE_SCARCITY, REFILL_RECOMMENDATION, + LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, REFILL_RECOMMENDATION, }; #[test] fn constants_are_correct() { assert_eq!( REFILL_RECOMMENDATION, - "In order to continue consuming services from other Nodes and avoid delinquency bans it \ - is necessary to allocate more funds in your consuming wallet." + "Please be aware that ignoring your debts might result in delinquency bans. In order to \ + consume services without limitations, you will need to put more funds into your \ + consuming wallet." ); assert_eq!( - LATER_DETECTED_MASQ_SEVERE_SCARCITY, - "Passing successful payment adjustment by the \ - transaction fee, but facing critical scarcity of MASQ balance. Operation will abort." + LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, + "Passed successfully adjustment by transaction fee but noticing critical scarcity of \ + MASQ balance. Operation will abort." ) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 3c28b17d3..52e2af09a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -41,7 +41,7 @@ pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount }) } -pub fn keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest( +pub fn keep_only_transaction_fee_affordable_accounts_and_drop_the_rest( criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, affordable_transaction_count: u16, ) -> Vec<(u128, PayableAccount)> { @@ -291,7 +291,7 @@ pub fn sort_in_descendant_order_by_criteria_sums( .collect() } -pub fn drop_criteria_sums_and_leave_accounts( +pub fn drop_criteria_and_leave_naked_affordable_accounts( criteria_and_accounts: Vec<(u128, PayableAccount)>, ) -> Vec { criteria_and_accounts diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index fd70ae4fa..07ccee22c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -22,8 +22,8 @@ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - before_and_after_debug_msg, log_adjustment_by_masq_required, - log_transaction_fee_adjustment_ok_but_masq_balance_undoable, + before_and_after_debug_msg, log_adjustment_by_service_fee_is_required, + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ @@ -32,7 +32,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAd use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_sums_and_leave_accounts, keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_and_leave_naked_affordable_accounts, keep_only_transaction_fee_affordable_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; @@ -85,7 +85,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(None) => (), Ok(Some(affordable_transaction_count)) => { - return Ok(Some(Adjustment::PriorityTransactionFee { + return Ok(Some(Adjustment::TransactionFeeInPriority { affordable_transaction_count, })) } @@ -93,7 +93,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let service_fee_balance_minor = agent.service_fee_balance(); - match Self::check_need_of_masq_adjustment( + match Self::check_need_of_adjustment_by_service_fee( &self.logger, Either::Left(qualified_payables), service_fee_balance_minor, @@ -119,16 +119,16 @@ impl PaymentAdjuster for PaymentAdjusterReal { let debug_info_opt = self.debug_info_opt(&qualified_payables); - let adjusted_accounts = self.run_adjustment(qualified_payables)?; + let affordable_accounts = self.run_adjustment(qualified_payables)?; debug!( self.logger, "{}", - before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &adjusted_accounts) + before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &affordable_accounts) ); Ok(OutboundPaymentsInstructions { - affordable_accounts: adjusted_accounts, + affordable_accounts, response_skeleton_opt, agent, }) @@ -160,18 +160,18 @@ impl PaymentAdjusterReal { let cw_transaction_fee_balance_minor = agent.transaction_fee_balance(); - let max_doable_tx_count = u128::try_from( + let max_possible_tx_count = u128::try_from( cw_transaction_fee_balance_minor / U256::from(per_transaction_requirement_minor), ) .expect("consuming wallet with too a big balance for the transaction fee"); - let (max_doable_tx_count_u16, required_tx_count_u16) = - Self::put_bigger_unsigned_integers_under_u16_ceiling( - max_doable_tx_count, + let (max_possible_tx_count_u16, required_tx_count_u16) = + Self::big_unsigned_integers_under_u16_ceiling( + max_possible_tx_count, number_of_accounts, ); - if max_doable_tx_count_u16 == 0 { + if max_possible_tx_count_u16 == 0 { Err( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts, @@ -179,33 +179,33 @@ impl PaymentAdjusterReal { cw_transaction_fee_balance_minor, }, ) - } else if max_doable_tx_count_u16 >= required_tx_count_u16 { + } else if max_possible_tx_count_u16 >= required_tx_count_u16 { Ok(None) } else { log_insufficient_transaction_fee_balance( logger, required_tx_count_u16, cw_transaction_fee_balance_minor, - max_doable_tx_count_u16, + max_possible_tx_count_u16, ); - Ok(Some(max_doable_tx_count_u16)) + Ok(Some(max_possible_tx_count_u16)) } } - fn put_bigger_unsigned_integers_under_u16_ceiling( - max_doable_tx_count: u128, + fn big_unsigned_integers_under_u16_ceiling( + max_possible_tx_count: u128, number_of_accounts: usize, ) -> (u16, u16) { ( - u16::try_from(max_doable_tx_count).unwrap_or(u16::MAX), + u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), u16::try_from(number_of_accounts).unwrap_or(u16::MAX), ) } - fn check_need_of_masq_adjustment( + fn check_need_of_adjustment_by_service_fee( logger: &Logger, payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, - cw_masq_balance_minor: u128, + cw_service_fee_balance_minor: u128, ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { Either::Left(accounts) => accounts.iter().collect(), @@ -215,17 +215,22 @@ impl PaymentAdjusterReal { .collect(), }; - let required_masq_sum: u128 = sum_as(&qualified_payables, |account: &&PayableAccount| { - account.balance_wei - }); + let required_service_fee_sum: u128 = + sum_as(&qualified_payables, |account: &&PayableAccount| { + account.balance_wei + }); - if cw_masq_balance_minor >= required_masq_sum { + if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { MasqAdjustmentPossibilityVerifier {} - .verify_adjustment_possibility(&qualified_payables, cw_masq_balance_minor)?; + .verify_adjustment_possibility(&qualified_payables, cw_service_fee_balance_minor)?; - log_adjustment_by_masq_required(logger, required_masq_sum, cw_masq_balance_minor); + log_adjustment_by_service_fee_is_required( + logger, + required_service_fee_sum, + cw_service_fee_balance_minor, + ); Ok(true) } } @@ -237,7 +242,7 @@ impl PaymentAdjusterReal { now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { - Adjustment::PriorityTransactionFee { + Adjustment::TransactionFeeInPriority { affordable_transaction_count, } => Some(affordable_transaction_count), Adjustment::MasqToken => None, @@ -262,11 +267,11 @@ impl PaymentAdjusterReal { )?; match accounts { Either::Left(non_exhausted_accounts) => { - let accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( + let affordable_accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( non_exhausted_accounts, self.inner.original_cw_masq_balance_minor(), ); - Ok(accounts_by_fully_exhausted_cw) + Ok(affordable_accounts_by_fully_exhausted_cw) } Either::Right(finalized_accounts) => Ok(finalized_accounts), } @@ -304,36 +309,36 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let weighted_accounts_affordable_by_transaction_fee = - keep_only_transaction_fee_affordable_count_of_accounts_and_drop_the_rest( + let accounts_with_criteria_affordable_by_transaction_fee = + keep_only_transaction_fee_affordable_accounts_and_drop_the_rest( criteria_and_accounts_in_descending_order, already_known_affordable_transaction_count, ); let unallocated_balance = self.inner.unallocated_cw_masq_balance_minor(); - let is_masq_adjustment_needed = match Self::check_need_of_masq_adjustment( + let is_service_fee_adjustment_needed = match Self::check_need_of_adjustment_by_service_fee( &self.logger, - Either::Right(&weighted_accounts_affordable_by_transaction_fee), + Either::Right(&accounts_with_criteria_affordable_by_transaction_fee), unallocated_balance, ) { Ok(answer) => answer, Err(e) => { - log_transaction_fee_adjustment_ok_but_masq_balance_undoable(&self.logger); + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); return Err(e); } }; - match is_masq_adjustment_needed { + match is_service_fee_adjustment_needed { true => { let adjustment_result_before_verification = self .propose_possible_adjustment_recursively( - weighted_accounts_affordable_by_transaction_fee, + accounts_with_criteria_affordable_by_transaction_fee, ); Ok(Either::Left(adjustment_result_before_verification)) } false => { - let finalized_accounts = drop_criteria_sums_and_leave_accounts( - weighted_accounts_affordable_by_transaction_fee, + let finalized_accounts = drop_criteria_and_leave_naked_affordable_accounts( + accounts_with_criteria_affordable_by_transaction_fee, ); Ok(Either::Right(finalized_accounts)) } @@ -394,7 +399,7 @@ impl PaymentAdjusterReal { vec![] } TreatOutweighedAccounts(outweighed) => { - self.adjust_cw_balance_down_as_result_of_this_last_iteration(&outweighed); + self.adjust_cw_balance_down_as_result_of_current_iteration(&outweighed); outweighed } }; @@ -555,6 +560,10 @@ impl PaymentAdjusterReal { } } + // the term "outweighed account" comes from a phenomenon with the criteria sum of an account + // increased significantly based on a different parameter than the debt size, which could've + // easily caused we would've granted the account (much) more money than what the accountancy + // has recorded. fn handle_possibly_outweighed_accounts( &self, non_finalized_adjusted_accounts: Vec, @@ -576,7 +585,7 @@ impl PaymentAdjusterReal { } } - fn adjust_cw_balance_down_as_result_of_this_last_iteration( + fn adjust_cw_balance_down_as_result_of_current_iteration( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { @@ -610,7 +619,7 @@ impl PaymentAdjusterReal { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Adjustment { MasqToken, - PriorityTransactionFee { affordable_transaction_count: u16 }, + TransactionFeeInPriority { affordable_transaction_count: u16 }, } #[derive(Debug, PartialEq, Eq)] @@ -622,23 +631,10 @@ pub enum PaymentAdjusterError { }, RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: usize, - cw_masq_balance_minor: u128, + cw_service_fee_balance_minor: u128, }, AllAccountsUnexpectedlyEliminated, } -// -// #[derive(Debug, PartialEq, Eq)] -// pub enum AnalysisError { -// NotEnoughTransactionFeeBalanceForSingleTx { -// number_of_accounts: usize, -// per_transaction_requirement_minor: u128, -// cw_transaction_fee_balance_minor: U256, -// }, -// RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { -// number_of_accounts: usize, -// cw_masq_balance_minor: u128, -// }, -// } impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -650,21 +646,21 @@ impl Display for PaymentAdjusterError { } => write!( f, "Found smaller transaction fee balance than does for a single payment. \ - Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ - Current consuming wallet balance: {} wei", + Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ + Current consuming wallet balance: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas() ), PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts, - cw_masq_balance_minor, + cw_service_fee_balance_minor: cw_masq_balance_minor, } => write!( f, "Analysis has projected a likely unacceptable adjustment leaving each \ - of the payable accounts with too a low adjusted amount to pay. Please, proceed \ - by sending funds to your wallet. Number of canceled payments: {}. Current \ - consuming wallet balance: {} wei of MASQ", + of the payable accounts with too a low adjusted amount to pay. Please, proceed \ + by sending funds to your wallet. Number of canceled payments: {}. Current \ + consuming wallet balance: {} wei of MASQ", number_of_accounts.separate_with_commas(), cw_masq_balance_minor.separate_with_commas() ), @@ -707,50 +703,43 @@ mod tests { use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; #[test] - #[should_panic( - expected = "Broken code: Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" - )] + #[should_panic(expected = "Broken code: Called the null implementation of \ + the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner")] fn payment_adjuster_new_is_created_with_inner_null() { let result = PaymentAdjusterReal::new(); let _ = result.inner.unallocated_cw_masq_balance_minor(); } - struct PayableBalancesAndCWBalanceTestConfig { - balances_of_accounts: Either, Vec>, - cw_balance_major: u64, - } - #[test] - fn search_for_indispensable_adjustment_negative_answer() { + fn search_for_indispensable_adjustment_gives_negative_answer() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_negative_answer"; + let test_name = "search_for_indispensable_adjustment_gives_negative_answer"; let mut subject = PaymentAdjusterReal::new(); - let logger = Logger::new(test_name); - subject.logger = logger; + subject.logger = Logger::new(test_name); // MASQ balance > payments - let input_1 = make_qualified_payables_and_blockchain_agent_for_initial_check( - Some(PayableBalancesAndCWBalanceTestConfig { + let input_1 = make_test_input_for_initial_check( + Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Right(vec![ gwei_to_wei::(85), gwei_to_wei::(15) - 1, ]), - cw_balance_major: 100, + cw_balance_minor: gwei_to_wei(100_u64), }), None, ); // MASQ balance == payments - let input_2 = make_qualified_payables_and_blockchain_agent_for_initial_check( - Some(PayableBalancesAndCWBalanceTestConfig { + let input_2 = make_test_input_for_initial_check( + Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Left(vec![85, 15]), - cw_balance_major: 100, + cw_balance_minor: gwei_to_wei(100_u64), }), None, ); // transaction fee balance > payments - let input_3 = make_qualified_payables_and_blockchain_agent_for_initial_check( + let input_3 = make_test_input_for_initial_check( None, - Some(TransactionFeeTestConfig { + Some(TestConfigForTransactionFee { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_limit_per_transaction: 53_000, @@ -758,9 +747,9 @@ mod tests { }), ); // transaction fee balance == payments - let input_4 = make_qualified_payables_and_blockchain_agent_for_initial_check( + let input_4 = make_test_input_for_initial_check( None, - Some(TransactionFeeTestConfig { + Some(TestConfigForTransactionFee { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_limit_per_transaction: 53_000, @@ -787,89 +776,87 @@ mod tests { fn search_for_indispensable_adjustment_positive_for_transaction_fee() { init_test_logging(); let test_name = "search_for_indispensable_adjustment_positive_for_transaction_fee"; - // means a confidently big balance is picked in the behind - let masq_balances_setup_opt = None; - let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); - subject.logger = logger; + subject.logger = Logger::new(test_name); let number_of_accounts = 3; - let (qualified_payables, agent) = - make_qualified_payables_and_blockchain_agent_for_initial_check( - masq_balances_setup_opt, - Some(TransactionFeeTestConfig { - agreed_transaction_fee_per_computed_unit_major: 100, - number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, - cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, - }), - ); + // means a confidently big balance is picked in the behind + let masq_balances_config_opt = None; + let (qualified_payables, agent) = make_test_input_for_initial_check( + masq_balances_config_opt, + Some(TestConfigForTransactionFee { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, + cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, + }), + ); let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!( result, - Ok(Some(Adjustment::PriorityTransactionFee { + Ok(Some(Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2 })) ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Gas amount 16,499,999,000,000,000 wei \ - cannot cover anticipated fees from sending 3 transactions. Maximum is 2. \ - The payments need to be adjusted in their count." + "WARN: {test_name}: Transaction fee amount 16,499,999,000,000,000 wei \ + from your wallet will not cover anticipated fees to send 3 transactions. \ + Maximum is 2. The payments count needs to be adjusted." + )); + log_handler.exists_log_containing(&format!( + "INFO: {test_name}: Please be aware that \ + ignoring your debts might result in delinquency bans. In order to consume services without \ + limitations, you will need to put more funds into your consuming wallet." )); - log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue consuming \ - services from other Nodes and avoid delinquency bans it is necessary to allocate more funds in \ - your consuming wallet.")); } #[test] - fn search_for_indispensable_adjustment_positive_for_masq_token() { + fn search_for_indispensable_adjustment_positive_for_service_fee_token() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_masq_token"; + let test_name = "search_for_indispensable_adjustment_positive_for_service_fee_token"; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; - let (qualified_payables, agent) = - make_qualified_payables_and_blockchain_agent_for_initial_check( - Some(PayableBalancesAndCWBalanceTestConfig { - balances_of_accounts: Either::Right(vec![ - gwei_to_wei::(85), - gwei_to_wei::(15) + 1, - ]), - cw_balance_major: 100, - }), - None, - ); + let (qualified_payables, agent) = make_test_input_for_initial_check( + Some(TestConfigForServiceFeeBalances { + balances_of_accounts: Either::Right(vec![ + gwei_to_wei::(85), + gwei_to_wei::(15) + 1, + ]), + cw_balance_minor: gwei_to_wei(100_u64), + }), + None, + ); let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); assert_eq!(result, Ok(Some(Adjustment::MasqToken))); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,000,001 \ - wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ token. \ - Adjustment in their count or the amounts is required.")); - log_handler.exists_log_containing(&format!("INFO: {test_name}: In order to continue consuming services \ - from other Nodes and avoid delinquency bans it is necessary to allocate more funds in your consuming \ - wallet.")); + wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ \ + token. Adjustment in their count or the amounts is required.")); + log_handler.exists_log_containing(&format!( + "INFO: {test_name}: Please be aware that \ + ignoring your debts might result in delinquency bans. In order to consume services without \ + limitations, you will need to put more funds into your consuming wallet." + )); } #[test] - fn checking_three_accounts_positive_for_transaction_fee_but_mask_balance_is_unbearably_low() { - let test_name = "checking_three_accounts_positive_for_transaction_fee_but_mask_balance_is_unbearably_low"; - let masq_too_low_major = 120 / 2 - 1; // this would normally kick a serious error - let masq_balances_setup_opt = Some(PayableBalancesAndCWBalanceTestConfig { + fn checking_three_accounts_positive_for_transaction_fee_but_service_fee_balance_is_unbearably_low( + ) { + let test_name = "checking_three_accounts_positive_for_transaction_fee_but_service_fee_balance_is_unbearably_low"; + let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; // this would normally kick a serious error + let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Left(vec![120, 300, 500]), - cw_balance_major: masq_too_low_major, + cw_balance_minor: cw_service_fee_balance_minor, }); let (qualified_payables, agent) = - make_qualified_payables_and_blockchain_agent_for_initial_check( - masq_balances_setup_opt, - None, - ); + make_test_input_for_initial_check(service_fee_balances_config_opt, None); let mut subject = PaymentAdjusterReal::new(); - let logger = Logger::new(test_name); - subject.logger = logger; + subject.logger = Logger::new(test_name); let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); @@ -878,7 +865,7 @@ mod tests { Err( PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, - cw_masq_balance_minor: gwei_to_wei(masq_too_low_major) + cw_service_fee_balance_minor } ) ); @@ -888,19 +875,18 @@ mod tests { fn not_enough_transaction_fee_balance_for_even_a_single_transaction() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; - let (qualified_payables, agent) = - make_qualified_payables_and_blockchain_agent_for_initial_check( - Some(PayableBalancesAndCWBalanceTestConfig { - balances_of_accounts: Either::Left(vec![123]), - cw_balance_major: 444, - }), - Some(TransactionFeeTestConfig { - agreed_transaction_fee_per_computed_unit_major: 100, - number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, - cw_transaction_fee_balance_major: 54_000 * 100, - }), - ); + let (qualified_payables, agent) = make_test_input_for_initial_check( + Some(TestConfigForServiceFeeBalances { + balances_of_accounts: Either::Left(vec![123]), + cw_balance_minor: 444, + }), + Some(TestConfigForTransactionFee { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts, + estimated_transaction_fee_units_limit_per_transaction: 55_000, + cw_transaction_fee_balance_major: 54_000 * 100, + }), + ); let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); @@ -923,7 +909,7 @@ mod tests { ( PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 5, - cw_masq_balance_minor: 333_000_000, + cw_service_fee_balance_minor: 333_000_000, }, "Analysis has projected a likely unacceptable adjustment leaving each of the payable \ accounts with too a low adjusted amount to pay. Please, proceed by sending funds to \ @@ -950,7 +936,7 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } - fn u16_max_plus_minus_correction(correction: i8) -> usize { + fn plus_minus_correction_of_u16_max(correction: i8) -> usize { if correction < 0 { (u16::MAX - correction.abs() as u16) as usize } else { @@ -959,14 +945,14 @@ mod tests { } #[test] - fn there_is_u16_ceiling_for_doable_txs_count() { + fn there_is_u16_ceiling_for_possible_tx_count() { let result = [-3_i8, -1, 0, 1, 10] .into_iter() - .map(|correction| u16_max_plus_minus_correction(correction) as u128) - .map(|max_doable_txs_count_u256| { + .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) + .map(|max_possible_tx_count| { let (doable_txs_count, _) = - PaymentAdjusterReal::put_bigger_unsigned_integers_under_u16_ceiling( - max_doable_txs_count_u256, + PaymentAdjusterReal::big_unsigned_integers_under_u16_ceiling( + max_possible_tx_count, 123, ); doable_txs_count @@ -980,13 +966,13 @@ mod tests { } #[test] - fn there_is_u16_ceiling_for_required_txs_count() { + fn there_is_u16_ceiling_for_number_of_accounts() { let result = [-9_i8, -1, 0, 1, 5] .into_iter() - .map(|correction| u16_max_plus_minus_correction(correction)) + .map(|correction| plus_minus_correction_of_u16_max(correction)) .map(|required_tx_count_usize| { let (_, required_tx_count) = - PaymentAdjusterReal::put_bigger_unsigned_integers_under_u16_ceiling( + PaymentAdjusterReal::big_unsigned_integers_under_u16_ceiling( 123, required_tx_count_usize, ); @@ -1028,7 +1014,7 @@ mod tests { subject.calculate_criteria_sums_for_accounts(qualified_payables); let mut previous_criteria_sum = u128::MAX; - let only_accounts = criteria_and_accounts + let accounts_alone = criteria_and_accounts .into_iter() .map(|(criteria_sum, account)| { assert!( @@ -1041,23 +1027,21 @@ mod tests { account }) .collect::>(); - assert_eq!(only_accounts, vec![account_3, account_1, account_2]) + assert_eq!(accounts_alone, vec![account_3, account_1, account_2]) } - #[test] - fn minor_but_highly_aged_debt_takes_priority_as_called_outweighed_and_stays_at_its_original_balance( - ) { + fn minor_but_a_lot_aged_debt_is_prioritized_outweighed_and_stays_as_the_full_original_balance() + { let now = SystemTime::now(); - let cw_masq_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; - let mut subject = make_initialized_subject(now, Some(cw_masq_balance), None); + let cw_service_fee_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; + let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance), None); let balance_1 = 1_500_000_000_000; let balance_2 = 25_000_000; let wallet_1 = make_wallet("blah"); - let last_paid_timestamp_1 = now.checked_sub(Duration::from_secs(5_500)).unwrap(); let account_1 = PayableAccount { wallet: wallet_1, balance_wei: balance_1, - last_paid_timestamp: last_paid_timestamp_1, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5_500)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { @@ -1077,7 +1061,7 @@ mod tests { .left() .unwrap(); - // First, a presentation of why this test is important + // First, let's have an example of why this test is important let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(qualified_payables); let criteria_total = criteria_total(&criteria_and_accounts); @@ -1086,15 +1070,15 @@ mod tests { let proposed_adjusted_balance_2 = proposed_adjustments[1].proposed_adjusted_balance; // The criteria sum of the second account grew very progressively due to the effect of the greater age; // consequences would've been that redistributing the new balances according to the computed criteria would've - // attributed the second account with more tokens to pay than it would've had before the test started; - // to prevent it, we set a logical rule that no account can ever demand more than the 100% of itself + // attributed the second account with a larger amount to pay than it would've had before the test started; + // to prevent it, we set a rule that no account can ever demand more than 100% of itself assert!( proposed_adjusted_balance_2 > 10 * balance_2, - "we expected the proposed balance \ - much bigger than the original which is {} but it was {}", + "we expected the proposed balance much bigger than the original which is {} but it was {}", balance_2, proposed_adjusted_balance_2 ); + // So the assertion above shows the concern true. let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!(first_returned_account.original_account, account_2); @@ -1115,23 +1099,30 @@ mod tests { } #[test] - fn account_never_becomes_outweighed_and_stuck_with_balance_higher_than_the_cw_balance_has_because_there_are_accounts_to_disqualify_first( + fn outweighed_account_never_demands_more_than_cw_balance_because_disqualified_accounts_go_first( ) { - // NOTE that the same is true for more outweighed accounts together that would require more than the whole cw balance, therefore there is no such a test either. - // This test answers what is happening when the cw MASQ balance cannot cover the outweighed accounts at the first try but if there are outweighed accounts - // some other accounts must be also around of which some are under the disqualification limit and one of these would definitely be heading to disqualification. - // With enough money, the other account might not need to meet disqualification which means the initial concern is still groundless: there must be enough money - // to cover the outweighed account if there is other one which is qualified neither as outweighed or disqualified. + // NOTE that the same is true for more outweighed accounts that would require more than + // the whole cw balance together, therefore there is no such a test either. + // This test answers the question what is happening when the cw MASQ balance cannot cover + // the outweighed accounts, which is just a hypothesis we can never reach in the reality. + // If there are outweighed accounts some other accounts must be also around of which some + // are under the disqualification limit pointing to one that would definitely head to its + // disqualification. + // With enough money, the other account might not meet disqualification which means, though, + // the initial concern is still groundless: there must be enough money at the moment to + // cover the outweighed account if there is another one which is considered neither as + // outweighed or disqualified. const SECONDS_IN_3_DAYS: u64 = 259_200; let test_name = - "account_never_becomes_outweighed_and_stuck_with_balance_higher_than_the_cw_balance_has_because_there_are_accounts_to_disqualify_first"; + "outweighed_account_never_demands_more_than_cw_balance_because_disqualified_accounts_go_first"; let now = SystemTime::now(); let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; let account_1 = PayableAccount { wallet: make_wallet("blah"), balance_wei: 1_000_000_000_000, last_paid_timestamp: now - // Greater age like this together with smaller balance usually causes the account to be qualified as outweighed + // Greater age like this together with smaller balance usually causes the account + // to come outweighed .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) .unwrap(), pending_payable_opt: None, @@ -1162,14 +1153,16 @@ mod tests { } => remaining, x => panic!("we expected to see a disqualified account but got: {:?}", x), }; + // We eliminated (disqualified) the other account than which was going to qualify as + // outweighed assert_eq!(remaining, vec![account_1]) } #[test] - fn there_are_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts( - ) { + fn there_is_door_leading_out_if_we_accidentally_wind_up_with_last_account_also_disqualified() { + // Not really an expected behavior but cannot be ruled out with an absolute confidence init_test_logging(); - let test_name = "there_are_doors_leading_out_if_we_happen_to_end_up_with_disqualified_account_while_no_remaining_accounts"; + let test_name = "there_is_door_leading_out_if_we_accidentally_wind_up_with_last_account_also_disqualified"; let mut subject = make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { @@ -1189,12 +1182,13 @@ mod tests { } #[test] - fn loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_overly_large_numbers( - ) { + fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { init_test_logging(); - let test_name = "loading_the_entire_process_with_exaggerated_debt_conditions_to_see_if_it_handles_such_big_numbers"; + let test_name = + "overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely"; let now = SystemTime::now(); - // each of 3 accounts contains the full token supply is 10 years old which generates extremely big numbers in the criteria + // Each of the 3 accounts refers to a debt sized as the entire masq token supply and being + // 10 years old which generates enormously large numbers in the criteria let qualified_payables = { let debt_age_in_months = vec![120, 120, 120]; make_extreme_accounts( @@ -1204,10 +1198,11 @@ mod tests { }; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - // for change extremely small cw balance - let cw_masq_balance = 1_000; + // In turn, extremely small cw balance + let cw_service_fee_balance = 1_000; let agent = { - let mock = BlockchainAgentMock::default().service_fee_balance_result(cw_masq_balance); + let mock = + BlockchainAgentMock::default().service_fee_balance_result(cw_service_fee_balance); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1219,21 +1214,20 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // None on the output, because the proposed final balances are way lower than (at least) the half of the original balances; - // normally, the initial feasibility check wouldn't allow this + // None on the output, because all the accounts were eventually disqualified; + // normally, the feasibility check at the entrance wouldn't allow this assert_eq!(result.affordable_accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( - "INFO: {test_name}: Dealing with the consuming wallet being short of MASQ. \ - Seems unavoidable to disregard payable {wallet} at the moment. \ - Reason is the computed possible payment of {} wei \ - would not be at least half of the original debt {}", + "INFO: {test_name}: Shortage of MASQ in your consuming wallet impacts on payable \ + {wallet}, ruled out from this round of payments. The proposed adjustment {} wei \ + was less than half of the recorded debt, {} wei", proposed_adjusted_balance_in_this_iteration.separate_with_commas(), (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() ) }; let log_handler = TestLogHandler::new(); - // Notice that the proposals grow as one disqualified account drops in each iteration + // Notice that the proposals grow as one disqualified account drops out in each iteration log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616830", 333, @@ -1249,14 +1243,15 @@ mod tests { } #[test] - fn adjust_payments_when_the_initial_transaction_count_evens_the_final_count() { + fn initial_accounts_count_evens_the_payments_count() { + // Meaning adjustment by service fee but no account elimination init_test_logging(); - let test_name = "adjust_payments_when_the_initial_transaction_count_evens_the_final_count"; + let test_name = "initial_accounts_count_evens_the_payments_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 4_444_444_444_444_444_444, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_234)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { @@ -1287,7 +1282,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::MasqToken, //this means the computation happens regardless the actual transaction_fee balance limitations + adjustment: Adjustment::MasqToken, response_skeleton_opt: None, }; @@ -1295,15 +1290,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { - balance_wei: 3_918_231_688_187_775_576, + balance_wei: 3_929_064_899_188_044_996, ..account_1 }; let account_2_adjusted = PayableAccount { - balance_wei: 5_921_593_128_688_275_336, + balance_wei: 5_915_861_818_146_926_543, ..account_2 }; let account_3_adjusted = PayableAccount { - balance_wei: 5_271_286_293_568_393_532, + balance_wei: 5_266_184_393_109_472_905, ..account_3 }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] @@ -1322,20 +1317,20 @@ mod tests { | Adjusted | |0x0000000000000000000000000000000000646566 6000000000000000000 -| 5921593128688275336 +| 5915861818146926543 |0x0000000000000000000000000000000000676869 6666666666000000000 -| 5271286293568393532 +| 5266184393109472905 |0x0000000000000000000000000000000000616263 4444444444444444444 -| 3918231688187775576" +| 3929064899188044996" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } #[test] - fn adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large( - ) { + fn only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices() { init_test_logging(); - let test_name = "adjust_payments_when_only_transaction_fee_limits_the_final_transaction_count_and_the_masq_balance_is_comfortably_large"; + let test_name = + "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), @@ -1368,7 +1363,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::PriorityTransactionFee { + adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, response_skeleton_opt: None, @@ -1376,6 +1371,8 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + // The account 3 takes the first place for its age + // (it weights more if the balance is so small) assert_eq!(result.affordable_accounts, vec![account_3, account_2]); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); @@ -1399,7 +1396,11 @@ mod tests { } #[test] - fn both_balances_are_insufficient_but_adjustment_by_masq_will_not_affect_the_accounts_count() { + fn both_balances_insufficient_but_adjustment_by_service_fee_will_not_affect_the_payments_count() + { + // The course of events: + // 1) adjustment by transaction fee (always means accounts elimination), + // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); let account_1 = PayableAccount { @@ -1437,7 +1438,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::PriorityTransactionFee { + adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, response_skeleton_opt, @@ -1445,7 +1446,7 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // account_1, the least important one, was eliminated for missing enough + // Account_1, the least important one, was eliminated for not big enough // transaction fee balance let expected_accounts = { let account_2_adjusted = PayableAccount { @@ -1460,19 +1461,19 @@ mod tests { } #[test] - fn adjust_payments_when_only_masq_balance_limits_the_final_transaction_count() { + fn only_service_fee_balance_limits_the_payments_count() { init_test_logging(); - let test_name = "adjust_payments_when_only_masq_balance_limits_the_final_transaction_count"; + let test_name = "only_service_fee_balance_limits_the_payments_count"; let now = SystemTime::now(); let wallet_1 = make_wallet("def"); - // account to be adjusted up to maximum + // Account to be adjusted to keep as much as how much is left in the cw balance let account_1 = PayableAccount { wallet: wallet_1.clone(), balance_wei: 333_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }; - // account to be outweighed and fully taken + // Account to be outweighed and fully preserved let wallet_2 = make_wallet("abc"); let account_2 = PayableAccount { wallet: wallet_2.clone(), @@ -1480,7 +1481,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), pending_payable_opt: None, }; - // account to be disqualified + // Account to be disqualified let wallet_3 = make_wallet("ghk"); let balance_3 = 600_000_000_000; let account_3 = PayableAccount { @@ -1504,6 +1505,8 @@ mod tests { client_id: 111, context_id: 234, }); + // Another place where I pick a populated response + // skeleton for hardening let adjustment_setup = PreparedAdjustment { qualified_payables, agent, @@ -1530,18 +1533,20 @@ mod tests { }) ); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); - TestLogHandler::new().exists_log_containing(&format!("INFO: {test_name}: Dealing with the consuming wallet \ - being short of MASQ. Seems unavoidable to disregard payable 0x000000000000000000000000000000000067686b \ - at the moment. Reason is the computed possible payment of 69,153,257,937 wei \ - would not be at least half of the original debt 600,000,000,000")); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {test_name}: Shortage of MASQ \ + in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ + ruled out from this round of payments. The proposed adjustment 69,153,257,937 wei was less \ + than half of the recorded debt, 600,000,000,000 wei" + )); } struct CompetitiveAccountsTestInputs<'a> { common: WalletsSetup<'a>, - balance_correction_minor_account_1: u128, - balance_correction_minor_account_2: u128, - age_correction_secs_account_1: u64, - age_correction_secs_account_2: u64, + account_1_balance_positive_correction_minor: u128, + account_2_balance_positive_correction_minor: u128, + account_1_age_positive_correction_secs: u64, + account_2_age_positive_correction_secs: u64, } #[derive(Clone, Copy)] @@ -1553,39 +1558,39 @@ mod tests { fn test_two_competitive_accounts_with_one_disqualified<'a>( test_scenario_name: &str, inputs: CompetitiveAccountsTestInputs, - expected_winning_account_wallet: &'a Wallet, + expected_wallet_of_the_winning_account: &'a Wallet, ) { let now = SystemTime::now(); - let service_fee_balance_in_minor_units = 100_000_000_000_000_u128 - 1; + let cw_service_fee_balance_in_minor = 100_000_000_000_000 - 1; let standard_balance_per_account = 100_000_000_000_000; let standard_age_per_account = 12000; let account_1 = PayableAccount { wallet: inputs.common.wallet_1.clone(), - balance_wei: standard_balance_per_account + inputs.balance_correction_minor_account_1, + balance_wei: standard_balance_per_account + + inputs.account_1_balance_positive_correction_minor, last_paid_timestamp: now .checked_sub(Duration::from_secs( - standard_age_per_account + inputs.age_correction_secs_account_1, + standard_age_per_account + inputs.account_1_age_positive_correction_secs, )) .unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: inputs.common.wallet_2.clone(), - balance_wei: standard_balance_per_account + inputs.balance_correction_minor_account_2, + balance_wei: standard_balance_per_account + + inputs.account_2_balance_positive_correction_minor, last_paid_timestamp: now .checked_sub(Duration::from_secs( - standard_age_per_account + inputs.age_correction_secs_account_2, + standard_age_per_account + inputs.account_2_age_positive_correction_secs, )) .unwrap(), pending_payable_opt: None, }; let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); - let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_result(cw_service_fee_balance_in_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1602,14 +1607,14 @@ mod tests { let winning_account = result.remove(0); assert_eq!( - &winning_account.wallet, expected_winning_account_wallet, + &winning_account.wallet, expected_wallet_of_the_winning_account, "{}: expected wallet {} but got {}", - test_scenario_name, winning_account.wallet, expected_winning_account_wallet + test_scenario_name, winning_account.wallet, expected_wallet_of_the_winning_account ); assert_eq!( - winning_account.balance_wei, service_fee_balance_in_minor_units, + winning_account.balance_wei, cw_service_fee_balance_in_minor, "{}: expected full cw balance {}, but the account had {}", - test_scenario_name, winning_account.balance_wei, service_fee_balance_in_minor_units + test_scenario_name, winning_account.balance_wei, cw_service_fee_balance_in_minor ); assert!( result.is_empty(), @@ -1620,82 +1625,87 @@ mod tests { } #[test] - fn adjust_payments_when_not_enough_masq_to_pay_both_at_least_by_their_half() { - fn merge_test_name_with_test_case(description: &str) -> String { + fn not_enough_masq_to_pay_for_both_accounts_at_least_by_their_half_so_one_wins() { + fn merge_test_name_with_test_scenario(description: &str) -> String { format!( - "adjust_payments_when_not_enough_masq_to_pay_both_at_least_by_their_half{}", + "not_enough_masq_to_pay_for_both_accounts_at_least_by_their_half_so_one_wins{}", description ) } - let wallet_1 = make_wallet("abcd"); - let wallet_2 = make_wallet("cdef"); + let w1 = make_wallet("abcd"); + let w2 = make_wallet("cdef"); let common_input = WalletsSetup { - wallet_1: &wallet_1, - wallet_2: &wallet_2, + wallet_1: &w1, + wallet_2: &w2, }; // scenario A - let first_scenario_name = merge_test_name_with_test_case("when equal"); - let expected_winning_account = &wallet_2; + let first_scenario_name = merge_test_name_with_test_scenario("when equally significant"); + let expected_wallet_of_the_winning_account = &w2; test_two_competitive_accounts_with_one_disqualified( &first_scenario_name, CompetitiveAccountsTestInputs { common: common_input, - balance_correction_minor_account_1: 0, - balance_correction_minor_account_2: 0, - age_correction_secs_account_1: 0, - age_correction_secs_account_2: 0, + account_1_balance_positive_correction_minor: 0, + account_2_balance_positive_correction_minor: 0, + account_1_age_positive_correction_secs: 0, + account_2_age_positive_correction_secs: 0, }, - expected_winning_account, + expected_wallet_of_the_winning_account, ); - + //-------------------------------------------------------------------- // scenario B - let second_scenario_name = merge_test_name_with_test_case("first heavier by balance"); - let expected_winning_account = &wallet_2; + let second_scenario_name = + merge_test_name_with_test_scenario("first more significant by balance"); + let expected_wallet_of_the_winning_account = &w2; test_two_competitive_accounts_with_one_disqualified( &second_scenario_name, CompetitiveAccountsTestInputs { common: common_input, - balance_correction_minor_account_1: 1, - balance_correction_minor_account_2: 0, - age_correction_secs_account_1: 0, - age_correction_secs_account_2: 0, + account_1_balance_positive_correction_minor: 1, + account_2_balance_positive_correction_minor: 0, + account_1_age_positive_correction_secs: 0, + account_2_age_positive_correction_secs: 0, }, - expected_winning_account, + expected_wallet_of_the_winning_account, ); - + //-------------------------------------------------------------------- // scenario C - let third_scenario_name = merge_test_name_with_test_case("second heavier by age"); - let expected_winning_account = &wallet_1; + let third_scenario_name = + merge_test_name_with_test_scenario("second more significant by age"); + let expected_wallet_of_the_winning_account = &w1; test_two_competitive_accounts_with_one_disqualified( &third_scenario_name, CompetitiveAccountsTestInputs { common: common_input, - balance_correction_minor_account_1: 0, - balance_correction_minor_account_2: 0, - age_correction_secs_account_1: 1, - age_correction_secs_account_2: 0, + account_1_balance_positive_correction_minor: 0, + account_2_balance_positive_correction_minor: 0, + account_1_age_positive_correction_secs: 1, + account_2_age_positive_correction_secs: 0, }, - expected_winning_account, + expected_wallet_of_the_winning_account, ) } #[test] - fn adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count() { + fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { init_test_logging(); - let test_name = "adjust_payments_when_masq_as_well_as_transaction_fee_limits_the_count"; + let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; let now = SystemTime::now(); - // Thrown away as the second for the proposed balance insignificance + // Thrown away as the second one due to shortage of service fee, + // for the proposed adjusted balance insignificance (the third account withdraws + // most of the available balance from the consuming wallet for itself) let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 10_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - // Thrown away as the first one for the proposed balance insignificance + // Thrown away as the first one due to shortage of transaction fee, + // as it is the least significant by criteria at the moment let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: 55_000_000_000, @@ -1713,18 +1723,18 @@ mod tests { let qualified_payables = vec![account_1, account_2, account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let service_fee_balance_in_minor_units = 300_000_000_000_000_u128; + let service_fee_balance_in_minor = 300_000_000_000_000_u128; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_result(service_fee_balance_in_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::PriorityTransactionFee { + adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, response_skeleton_opt: None, @@ -1736,7 +1746,7 @@ mod tests { result.affordable_accounts, vec![PayableAccount { wallet: wallet_3, - balance_wei: service_fee_balance_in_minor_units, + balance_wei: service_fee_balance_in_minor, last_paid_timestamp: last_paid_timestamp_3, pending_payable_opt: None, }] @@ -1762,12 +1772,12 @@ mod tests { } #[test] - fn error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient( + fn late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient( ) { init_test_logging(); - let test_name = "error_from_the_depths_after_transaction_fee_adjusted_but_masq_balance_is_rechecked_and_found_fully_insufficient"; + let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); - //this account gets eliminated in the transaction-fee cut + // This account is eliminated in the transaction fee cut let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 111_000_000_000_000, @@ -1788,8 +1798,8 @@ mod tests { }; let qualified_payables = vec![account_1, account_2, account_3]; let mut subject = PaymentAdjusterReal::new(); - // This is exactly the amount which will provoke an error subject.logger = Logger::new(test_name); + // This is exactly the amount which will provoke an error let service_fee_balance_in_minor_units = (111_000_000_000_000 / 2) - 1; let agent = { let mock = BlockchainAgentMock::default() @@ -1799,7 +1809,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::PriorityTransactionFee { + adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, response_skeleton_opt: None, @@ -1815,19 +1825,19 @@ mod tests { err, PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 2, - cw_masq_balance_minor: service_fee_balance_in_minor_units + cw_service_fee_balance_minor: service_fee_balance_in_minor_units } ); TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Passing successful payment adjustment by the transaction fee, but \ - facing critical scarcity of MASQ balance. Operation will abort." + "ERROR: {test_name}: Passed successfully adjustment by transaction fee but noticing \ + critical scarcity of MASQ balance. Operation will abort." )); } #[test] - fn entry_check_predicts_worth_of_trying_for_potential_adjustment_aptly_despite_many_eliminated_accounts( - ) { - let test_name = "entry_check_predicts_worth_of_trying_for_potential_adjustment_aptly_despite_many_eliminated_accounts"; + fn entry_check_advises_trying_adjustment_despite_lots_of_potentially_disqualified_accounts() { + // This test tries to prove that the entry check can reliably predict whether it makes any + // sense to set about the adjustment let now = SystemTime::now(); // Disqualified in the first iteration let account_1 = PayableAccount { @@ -1853,17 +1863,25 @@ mod tests { last_paid_timestamp: last_paid_timestamp_3, pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2, account_3.clone()]; + // Disqualified in the third iteration (because it is younger debt than the one at wallet 3) + let account_4 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 100_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(20000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3.clone(), account_4]; let mut subject = PaymentAdjusterReal::new(); - let logger = Logger::new(test_name); - subject.logger = logger.clone(); - // This cw balance should be enough to fulfill the entry check. After eliminating two accounts, - // the final winning resolution of the third account will work out because of the only - // additional one. - // The strategies advance reversed. The initial check seeks the smallest account, - // the disqualification strategy always takes from the largest accounts first. - // As a result, we can forecast the chances if the adjustment would succeed, not having to - // move forward beyond the entry check. + // This cw balance should make the entry check pass. + // In the adjustment procedure, after three accounts are gradually eliminated, + // resolution of the third account will work out well because of the additional unit. + // + // Both the entry check and the adjustment algorithm strategies advance reversely. + // The initial check inspects the smallest account. + // The strategy of account disqualifications always cuts from the largest accounts piece + // by piece, one in every iteration. + // Consequently, we can evaluate early if the adjustment has chances to succeed and stop + // just at the entry check if it doesn't. let service_fee_balance_in_minor_units = (balance_3 / 2) + 1; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = { @@ -1879,8 +1897,8 @@ mod tests { response_skeleton_opt: None, }; - let check_result = PaymentAdjusterReal::check_need_of_masq_adjustment( - &logger, + let check_result = PaymentAdjusterReal::check_need_of_adjustment_by_service_fee( + &Logger::new("test"), Either::Left(&qualified_payables), service_fee_balance_in_minor_units, ); @@ -1900,28 +1918,32 @@ mod tests { assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp) } - struct TransactionFeeTestConfig { + struct TestConfigForServiceFeeBalances { + // Either gwei or wei + balances_of_accounts: Either, Vec>, + cw_balance_minor: u128, + } + + struct TestConfigForTransactionFee { agreed_transaction_fee_per_computed_unit_major: u64, number_of_accounts: usize, estimated_transaction_fee_units_limit_per_transaction: u64, cw_transaction_fee_balance_major: u64, } - fn make_qualified_payables_and_blockchain_agent_for_initial_check( - masq_balances_config_opt: Option, - transaction_fee_config_opt: Option, + fn make_test_input_for_initial_check( + service_fee_balances_config_opt: Option, + transaction_fee_config_opt: Option, ) -> (Vec, Box) { - let masq_balances_setup = match masq_balances_config_opt { + let service_fee_balances_setup = match service_fee_balances_config_opt { Some(config) => config, - None => PayableBalancesAndCWBalanceTestConfig { + None => TestConfigForServiceFeeBalances { balances_of_accounts: Either::Left(vec![1, 1]), - cw_balance_major: u64::MAX, + cw_balance_minor: u64::MAX as u128, }, }; - let cw_service_fee_minor: u128 = gwei_to_wei(masq_balances_setup.cw_balance_major); - - let balances_of_accounts_minor = match masq_balances_setup.balances_of_accounts { + let balances_of_accounts_minor = match service_fee_balances_setup.balances_of_accounts { Either::Left(in_major) => in_major .into_iter() .map(|major| gwei_to_wei(major)) @@ -1929,12 +1951,12 @@ mod tests { Either::Right(in_minor) => in_minor, }; - let accounts_count = balances_of_accounts_minor.len(); + let accounts_count_from_sf_config = balances_of_accounts_minor.len(); let ( - desired_transaction_fee_price, - number_of_payments, - estimated_transaction_fee_unit_limit_per_transaction, + agreed_transaction_fee_price, + accounts_count_from_tf_config, + estimated_limit_for_transaction_fee_units_per_transaction, cw_balance_transaction_fee_major, ) = match transaction_fee_config_opt { Some(conditions) => ( @@ -1943,48 +1965,37 @@ mod tests { conditions.estimated_transaction_fee_units_limit_per_transaction, conditions.cw_transaction_fee_balance_major, ), - None => (120, accounts_count, 55_000, u64::MAX), - }; - - let qualified_payables: Vec<_> = if number_of_payments != accounts_count { - (0..number_of_payments) - .map(|idx| make_payable_account(idx as u64)) - .collect() - } else { - balances_of_accounts_minor - .into_iter() - .enumerate() - .map(|(idx, balance)| { - let mut account = make_payable_account(idx as u64); - account.balance_wei = balance; - account - }) - .collect() - }; + None => (120, accounts_count_from_sf_config, 55_000, u64::MAX), + }; + + let qualified_payables: Vec<_> = + if accounts_count_from_tf_config != accounts_count_from_sf_config { + (0..accounts_count_from_tf_config) + .map(|idx| make_payable_account(idx as u64)) + .collect() + } else { + balances_of_accounts_minor + .into_iter() + .enumerate() + .map(|(idx, balance)| { + let mut account = make_payable_account(idx as u64); + account.balance_wei = balance; + account + }) + .collect() + }; let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( - estimated_transaction_fee_unit_limit_per_transaction * desired_transaction_fee_price, + estimated_limit_for_transaction_fee_units_per_transaction + * agreed_transaction_fee_price, ); let blockchain_agent = BlockchainAgentMock::default() .transaction_fee_balance_result(cw_transaction_fee_minor) - .service_fee_balance_result(cw_service_fee_minor) + .service_fee_balance_result(service_fee_balances_setup.cw_balance_minor) .estimated_transaction_fee_per_transaction_result( estimated_transaction_fee_per_transaction_minor, ); - // PayablePaymentSetup { - // qualified_payables, - // this_stage_data_opt: Some(StageData::FinancialAndTechDetails( - // FinancialAndTechDetails { - // consuming_wallet_balances: ConsumingWalletBalances { - // transaction_fee_minor: cw_transaction_fee_minor, - // masq_tokens_minor: cw_masq_balance_minor, - // }, - // estimated_gas_limit_per_transaction: estimated_transaction_fee_limit_per_tx, - // agreed_transaction_fee_per_computed_unit_major: desired_transaction_fee_price, - // }, - // )), - // response_skeleton_opt: None, - // } + (qualified_payables, Box::new(blockchain_agent)) } } diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/verifier.rs index bc3192601..629a6a4af 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/verifier.rs @@ -11,12 +11,13 @@ impl MasqAdjustmentPossibilityVerifier { pub fn verify_adjustment_possibility( &self, accounts: &[&PayableAccount], - cw_masq_balance_minor: u128, + cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - // The reasoning is that the real adjustment algorithm will proceed by eliminating the biggest - // account in each iteration, reaching out the smallest one eventually; if the smallest one - // reduced by the disqualification margin turned out possible to pay with the currently available - // balance, we can tell that this Node is going to initiate at least one blockchain transaction + // The idea stands as the adjustment algorithm will have to proceed in each iteration by + // eliminating the biggest account there as it always finds an account to disqualify, + // reaching out the smallest one eventually; if the smallest one reduced by + // the disqualification margin turns out to be still possibly paid out we can be sure + // that this Node is going to realize at least one blockchain transaction let sorted = accounts .iter() .sorted_by(|account_a, account_b| { @@ -25,14 +26,15 @@ impl MasqAdjustmentPossibilityVerifier { .collect::>(); let smallest_account = sorted.first().expect("empty Vec of qualified payables "); - if calculate_disqualification_edge(smallest_account.balance_wei) <= cw_masq_balance_minor { + if calculate_disqualification_edge(smallest_account.balance_wei) + <= cw_service_fee_balance_minor + { Ok(()) } else { - let number_of_accounts = accounts.len(); Err( PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts, - cw_masq_balance_minor, + number_of_accounts: accounts.len(), + cw_service_fee_balance_minor, }, ) } @@ -108,7 +110,7 @@ mod tests { Err( PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, - cw_masq_balance_minor: cw_masq_balance + cw_service_fee_balance_minor: cw_masq_balance } ) ) From d2a03c0ed71ef537a9c3b1c7ce33d95e89c56f93 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 7 Nov 2023 20:15:19 +0800 Subject: [PATCH 089/250] GH-711: test server utility method --- node/src/blockchain/blockchain_bridge.rs | 2 +- .../lower_level_interface_web3.rs | 23 +++----- .../blockchain_interface_web3/mod.rs | 59 ++++++------------- node/src/test_utils/http_test_server.rs | 4 ++ 4 files changed, 31 insertions(+), 57 deletions(-) diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index c78f797e1..57f02e5be 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -2029,7 +2029,7 @@ pub mod exportable_test_parts { } fn launch_prepared_test_server() -> (TestServer, String) { let port = find_free_port(); - let server_url = format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port); + let server_url = format!("http://{}:{}", Ipv4Addr::LOCALHOST, port); ( TestServer::start( port, 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 345853476..f21990b19 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 @@ -110,11 +110,8 @@ mod tests { port, vec![br#"{"jsonrpc":"2.0","id":0,"result":"0xDEADBEEF"}"#.to_vec()], ); - let (_event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (_event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = make_subject(transport, chain); @@ -170,11 +167,8 @@ mod tests { let test_server = TestServer::start (port, vec![ br#"{"jsonrpc":"2.0","id":0,"result":"0x00000000000000000000000000000000000000000000000000000000DEADBEEF"}"#.to_vec() ]); - let (_event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (_event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = make_subject(transport, chain); @@ -350,14 +344,11 @@ mod tests { F: FnOnce(&LowBlockchainIntWeb3, &Wallet) -> ResultForBalance, { let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ + let test_server = TestServer::start (port, vec![ br#"{"jsonrpc":"2.0","id":0,"result":"0x000000000000000000000000000000000000000000000000000000000000FFFQ"}"#.to_vec() ]); - let (_event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (_event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let wallet = Wallet::from_str("0x3f69f9efd4f2592fd70be8c32ecd9dce71c472fc").unwrap(); let subject = make_subject(transport, chain); 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 dd77bb873..ab362e39c 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -795,11 +795,8 @@ mod tests { ] }]"#.to_vec(), ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let end_block_nbr = 1024u64; @@ -857,11 +854,8 @@ mod tests { port, vec![br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[]}]"#.to_vec()], ); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, TEST_DEFAULT_CHAIN); let end_block_nbr = 1024u64; @@ -927,14 +921,11 @@ mod tests { fn blockchain_interface_web3_retrieve_transactions_returns_an_error_if_a_response_with_too_few_topics_is_returned( ) { let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ + let test_server = TestServer::start (port, vec![ br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d63100000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); @@ -954,14 +945,11 @@ mod tests { fn blockchain_interface_web3_retrieve_transactions_returns_an_error_if_a_response_with_data_that_is_too_long_is_returned( ) { let port = find_free_port(); - let _test_server = TestServer::start(port, vec![ + let test_server = TestServer::start(port, vec![ br#"[{"jsonrpc":"2.0","id":2,"result":"0x400"},{"jsonrpc":"2.0","id":3,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","blockNumber":"0x4be663","data":"0x0000000000000000000000000000000000000000000000056bc75e2d6310000001","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); @@ -978,15 +966,12 @@ mod tests { fn blockchain_interface_web3_retrieve_transactions_ignores_transaction_logs_that_have_no_block_number( ) { let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ + let test_server = TestServer::start (port, vec![ br#"[{"jsonrpc":"2.0","id":1,"result":"0x400"},{"jsonrpc":"2.0","id":2,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","data":"0x0000000000000000000000000000000000000000000000000010000000000000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); init_test_logging(); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let end_block_nbr = 1024u64; let subject = @@ -1015,14 +1000,11 @@ mod tests { fn blockchain_interface_non_clandestine_retrieve_transactions_uses_block_number_latest_as_fallback_start_block_plus_one( ) { let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ + let test_server = TestServer::start (port, vec![ br#"[{"jsonrpc":"2.0","id":1,"result":"error"},{"jsonrpc":"2.0","id":2,"result":[{"address":"0xcd6c588e005032dd882cd43bf53a32129be81302","blockHash":"0x1a24b9169cbaec3f6effa1f600b70c7ab9e8e86db44062b49132a4415d26732a","data":"0x0000000000000000000000000000000000000000000000000010000000000000","logIndex":"0x0","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000003f69f9efd4f2592fd70be8c32ecd9dce71c472fc","0x000000000000000000000000adc1853c7859369639eb414b6342b36288fe6092"],"transactionHash":"0x955cec6ac4f832911ab894ce16aa22c3003f46deff3f7165b32700d2f5ff0681","transactionIndex":"0x0"}]}]"#.to_vec() ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); @@ -2076,15 +2058,12 @@ mod tests { #[test] fn blockchain_interface_web3_can_fetch_transaction_receipt() { let port = find_free_port(); - let _test_server = TestServer::start (port, vec![ + let test_server = TestServer::start (port, vec![ br#"{"jsonrpc":"2.0","id":2,"result":{"transactionHash":"0xa128f9ca1e705cc20a936a24a7fa1df73bad6e0aaf58e8e6ffcc154a7cff6e0e","blockHash":"0x6d0abccae617442c26104c2bc63d1bc05e1e002e555aec4ab62a46e826b18f18","blockNumber":"0xb0328d","contractAddress":null,"cumulativeGasUsed":"0x60ef","effectiveGasPrice":"0x22ecb25c00","from":"0x7424d05b59647119b01ff81e2d3987b6c358bf9c","gasUsed":"0x60ef","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000","status":"0x0","to":"0x384dec25e03f94931767ce4c3556168468ba24c3","transactionIndex":"0x0","type":"0x0"}}"# .to_vec() ]); - let (event_loop_handle, transport) = Http::with_max_parallel( - &format!("http://{}:{}", &Ipv4Addr::LOCALHOST, port), - REQUESTS_IN_PARALLEL, - ) - .unwrap(); + let (event_loop_handle, transport) = + Http::with_max_parallel(&test_server.local_url(), REQUESTS_IN_PARALLEL).unwrap(); let chain = TEST_DEFAULT_CHAIN; let subject = BlockchainInterfaceWeb3::new(transport, event_loop_handle, chain); let tx_hash = diff --git a/node/src/test_utils/http_test_server.rs b/node/src/test_utils/http_test_server.rs index 56e0daddf..80214b22a 100644 --- a/node/src/test_utils/http_test_server.rs +++ b/node/src/test_utils/http_test_server.rs @@ -52,6 +52,10 @@ impl TestServer { TestServer { port, rx } } + pub fn server_url(&self) -> String { + format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), self.port) + } + pub fn requests_so_far(&self) -> Vec>> { let mut requests = vec![]; while let Ok(request) = self.rx.try_recv() { From 55fa057120dfad4239606e1e5a05ed94f9729b5e Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:32:12 +0800 Subject: [PATCH 090/250] GH-711: small modificatios from linux machine; unrelated to the architecture though --- masq_lib/src/utils.rs | 2 +- node/src/accountant/mod.rs | 7 +--- .../age_criterion_calculator.rs | 16 ++++---- .../balance_criterion_calculator.rs | 14 +++---- .../criteria_calculators/mod.rs | 12 +++--- .../payment_adjuster/diagnostics.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 4 +- .../miscellaneous/helper_functions.rs | 40 +++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 6 +-- node/src/test_utils/http_test_server.rs | 2 +- 10 files changed, 49 insertions(+), 56 deletions(-) diff --git a/masq_lib/src/utils.rs b/masq_lib/src/utils.rs index 24dc6d320..0f05e9e4f 100644 --- a/masq_lib/src/utils.rs +++ b/masq_lib/src/utils.rs @@ -434,7 +434,7 @@ macro_rules! as_any_in_trait_impl { #[macro_export] macro_rules! test_only_use { - ($($use_clause: item),+) => { + ($($use_clause: item)+) => { $( #[cfg(test)] $use_clause diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 6ff941e71..f712a4e1e 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -661,14 +661,9 @@ impl Accountant { Some(Either::Left(complete_msg)) => Some(complete_msg), Some(Either::Right(unaccepted_msg)) => { //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 - match self - .scanners + self.scanners .payable .perform_payment_adjustment(unaccepted_msg, &self.logger) - { - Some(instructions) => Some(instructions), - None => None, - } } None => None, }; diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index ca49aebe2..980f06567 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -4,18 +4,17 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, ParameterCriterionCalculator, }; -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - DiagnosticsConfig, -}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use std::sync::Mutex; -use std::time::SystemTime; -use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; use crate::standard_impls_for_calculator; +use std::time::SystemTime; +test_only_use!( + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; + use std::sync::Mutex; +); const AGE_MAIN_EXPONENT: u32 = 3; -const AGE_MULTIPLIER: u128 = 150; const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; @@ -185,7 +184,7 @@ mod tests { AgeCriterionCalculator, AgeInput, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, - AGE_MAIN_EXPONENT, AGE_MULTIPLIER, + AGE_MAIN_EXPONENT, }; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, ParameterCriterionCalculator, @@ -197,7 +196,6 @@ mod tests { #[test] fn constants_are_correct() { assert_eq!(AGE_MAIN_EXPONENT, 3); - assert_eq!(AGE_MULTIPLIER, 150); assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index e4a187032..eba157af3 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -4,13 +4,13 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, ParameterCriterionCalculator, }; -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - DiagnosticsConfig, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{log_2}; -use std::sync::Mutex; -use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; use crate::standard_impls_for_calculator; +test_only_use!( + use std::sync::Mutex; + use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; +); // This parameter affects the steepness (sensitivity to balance increase) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; @@ -37,7 +37,7 @@ where pub fn new(iter: I) -> Self { let formula = Box::new(|wrapped_balance_minor: BalanceInput| { let balance_minor = wrapped_balance_minor.0; - let binary_weight = log_2(Self::calculate_binary_argument(balance_minor)); + let binary_weight = Self::nonzero_log2(Self::calculate_binary_argument(balance_minor)); balance_minor .checked_mul(binary_weight as u128) .expect("mul overflow") diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index a7972ebe2..fd8e26a82 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -6,14 +6,16 @@ pub mod balance_criterion_calculator; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - compute_progressive_characteristics, DiagnosticsConfig, - COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, -}; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::calculator_local_diagnostics; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; -use std::sync::Mutex; +test_only_use!( + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + compute_progressive_characteristics, DiagnosticsConfig, + COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, + }; + use std::sync::Mutex; +); // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator: diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 5c7b104c8..b125f4329 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -166,7 +166,7 @@ pub mod formulas_progressive_characteristics { use std::sync::{Mutex, Once}; use thousands::Separable; - pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = false; + pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; //mutex should be fine for debugging, no need for mut static static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 08415d470..f1ed0cb57 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -31,7 +31,7 @@ pub fn format_brief_adjustment_summary( adjusted_accounts: &[PayableAccount], ) -> String { adjusted_accounts - .into_iter() + .iter() .sorted_by(|account_a, account_b| { Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) }) @@ -56,7 +56,7 @@ pub fn format_brief_adjustment_summary( length = WALLET_ADDRESS_LENGTH )); let list = excluded - .into_iter() + .iter() .sorted_by(|(_, balance_account_a), (_, balance_account_b)| { Ord::cmp(&balance_account_b, &balance_account_a) }) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 52e2af09a..c076d872d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -15,6 +15,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ use crate::sub_lib::wallet::Wallet; use itertools::Itertools; use masq_lib::logger::Logger; +use std::cmp::Ordering; use std::iter::successors; use std::ops::Not; @@ -36,9 +37,7 @@ where } pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { - sum_as(&accounts_with_individual_criteria, |(criteria, _)| { - *criteria - }) + sum_as(accounts_with_individual_criteria, |(criteria, _)| *criteria) } pub fn keep_only_transaction_fee_affordable_accounts_and_drop_the_rest( @@ -63,9 +62,7 @@ pub fn compute_fractional_numbers_preventing_mul_coefficient( ) -> u128 { let criteria_sum_digits_count = log_10(account_criteria_sum); let cw_balance_digits_count = log_10(cw_masq_balance_minor); - let positive_difference = criteria_sum_digits_count - .checked_sub(cw_balance_digits_count) - .unwrap_or(0); + let positive_difference = criteria_sum_digits_count.saturating_sub(cw_balance_digits_count); let safe_mul_coefficient = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; 10_u128 .checked_pow(safe_mul_coefficient as u32) @@ -107,21 +104,23 @@ pub fn find_largest_nominated_account<'a>( accounts .iter() .fold(**first_account, |largest_so_far, current| { - if current.original_account.balance_wei < largest_so_far.original_account.balance_wei { - largest_so_far - } else if current.original_account.balance_wei - == largest_so_far.original_account.balance_wei - { + match Ord::cmp( + ¤t.original_account.balance_wei, + &largest_so_far.original_account.balance_wei, + ) { + Ordering::Less => largest_so_far, + Ordering::Greater => current, + Ordering::Equal => // Greater value means younger - if current.original_account.last_paid_timestamp - > largest_so_far.original_account.last_paid_timestamp { - current - } else { - largest_so_far + if current.original_account.last_paid_timestamp + > largest_so_far.original_account.last_paid_timestamp + { + current + } else { + largest_so_far + } } - } else { - current } }) } @@ -260,8 +259,7 @@ impl CwExhaustingStatus { possible_extra_addition: u128, ) -> Self { let corrected_adjusted_account_before_finalization = { - non_finalized_account_info.proposed_adjusted_balance = - non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition; + non_finalized_account_info.proposed_adjusted_balance += possible_extra_addition; non_finalized_account_info }; self.remainder = self @@ -317,7 +315,7 @@ pub fn list_accounts_nominated_for_disqualification( disqualification_edge, ); - Some(&*account_info) + Some(account_info) } else { None } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 07ccee22c..7e13dfb40 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -15,6 +15,7 @@ use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeRunner, ServiceFeeOnlyRunner, }; use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculators}; +#[cfg(test)] use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; @@ -441,6 +442,7 @@ impl PaymentAdjusterReal { sort_in_descendant_order_by_criteria_sums(criteria_and_accounts); // effective only if the iterator is collected + #[cfg(test)] print_formulas_characteristics_for_diagnostics(); collected_accounts_with_criteria @@ -541,11 +543,9 @@ impl PaymentAdjusterReal { .into_iter() .filter(|account_info| { account_info.original_account.wallet != disqualified_account_wallet - }) - .collect::>(); + }); let remaining_reverted = remaining - .into_iter() .map(|account_info| { PayableAccount::from((account_info, ProposedAdjustmentResolution::Revert)) }) diff --git a/node/src/test_utils/http_test_server.rs b/node/src/test_utils/http_test_server.rs index 80214b22a..54648582c 100644 --- a/node/src/test_utils/http_test_server.rs +++ b/node/src/test_utils/http_test_server.rs @@ -52,7 +52,7 @@ impl TestServer { TestServer { port, rx } } - pub fn server_url(&self) -> String { + pub fn local_url(&self) -> String { format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), self.port) } From baa270aa7b9442798512f4fd615567f84717c015 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:42:35 +0800 Subject: [PATCH 091/250] GH-711: took diagnostical line out from the place applying criteria; now rendered by a call in the test tree --- .../payment_adjuster/diagnostics.rs | 15 +++++--- node/src/accountant/payment_adjuster/mod.rs | 37 ++++++++++--------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index b125f4329..1ee00b658 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -166,9 +166,13 @@ pub mod formulas_progressive_characteristics { use std::sync::{Mutex, Once}; use thousands::Separable; + // Only for debugging; in order to see the characteristic values of distinct parameter + // you only have to run one (no matter if more) test which executes including the core part + // where the criteria are applied, in other words computed. You cannot grab a wrong one if + // you are picking from high level tests of the PaymentAdjuster class pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; //mutex should be fine for debugging, no need for mut static - static STRINGS_WITH_FORMULAS_CHARACTERISTICS: Mutex> = Mutex::new(vec![]); + static SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS: Mutex> = Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); pub struct DiagnosticsConfig { @@ -176,14 +180,15 @@ pub mod formulas_progressive_characteristics { pub horizontal_axis_native_type_formatter: Box A + Send>, } - pub fn print_formulas_characteristics_for_diagnostics() { + pub fn render_formulas_characteristics_for_diagnostics_if_enabled() { if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { - let report = STRINGS_WITH_FORMULAS_CHARACTERISTICS + let comprehend_debug_summary = SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS .lock() .expect("diagnostics poisoned") .join("\n\n"); - eprintln!("{}", report) + + eprintln!("{}", comprehend_debug_summary) }) } } @@ -215,7 +220,7 @@ pub mod formulas_progressive_characteristics { main_param_name )); let full_text = head.into_iter().chain(characteristics).join("\n"); - STRINGS_WITH_FORMULAS_CHARACTERISTICS + SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS .lock() .expect("diagnostics poisoned") .push(full_text); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 7e13dfb40..8b9623d0f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -15,8 +15,6 @@ use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeRunner, ServiceFeeOnlyRunner, }; use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculators}; -#[cfg(test)] -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::print_formulas_characteristics_for_diagnostics; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; use crate::accountant::payment_adjuster::inner::{ @@ -438,14 +436,7 @@ impl PaymentAdjusterReal { .calculate_age_criteria(self) .calculate_balance_criteria(); - let collected_accounts_with_criteria = - sort_in_descendant_order_by_criteria_sums(criteria_and_accounts); - - // effective only if the iterator is collected - #[cfg(test)] - print_formulas_characteristics_for_diagnostics(); - - collected_accounts_with_criteria + sort_in_descendant_order_by_criteria_sums(criteria_and_accounts) } fn perform_masq_adjustment( @@ -697,6 +688,7 @@ mod tests { use std::{usize, vec}; use thousands::Separable; use web3::types::U256; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{render_formulas_characteristics_for_diagnostics_if_enabled}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -988,6 +980,7 @@ mod tests { #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { + render_formulas_characteristics_for_diagnostics_if_enabled(); let now = SystemTime::now(); let subject = make_initialized_subject(now, None, None); let account_1 = PayableAccount { @@ -1032,6 +1025,7 @@ mod tests { #[test] fn minor_but_a_lot_aged_debt_is_prioritized_outweighed_and_stays_as_the_full_original_balance() { + render_formulas_characteristics_for_diagnostics_if_enabled(); let now = SystemTime::now(); let cw_service_fee_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance), None); @@ -1155,7 +1149,8 @@ mod tests { }; // We eliminated (disqualified) the other account than which was going to qualify as // outweighed - assert_eq!(remaining, vec![account_1]) + assert_eq!(remaining, vec![account_1]); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1240,6 +1235,7 @@ mod tests { "0x000000000000000000000000000000626c616832", 1000, )); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1324,6 +1320,7 @@ mod tests { | 3929064899188044996" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1393,6 +1390,7 @@ mod tests { |0x0000000000000000000000000000000000616263 111000000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1458,6 +1456,7 @@ mod tests { assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1537,8 +1536,8 @@ mod tests { "INFO: {test_name}: Shortage of MASQ \ in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ ruled out from this round of payments. The proposed adjustment 69,153,257,937 wei was less \ - than half of the recorded debt, 600,000,000,000 wei" - )); + than half of the recorded debt, 600,000,000,000 wei")); + render_formulas_characteristics_for_diagnostics_if_enabled(); } struct CompetitiveAccountsTestInputs<'a> { @@ -1687,7 +1686,9 @@ mod tests { account_2_age_positive_correction_secs: 0, }, expected_wallet_of_the_winning_account, - ) + ); + + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1769,6 +1770,7 @@ mod tests { |0x0000000000000000000000000000000000646566 55000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1830,8 +1832,8 @@ mod tests { ); TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Passed successfully adjustment by transaction fee but noticing \ - critical scarcity of MASQ balance. Operation will abort." - )); + critical scarcity of MASQ balance. Operation will abort.")); + render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1915,7 +1917,8 @@ mod tests { }] ); assert_eq!(adjustment_result.response_skeleton_opt, None); - assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp) + assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp); + render_formulas_characteristics_for_diagnostics_if_enabled(); } struct TestConfigForServiceFeeBalances { From 267e1681e3c2ba1e00a44cb81848e1b0f835d855 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:36:50 +0800 Subject: [PATCH 092/250] GH-711: rearranged the way to feed the configs for rengering the characteristics --- .../age_criterion_calculator.rs | 34 +++---------------- .../balance_criterion_calculator.rs | 8 ++--- .../payment_adjuster/diagnostics.rs | 12 ++++--- node/src/accountant/payment_adjuster/mod.rs | 6 ++-- .../accountant/payment_adjuster/test_utils.rs | 8 ++++- 5 files changed, 26 insertions(+), 42 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 980f06567..7a0bc51c2 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -125,7 +125,7 @@ impl From<&PayableAccount> for AgeInput { pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; - use itertools::Either::{Left, Right}; + use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; use std::time::Duration; @@ -134,35 +134,9 @@ pub mod characteristics_config { lazy_static! { pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let now = SystemTime::now(); - let horisontal_axis_data_suply = { - [ - Left(1), - Left(5), - Left(9), - Left(25), - Left(44), - Left(50), - Left(75), - Right(2), - Right(3), - Right(4), - Left(33_333), - Right(5), - Right(6), - Right(7), - Right(8), - Left(200_300_400), - Right(9), - Right(10), - Right(12), - ] - .into_iter() - .map(|exp| match exp { - Left(precise_secs) => precise_secs, - Right(decimal_exp) => 10_u128.pow(decimal_exp), - }) - .collect() - }; + let literal_nums = [1,5,9,25,44,50,75, 33_333, 200_300_400, 78_000_000_000, 444_333_444_444]; + let decadic_exponents = [2,3,4,5,6,7,8,9,10, 12]; + let horisontal_axis_data_suply = reinterpret_vec_of_values_on_x_axis(literal_nums, decadic_exponents); Mutex::new(Some(DiagnosticsConfig { horizontal_axis_progressive_supply: horisontal_axis_data_suply, horizontal_axis_native_type_formatter: Box::new( diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index eba157af3..ccbb68b8d 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -72,15 +72,15 @@ impl From<&PayableAccount> for BalanceInput { pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; lazy_static! { pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let horisontal_axis_decimal_exponents = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18, 21, 25] - .into_iter() - .map(|exp| 10_u128.pow(exp)) - .collect(); + let literal_nums = [123_456, 7_777_777, 1_888_999_999_888, 543_210_000_000_000_000_000]; + let decadic_exponents = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; + let horisontal_axis_decimal_exponents = reinterpret_vec_of_values_on_x_axis(literal_nums, decadic_exponents); Mutex::new(Some(DiagnosticsConfig { horizontal_axis_progressive_supply: horisontal_axis_decimal_exponents, horizontal_axis_native_type_formatter: Box::new(|balance_wei| { diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 1ee00b658..449e2c477 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -172,7 +172,8 @@ pub mod formulas_progressive_characteristics { // you are picking from high level tests of the PaymentAdjuster class pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; //mutex should be fine for debugging, no need for mut static - static SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS: Mutex> = Mutex::new(vec![]); + static SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS: Mutex> = + Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); pub struct DiagnosticsConfig { @@ -183,10 +184,11 @@ pub mod formulas_progressive_characteristics { pub fn render_formulas_characteristics_for_diagnostics_if_enabled() { if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { - let comprehend_debug_summary = SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS - .lock() - .expect("diagnostics poisoned") - .join("\n\n"); + let comprehend_debug_summary = + SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS + .lock() + .expect("diagnostics poisoned") + .join("\n\n"); eprintln!("{}", comprehend_debug_summary) }) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8b9623d0f..c61c9cde6 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1536,7 +1536,8 @@ mod tests { "INFO: {test_name}: Shortage of MASQ \ in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ ruled out from this round of payments. The proposed adjustment 69,153,257,937 wei was less \ - than half of the recorded debt, 600,000,000,000 wei")); + than half of the recorded debt, 600,000,000,000 wei" + )); render_formulas_characteristics_for_diagnostics_if_enabled(); } @@ -1830,7 +1831,8 @@ mod tests { cw_service_fee_balance_minor: service_fee_balance_in_minor_units } ); - TestLogHandler::new().exists_log_containing(&format!( + TestLogHandler::new() + .exists_log_containing(&format!( "ERROR: {test_name}: Passed successfully adjustment by transaction fee but noticing \ critical scarcity of MASQ balance. Operation will abort.")); render_formulas_characteristics_for_diagnostics_if_enabled(); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 98fa6a138..f1824dcbd 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -6,7 +6,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::test_utils::make_wallet; -use itertools::Either; +use itertools::{Either, Itertools}; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; @@ -63,4 +63,10 @@ pub fn make_extreme_accounts( .collect() } +pub fn reinterpret_vec_of_values_on_x_axis( + literal_feed: [u128; L1], exponent_determined_feed: [u32;L2] +) -> Vec { + let exponent_based_numbers = exponent_determined_feed.into_iter().map(|exponent|10_u128.pow(exponent)); + literal_feed.into_iter().chain(exponent_based_numbers).sorted().collect() +} pub type Sentinel = Empty<(u128, PayableAccount)>; From cf28dec3d1be9112ac2c45cf21c5244c15707aca Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 13 Nov 2023 23:06:50 +0800 Subject: [PATCH 093/250] GH-711: better diagnostics and tuned out the relation between age and balance by parameters in the formulas --- .../age_criterion_calculator.rs | 46 ++++++++--- .../balance_criterion_calculator.rs | 38 ++++++--- .../criteria_calculators/mod.rs | 10 +-- .../payment_adjuster/diagnostics.rs | 80 ++++++++++++++----- node/src/accountant/payment_adjuster/mod.rs | 15 ++-- .../accountant/payment_adjuster/test_utils.rs | 13 ++- 6 files changed, 148 insertions(+), 54 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 7a0bc51c2..ff55ca236 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::standard_impls_for_calculator; use std::time::SystemTime; test_only_use!( - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; use std::sync::Mutex; ); @@ -124,7 +124,7 @@ impl From<&PayableAccount> for AgeInput { #[cfg(test)] pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; @@ -132,14 +132,42 @@ pub mod characteristics_config { use std::time::SystemTime; lazy_static! { - pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let now = SystemTime::now(); - let literal_nums = [1,5,9,25,44,50,75, 33_333, 200_300_400, 78_000_000_000, 444_333_444_444]; - let decadic_exponents = [2,3,4,5,6,7,8,9,10, 12]; - let horisontal_axis_data_suply = reinterpret_vec_of_values_on_x_axis(literal_nums, decadic_exponents); - Mutex::new(Some(DiagnosticsConfig { - horizontal_axis_progressive_supply: horisontal_axis_data_suply, - horizontal_axis_native_type_formatter: Box::new( + let literal_values = [ + 1, + 5, + 9, + 25, + 44, + 50, + 75, + 180, + 600, + 900, + 33_333, + 86_400, + 255_000, + 6_700_000, + 55_333_000, + 200_300_400, + 500_000_000, + 7_000_000_000, + 78_000_000_000, + 444_333_444_444, + ]; + let decadic_exponents = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let horisontal_axis_data_suply = + reinterpret_vec_of_values_on_x_axis(literal_values, decadic_exponents); + Mutex::new(Some(DiagnosticsAxisX { + non_remarkable_values_supply: horisontal_axis_data_suply, + remarkable_values_opt: Some(vec![ + (60, "MINUTE"), + (3_600, "HOUR"), + (86_400, "DAY"), + (604_800, "WEEK"), + ]), + convertor_to_expected_formula_input_type: Box::new( move |secs_since_last_paid_payable| { let native_time = now .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index ccbb68b8d..03eddd81a 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -9,11 +9,13 @@ use crate::standard_impls_for_calculator; test_only_use!( use std::sync::Mutex; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; ); -// This parameter affects the steepness (sensitivity to balance increase) -const BALANCE_LOG_2_ARG_DIVISOR: u128 = 33; +// This parameter affects the steepness inversely, but just slowly +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 4300; +// This parameter affects the steepness analogously, but energetically +const BALANCE_FINAL_MULTIPLIER: u128 = 2; pub struct BalanceCriterionCalculator where @@ -41,6 +43,7 @@ where balance_minor .checked_mul(binary_weight as u128) .expect("mul overflow") + * BALANCE_FINAL_MULTIPLIER }); Self { iter, formula } } @@ -71,19 +74,32 @@ impl From<&PayableAccount> for BalanceInput { #[cfg(test)] pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsConfig; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; lazy_static! { - pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let literal_nums = [123_456, 7_777_777, 1_888_999_999_888, 543_210_000_000_000_000_000]; - let decadic_exponents = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; - let horisontal_axis_decimal_exponents = reinterpret_vec_of_values_on_x_axis(literal_nums, decadic_exponents); - Mutex::new(Some(DiagnosticsConfig { - horizontal_axis_progressive_supply: horisontal_axis_decimal_exponents, - horizontal_axis_native_type_formatter: Box::new(|balance_wei| { + pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { + let literal_values = [ + 123_456, + 7_777_777, + 1_888_999_999_888, + 543_210_000_000_000_000_000, + ]; + let decadic_exponents = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, + ]; + let horisontal_axis_decimal_exponents = + reinterpret_vec_of_values_on_x_axis(literal_values, decadic_exponents); + Mutex::new(Some(DiagnosticsAxisX { + non_remarkable_values_supply: horisontal_axis_decimal_exponents, + remarkable_values_opt: Some(vec![ + (10_u128.pow(9), "GWEI"), + (10_u128.pow(18), "MASQ"), + ]), + convertor_to_expected_formula_input_type: Box::new(|balance_wei| { BalanceInput(balance_wei) }), })) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index fd8e26a82..c112755cb 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -11,7 +11,7 @@ use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - compute_progressive_characteristics, DiagnosticsConfig, + compute_progressive_characteristics, DiagnosticsAxisX, COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, }; use std::sync::Mutex; @@ -52,9 +52,9 @@ pub trait CriterionCalculator: } #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>>; + fn diagnostics_config_location(&self) -> &Mutex>>; #[cfg(test)] - fn diagnostics_config_opt(&self) -> Option> { + fn diagnostics_config_opt(&self) -> Option> { self.diagnostics_config_location() .lock() .expect("diagnostics poisoned") @@ -135,9 +135,7 @@ macro_rules! standard_impls_for_calculator { } #[cfg(test)] - fn diagnostics_config_location( - &self, - ) -> &Mutex>> { + fn diagnostics_config_location(&self) -> &Mutex>> { &$diagnostics_config_opt } } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 449e2c477..31a6684bc 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -176,9 +176,28 @@ pub mod formulas_progressive_characteristics { Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); - pub struct DiagnosticsConfig { - pub horizontal_axis_progressive_supply: Vec, - pub horizontal_axis_native_type_formatter: Box A + Send>, + pub struct DiagnosticsAxisX { + pub non_remarkable_values_supply: Vec, + pub remarkable_values_opt: Option>, + pub convertor_to_expected_formula_input_type: Box A + Send>, + } + + impl DiagnosticsAxisX { + fn finalize_input_with_remarkable_values(&self) -> Vec { + match self.remarkable_values_opt.as_ref() { + Some(vals) => { + let filtered_remarkable_values = vals.iter().map(|(num, _)| num); + let standard_input = self.non_remarkable_values_supply.iter(); + filtered_remarkable_values + .chain(standard_input) + .sorted() + .dedup() + .map(|num| *num) + .collect() + } + None => self.non_remarkable_values_supply.clone(), + } + } } pub fn render_formulas_characteristics_for_diagnostics_if_enabled() { @@ -195,28 +214,51 @@ pub mod formulas_progressive_characteristics { } } + fn render_notation( + coordinate_value: u128, + remarkable_vals: Option<&Vec<(u128, &'static str)>>, + ) -> String { + match should_mark_be_used(coordinate_value, remarkable_vals) { + Some(mark) => format!("{} {}", coordinate_value.separate_with_commas(), mark), + None => coordinate_value.separate_with_commas(), + } + } + fn should_mark_be_used( + coordinate_value: u128, + remarkable_vals: Option<&Vec<(u128, &'static str)>>, + ) -> Option<&'static str> { + match remarkable_vals { + Some(vals) => vals + .iter() + .find(|(val, _)| coordinate_value == *val) + .map(|(_, mark)| *mark), + None => None, + } + } + pub fn compute_progressive_characteristics( main_param_name: &'static str, - config_opt: Option>, + config_opt: Option>, formula: &dyn Fn(A) -> u128, ) where A: Debug, { - config_opt.map(|config| { - let config_x_axis_type_formatter = config.horizontal_axis_native_type_formatter; - let characteristics = - config - .horizontal_axis_progressive_supply - .into_iter() - .map(|input| { - let correctly_formatted_input = config_x_axis_type_formatter(input); - format!( - "x: {:( - literal_feed: [u128; L1], exponent_determined_feed: [u32;L2] + literal_feed: [u128; L1], + exponent_determined_feed: [u32; L2], ) -> Vec { - let exponent_based_numbers = exponent_determined_feed.into_iter().map(|exponent|10_u128.pow(exponent)); - literal_feed.into_iter().chain(exponent_based_numbers).sorted().collect() + let exponent_based_numbers = exponent_determined_feed + .into_iter() + .map(|exponent| 10_u128.pow(exponent)); + literal_feed + .into_iter() + .chain(exponent_based_numbers) + .sorted() + .collect() } pub type Sentinel = Empty<(u128, PayableAccount)>; From c685f352884d8317c3368595868486dba5dad14c Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 14 Nov 2023 22:43:09 +0800 Subject: [PATCH 094/250] GH-711: fixed and improved a test --- .../payment_adjuster/diagnostics.rs | 6 +- .../miscellaneous/helper_functions.rs | 110 +++++++++++------- node/src/accountant/payment_adjuster/mod.rs | 21 +--- 3 files changed, 75 insertions(+), 62 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 31a6684bc..da24cbfc9 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -167,11 +167,9 @@ pub mod formulas_progressive_characteristics { use thousands::Separable; // Only for debugging; in order to see the characteristic values of distinct parameter - // you only have to run one (no matter if more) test which executes including the core part - // where the criteria are applied, in other words computed. You cannot grab a wrong one if - // you are picking from high level tests of the PaymentAdjuster class pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; - //mutex should be fine for debugging, no need for mut static + // TODO + // Mutex should be fine for debugging, no need for mut static static SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS: Mutex> = Mutex::new(vec![]); static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index c076d872d..67f08f576 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -484,11 +484,9 @@ mod tests { #[test] fn multiplication_coefficient_extreme_feeding_and_safety_ceiling() { - // We also add some extra multiples of 10, the more of them the more accurate - // numbers we can expect from the ultimate results of the payment adjuster machinery - // - // We cannot say by heart which of the criteria sums of these parameters evaluates to is for sure bigger than another, - // therefore we could hardly put them into an order + // We cannot say by heart which of the evaluated criteria sums from + // these parameters below will be bigger than another and therefore + // we cannot line them up in an order let accounts_as_months_and_balances = vec![ (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), (5, 10_u128.pow(18)), @@ -520,7 +518,7 @@ mod tests { reserved_initial_accounts_order_according_to_wallets .iter() .enumerate(); - let mul_coefficients_and_criteria_sums_matching_the_order_of_the_original_inputs = results + let mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs = results .into_iter() .map( |(computed_coefficient, account_wallet, account_criteria_sum)| { @@ -534,44 +532,70 @@ mod tests { .sorted_by(|(idx_a, _, _), (idx_b, _, _)| Ord::cmp(&idx_b, &idx_a)) .map(|(_, coefficient, criteria_sum)| (coefficient, criteria_sum)) .collect::>(); - // For ease with visual check - #[rustfmt::skip] - fn expected_result() -> Vec<(u128, u128)> { - vec![ - ( - 100000000000000000000000000000000000000, - 3337685069315260840795395456195 - ), - ( - 100000000000000000000000000000000000, - 3147569315260840795395456195 - ), - ( - 100000000000000000000000000000000000, - 3032031628421209321195830846 - ), - ( - 100000000000000000000000000000000, - 8388546281691944888584197 - ), - ( - 10000000000000000000000000000000, - 428973298027210119732866 - ), - ( - 10000000000000000000000000000000, - 137300730946560000000000 - ), - ( - 100000000000000000000000000000000000, - 2962514007434791591273391962 - ) - ] - } + let templates_for_coefficients = vec![ + 100000000000000000000000000000000000000, + 100000000000000000000000000000000000, + 100000000000000000000000000000000000, + 100000000000000000000000000000000, + 10000000000000000000000000000000, + 10000000000000000000000000000000, + 100000000000000000000000000000000000, + ]; + // I was trying to write these assertions so that it wouldn't require us to rewrite + // the expected values everytime someone pokes into the formulas. + check_relation_to_computed_criteria_sum_fairly_but_with_enough_benevolence( + &mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs, + ); + compare_coefficients_to_templates( + &mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs, + &templates_for_coefficients, + ); + } + + fn check_relation_to_computed_criteria_sum_fairly_but_with_enough_benevolence( + output: &[(u128, u128)], + ) { + output.iter().for_each(|(coefficient, corresponding_criteria_sum)| { + let coefficient_num_decimal_length = log_10(*coefficient); + let criteria_sum_decimal_length = log_10(*corresponding_criteria_sum); + assert_eq!(coefficient_num_decimal_length, criteria_sum_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, + "coefficient with bad safety margin; should be {} but was {}, as one of this set {:?}", + coefficient_num_decimal_length, + criteria_sum_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, + output + ); + + let expected_division_by_10_if_wrong = 10_u128.pow(coefficient_num_decimal_length as u32 - 1); + let experiment_result = corresponding_criteria_sum / 10; + match experiment_result == expected_division_by_10_if_wrong { + false => (), + true => match corresponding_criteria_sum % 10 { + 0 => panic!("the criteria sum is a pure power of ten, too a suspicious result, \ + check it in {:?}", output), + _ => () + } + } + }) + } + + fn compare_coefficients_to_templates(outputs: &[(u128, u128)], templates: &[u128]) { assert_eq!( - mul_coefficients_and_criteria_sums_matching_the_order_of_the_original_inputs, - expected_result() - ) + outputs.len(), + templates.len(), + "count of actual values {:?} and templates don't match {:?}", + outputs, + templates + ); + outputs + .iter() + .zip(templates.iter()) + .for_each(|((actual_coeff, _), expected_coeff)| { + assert_eq!( + actual_coeff, expected_coeff, + "actual coefficient {} does not match the expected one {} in the full set {:?}", + actual_coeff, expected_coeff, outputs + ) + }) } fn make_non_finalized_adjusted_accounts( diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index ef78191ea..ace32650c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -45,6 +45,8 @@ use std::iter::once; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +#[cfg(test)] +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::render_formulas_characteristics_for_diagnostics_if_enabled; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -297,6 +299,9 @@ impl PaymentAdjusterReal { let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); + #[cfg(test)] + render_formulas_characteristics_for_diagnostics_if_enabled(); + adjustment_runner.adjust_multiple(self, accounts_with_individual_criteria_sorted) } @@ -688,7 +693,6 @@ mod tests { use std::{usize, vec}; use thousands::Separable; use web3::types::U256; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{render_formulas_characteristics_for_diagnostics_if_enabled}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -980,7 +984,6 @@ mod tests { #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { - render_formulas_characteristics_for_diagnostics_if_enabled(); let now = SystemTime::now(); let subject = make_initialized_subject(now, None, None); let account_1 = PayableAccount { @@ -1025,7 +1028,6 @@ mod tests { #[test] fn minor_but_a_lot_aged_debt_is_prioritized_outweighed_and_stays_as_the_full_original_balance() { - render_formulas_characteristics_for_diagnostics_if_enabled(); let now = SystemTime::now(); let cw_service_fee_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance), None); @@ -1150,7 +1152,6 @@ mod tests { // We eliminated (disqualified) the other account than which was going to qualify as // outweighed assert_eq!(remaining, vec![account_1]); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1235,7 +1236,6 @@ mod tests { "0x000000000000000000000000000000626c616832", 1000, )); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1323,7 +1323,6 @@ mod tests { | {expected_balance_1}" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1393,7 +1392,6 @@ mod tests { |0x0000000000000000000000000000000000616263 111000000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1459,7 +1457,6 @@ mod tests { assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1538,10 +1535,9 @@ mod tests { TestLogHandler::new().exists_log_containing(&format!( "INFO: {test_name}: Shortage of MASQ \ in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ - ruled out from this round of payments. The proposed adjustment 69,153,257,937 wei was less \ + ruled out from this round of payments. The proposed adjustment 69,153,137,093 wei was less \ than half of the recorded debt, 600,000,000,000 wei" )); - render_formulas_characteristics_for_diagnostics_if_enabled(); } struct CompetitiveAccountsTestInputs<'a> { @@ -1691,8 +1687,6 @@ mod tests { }, expected_wallet_of_the_winning_account, ); - - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1774,7 +1768,6 @@ mod tests { |0x0000000000000000000000000000000000646566 55000000000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1838,7 +1831,6 @@ mod tests { .exists_log_containing(&format!( "ERROR: {test_name}: Passed successfully adjustment by transaction fee but noticing \ critical scarcity of MASQ balance. Operation will abort.")); - render_formulas_characteristics_for_diagnostics_if_enabled(); } #[test] @@ -1923,7 +1915,6 @@ mod tests { ); assert_eq!(adjustment_result.response_skeleton_opt, None); assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp); - render_formulas_characteristics_for_diagnostics_if_enabled(); } struct TestConfigForServiceFeeBalances { From c4c2102aa0e847dbc799a200624cfd3386050f1f Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 14 Nov 2023 23:41:38 +0800 Subject: [PATCH 095/250] GH-711: fixed another test --- .../balance_criterion_calculator.rs | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 03eddd81a..5d5e36230 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -12,8 +12,14 @@ test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; ); -// This parameter affects the steepness inversely, but just slowly -const BALANCE_LOG_2_ARG_DIVISOR: u128 = 4300; +// This parameter affects the steepness inversely, but just slowly. +// +// Don't worry to change the number; it's not as scientific as it looks, +// I arrived at it after many attempts, true, but only until I became +// aligned with the tuning compared to the values gotten from the Age +// parameter (to reproduce the process you probably need to use +// the rendering tools from the diagnostics module) +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; // This parameter affects the steepness analogously, but energetically const BALANCE_FINAL_MULTIPLIER: u128 = 2; @@ -39,7 +45,8 @@ where pub fn new(iter: I) -> Self { let formula = Box::new(|wrapped_balance_minor: BalanceInput| { let balance_minor = wrapped_balance_minor.0; - let binary_weight = Self::nonzero_log2(Self::calculate_binary_argument(balance_minor)); + let argument_for_log = Self::calculate_binary_argument(balance_minor); + let binary_weight = Self::nonzero_log2(argument_for_log); balance_minor .checked_mul(binary_weight as u128) .expect("mul overflow") @@ -48,8 +55,8 @@ where Self { iter, formula } } - fn nonzero_log2(balance_minor: u128) -> u32 { - let log = log_2(Self::calculate_binary_argument(balance_minor)); + fn nonzero_log2(input: u128) -> u32 { + let log = log_2(input); if log > 0 { log } else { @@ -110,7 +117,8 @@ pub mod characteristics_config { #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ - BalanceCriterionCalculator, BalanceInput, BALANCE_LOG_2_ARG_DIVISOR, + BalanceCriterionCalculator, BalanceInput, BALANCE_FINAL_MULTIPLIER, + BALANCE_LOG_2_ARG_DIVISOR, }; use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, ParameterCriterionCalculator, @@ -153,12 +161,12 @@ mod tests { #[test] fn nonzero_log2_works() { - let result: Vec<_> = [0, 5, 66, 100, 131, 132] + let result: Vec<_> = [0, 1, 2, 5, 66, 100, 131, 132, u64::MAX as u128 + 1] .into_iter() .map(|balance| BalanceCriterionCalculator::::nonzero_log2(balance)) .collect(); - assert_eq!(result, vec![1, 1, 1, 1, 1, 2]) + assert_eq!(result, vec![1, 1, 1, 2, 6, 6, 7, 7, 64]) } #[test] @@ -178,15 +186,17 @@ mod tests { let result = subject.formula()(balance_wei_wrapped); let expected_result = { - let binary_weight = log_2( - BalanceCriterionCalculator::::calculate_binary_argument( - balance_wei_wrapped.0, - ), - ); + let binary_weight = + BalanceCriterionCalculator::::nonzero_log2(BalanceCriterionCalculator::< + Sentinel, + >::calculate_binary_argument( + balance_wei_wrapped.0 + )); balance_wei_wrapped .0 .checked_mul(binary_weight as u128) .expect("mul overflow") + * BALANCE_FINAL_MULTIPLIER }; assert_eq!(result, expected_result) } From 30d89bbbc06a9df498c1d076ac81fb33ccf2cab6 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 15 Nov 2023 21:04:01 +0800 Subject: [PATCH 096/250] GH-711: cleared out; awaiting the multinode test first run --- .../balance_criterion_calculator.rs | 4 +-- .../criteria_calculators/mod.rs | 5 ++- .../payment_adjuster/diagnostics.rs | 31 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 5d5e36230..628b26134 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -123,13 +123,13 @@ mod tests { use crate::accountant::payment_adjuster::criteria_calculators::{ CriterionCalculator, ParameterCriterionCalculator, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; use crate::accountant::payment_adjuster::test_utils::Sentinel; use std::iter; #[test] fn constants_are_correct() { - assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 33) + assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 18_490_000); + assert_eq!(BALANCE_FINAL_MULTIPLIER, 2) } #[test] diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index c112755cb..cdd218984 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -11,8 +11,7 @@ use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - compute_progressive_characteristics, DiagnosticsAxisX, - COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, + compute_progressive_characteristics, DiagnosticsAxisX, COMPUTE_FORMULAS_CHARACTERISTICS, }; use std::sync::Mutex; ); @@ -65,7 +64,7 @@ pub trait CriterionCalculator: where ::Input: Debug, { - if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { + if COMPUTE_FORMULAS_CHARACTERISTICS { compute_progressive_characteristics( self.parameter_name(), self.diagnostics_config_opt(), diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index da24cbfc9..d6ff35e9b 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -const PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS: bool = false; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -25,7 +25,7 @@ where F1: Fn() -> String, F2: Fn() -> String, { - if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { + if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { let subject = if let Some(subject_renderer) = subject_renderer_opt { subject_renderer() } else { @@ -43,7 +43,7 @@ where } pub fn collection_diagnostics(label: &str, accounts: &[D]) { - if PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS { + if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { eprintln!("{}", label); accounts .iter() @@ -167,11 +167,14 @@ pub mod formulas_progressive_characteristics { use thousands::Separable; // Only for debugging; in order to see the characteristic values of distinct parameter - pub const COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS: bool = true; - // TODO - // Mutex should be fine for debugging, no need for mut static - static SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS: Mutex> = + pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; + // Preserve the 'static' keyword + static SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER: Mutex> = Mutex::new(vec![]); + // Preserve the 'static' keyword + // + // The singleton ensures that we print the characteristics always only once, also if multiple + // tests are running static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); pub struct DiagnosticsAxisX { @@ -199,10 +202,10 @@ pub mod formulas_progressive_characteristics { } pub fn render_formulas_characteristics_for_diagnostics_if_enabled() { - if COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS { + if COMPUTE_FORMULAS_CHARACTERISTICS { FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { let comprehend_debug_summary = - SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS + SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER .lock() .expect("diagnostics poisoned") .join("\n\n"); @@ -262,7 +265,7 @@ pub mod formulas_progressive_characteristics { main_param_name )); let full_text = head.into_iter().chain(characteristics).join("\n"); - SUMMARIES_OF_FORMULA_CHARACTERISTICS_SEPARATE_BY_PARAMETERS + SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER .lock() .expect("diagnostics poisoned") .push(full_text); @@ -272,12 +275,12 @@ pub mod formulas_progressive_characteristics { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS; - use crate::accountant::payment_adjuster::diagnostics::PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::COMPUTE_FORMULAS_CHARACTERISTICS; + use crate::accountant::payment_adjuster::diagnostics::PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS; #[test] fn constants_are_correct() { - assert_eq!(PRINT_PARTIAL_COMPUTATIONS_FOR_DIAGNOSTICS, false); - assert_eq!(COMPUTE_FORMULAS_PROGRESSIVE_CHARACTERISTICS, false) + assert_eq!(PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS, false); + assert_eq!(COMPUTE_FORMULAS_CHARACTERISTICS, false) } } From 2b33d2bd52e7aa607af48677249c18420e527c9b Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:17:30 +0800 Subject: [PATCH 097/250] GH-711: verify_bill_payment is sanitized --- multinode_integration_tests/ci/all.sh | 2 +- .../docker/blockchain/Dockerfile | 3 +- .../docker/blockchain/entrypoint.sh | 4 +- .../tests/verify_bill_payment.rs | 379 +++++++++++++++++- node/src/test_utils/mod.rs | 5 - 5 files changed, 365 insertions(+), 28 deletions(-) diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index 6ce38689c..70d5272d2 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -46,5 +46,5 @@ popd pushd "$CI_DIR/.." export RUSTFLAGS="-D warnings -Anon-snake-case" ci/lint.sh -cargo test --release -- --nocapture --test-threads=1 +cargo test verify_bill_payment --release -- --nocapture --test-threads=1 popd diff --git a/multinode_integration_tests/docker/blockchain/Dockerfile b/multinode_integration_tests/docker/blockchain/Dockerfile index 027eb7a27..54a4dfcc9 100644 --- a/multinode_integration_tests/docker/blockchain/Dockerfile +++ b/multinode_integration_tests/docker/blockchain/Dockerfile @@ -1,5 +1,6 @@ # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -FROM trufflesuite/ganache-cli:v6.7.0 +#FROM trufflesuite/ganache-cli:v6.7.0 +FROM --platform=linux/arm64 nutrina/ganache-cli:0.3 ADD ./entrypoint.sh /app/ diff --git a/multinode_integration_tests/docker/blockchain/entrypoint.sh b/multinode_integration_tests/docker/blockchain/entrypoint.sh index f9d6cc220..f80123f9d 100755 --- a/multinode_integration_tests/docker/blockchain/entrypoint.sh +++ b/multinode_integration_tests/docker/blockchain/entrypoint.sh @@ -1,3 +1,5 @@ #!/bin/sh -node /app/ganache-core.docker.cli.js -p 18545 --networkId 2 --verbose --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" +#node /app/ganache-core.docker.cli.js -p 18545 --networkId 2 --verbose --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" + +ganache-cli -h 0.0.0.0 -p 18545 --networkId 2 --verbose -m "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ No newline at end of file diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 5e9b50347..a03fd7776 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -3,7 +3,10 @@ use bip39::{Language, Mnemonic, Seed}; use futures::Future; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::WEIS_IN_GWEI; -use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; +use masq_lib::messages::FromMessageBody; +use masq_lib::messages::ToMessageBody; +use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; +use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; @@ -38,6 +41,15 @@ use web3::Web3; #[test] fn verify_bill_payment() { + // Note: besides the main objectives of this test, it relies on (and so + // it proves) the premise that each Node, when reaching its full connectivity + // (able to make a route), activates its accountancy module whereas it also + // begins the first cycle of the scanners immediately. That's why it might + // not be wise to take out the passages with starting a bunch of Nodes just + // in order to meet the condition even though the test could be rewritten + // into a simpler one by using directly the `scans` command from a UI through + // which we would achieve the same with less PCU work an in a shorter time. + // See the second test in this file. let mut cluster = match MASQNodeCluster::start() { Ok(cluster) => cluster, Err(e) => panic!("{}", e), @@ -64,7 +76,7 @@ fn verify_bill_payment() { assert_balances( &contract_owner_wallet, &blockchain_interface, - "99998043204000000000", + "99998381140000000000", "472000000000000000000000000", ); let payment_thresholds = PaymentThresholds { @@ -75,29 +87,47 @@ fn verify_bill_payment() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; - let (consuming_config, _) = - build_config(&blockchain_server, &seed, payment_thresholds, deriv_path); + // You might've asked yourself why we assume the consuming Node is configured with the contract + // owner wallet. That does not seem right but it has likely been chosen because at the beginning, + // when the contract is deployd, only the contract owner possesses the tokens. So, avoiding extra + // complexity from "airdropping" the tokens, we pretend this is okay. Above all, it completely + // is for this kind of tests. + let (consuming_config, _) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + deriv_path, + None, + ); let (serving_node_1_config, serving_node_1_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 1), + None, ); let (serving_node_2_config, serving_node_2_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 2), + None, ); let (serving_node_3_config, serving_node_3_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 3), + None, ); - let amount = 10 * payment_thresholds.permanent_debt_allowed_gwei as u128 * WEIS_IN_GWEI as u128; + let amount = |addition: u128| { + payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128 + addition + }; + let amount_1 = amount(123); + let amount_2 = amount(456); + let amount_3 = amount(789); let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); @@ -127,13 +157,13 @@ fn verify_bill_payment() { ); let now = SystemTime::now(); consuming_payable_dao - .more_money_payable(now, &serving_node_1_wallet, amount) + .more_money_payable(now, &serving_node_1_wallet, amount_1) .unwrap(); consuming_payable_dao - .more_money_payable(now, &serving_node_2_wallet, amount) + .more_money_payable(now, &serving_node_2_wallet, amount_2) .unwrap(); consuming_payable_dao - .more_money_payable(now, &serving_node_3_wallet, amount) + .more_money_payable(now, &serving_node_3_wallet, amount_3) .unwrap(); let (serving_node_1_name, serving_node_1_index) = @@ -147,7 +177,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); serving_node_1_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_1) .unwrap(); open_all_file_permissions(serving_node_1_path.clone().into()); @@ -162,7 +192,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); serving_node_2_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_2) .unwrap(); open_all_file_permissions(serving_node_2_path.clone().into()); @@ -177,7 +207,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); serving_node_3_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_3) .unwrap(); open_all_file_permissions(serving_node_3_path.clone().into()); @@ -186,11 +216,15 @@ fn verify_bill_payment() { expire_receivables(serving_node_2_path.into()); expire_receivables(serving_node_3_path.into()); + let expected_deployed_token_initial_supply = 472_000_000_000_000_000_000_000_000_u128; + + // Note: ganache defaults the balance of each created account to 100 ETH + assert_balances( &contract_owner_wallet, &blockchain_interface, - "99998043204000000000", - "472000000000000000000000000", + "99998381140000000000", + &expected_deployed_token_initial_supply.to_string(), ); assert_balances( @@ -232,25 +266,323 @@ fn verify_bill_payment() { thread::sleep(Duration::from_millis(400)); } + let expected_final_token_balance_consuming_node = + expected_deployed_token_initial_supply - (amount_1 + amount_2 + amount_3); + assert_balances( &contract_owner_wallet, &blockchain_interface, - "99997886466000000000", - "471999999700000000000000000", + "99998223622000000000", + &expected_final_token_balance_consuming_node.to_string(), ); assert_balances( &serving_node_1_wallet, &blockchain_interface, "100000000000000000000", - amount.to_string().as_str(), + &amount_1.to_string(), ); assert_balances( &serving_node_2_wallet, &blockchain_interface, "100000000000000000000", - amount.to_string().as_str(), + &amount_2.to_string(), + ); + + assert_balances( + &serving_node_3_wallet, + &blockchain_interface, + "100000000000000000000", + amount_3.to_string().as_str(), + ); + + let serving_node_1 = cluster.start_named_real_node( + &serving_node_1_name, + serving_node_1_index, + serving_node_1_config, + ); + let serving_node_2 = cluster.start_named_real_node( + &serving_node_2_name, + serving_node_2_index, + serving_node_2_config, + ); + let serving_node_3 = cluster.start_named_real_node( + &serving_node_3_name, + serving_node_3_index, + serving_node_3_config, + ); + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(serving_node_1.node_reference()) + .neighbor(serving_node_2.node_reference()) + .neighbor(serving_node_3.node_reference()) + .build(), + ); + } + + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_1_receivable_dao.account_status(&contract_owner_wallet) { + status.balance_wei == 0 + } else { + false + } + }); + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_2_receivable_dao.account_status(&contract_owner_wallet) { + status.balance_wei == 0 + } else { + false + } + }); + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_3_receivable_dao.account_status(&contract_owner_wallet) { + status.balance_wei == 0 + } else { + false + } + }); +} + +#[test] +fn payments_were_adjusted_due_to_insufficient_balance() { + let mut cluster = match MASQNodeCluster::start() { + Ok(cluster) => cluster, + Err(e) => panic!("{}", e), + }; + let blockchain_server = BlockchainServer { + name: "ganache-cli", + }; + blockchain_server.start(); + blockchain_server.wait_until_ready(); + let url = blockchain_server.url().to_string(); + let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); + let web3 = Web3::new(http.clone()); + let deriv_path = derivation_path(0, 0); + let seed = make_seed(); + let (contract_owner_wallet, _) = make_node_wallet(&seed, &deriv_path); + let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); + assert_eq!( + contract_addr, + cluster.chain.rec().contract, + "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", + contract_addr + ); + let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); + assert_balances( + &contract_owner_wallet, + &blockchain_interface, + "99998381140000000000", + "472000000000000000000000000", + ); + let payment_thresholds = PaymentThresholds { + threshold_interval_sec: 2_592_000, + debt_threshold_gwei: 1_000_000_000, + payment_grace_period_sec: 86_400, + maturity_threshold_sec: 86_400, + permanent_debt_allowed_gwei: 10_000_000, + unban_below_gwei: 10_000_000, + }; + let consuming_node_ui_port = find_free_port(); + // You might've noticed that we assume the consuming Node is configured with the contract owner + // wallet. That does not seem right but it has likely been chosen because at the beginning, when + // the contract is deployd, only the contract owner possesses the tokens. So, avoiding extra + // complexity from "airdropping" the tokens, we pretend this is okay. Above all, it completely + // is for this kind of tests. + let (consuming_config, _) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + deriv_path, + Some(consuming_node_ui_port), + ); + + let (serving_node_1_config, serving_node_1_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 1), + None, + ); + let (serving_node_2_config, serving_node_2_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 2), + None, + ); + let (serving_node_3_config, serving_node_3_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 3), + None, + ); + + let amount = payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128; + + let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); + let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); + let consuming_node_connection = DbInitializerReal::default() + .initialize( + Path::new(&consuming_node_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); + open_all_file_permissions(consuming_node_path.clone().into()); + assert_eq!( + format!("{}", &contract_owner_wallet), + "0x5a4d5df91d0124dec73dbd112f82d6077ccab47d" + ); + assert_eq!( + format!("{}", &serving_node_1_wallet), + "0x7a3cf474962646b18666b5a5be597bb0af013d81" + ); + assert_eq!( + format!("{}", &serving_node_2_wallet), + "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" + ); + assert_eq!( + format!("{}", &serving_node_3_wallet), + "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" + ); + let now = SystemTime::now(); + consuming_payable_dao + .more_money_payable(now, &serving_node_1_wallet, amount) + .unwrap(); + consuming_payable_dao + .more_money_payable(now, &serving_node_2_wallet, amount) + .unwrap(); + consuming_payable_dao + .more_money_payable(now, &serving_node_3_wallet, amount) + .unwrap(); + + let (serving_node_1_name, serving_node_1_index) = + cluster.prepare_real_node(&serving_node_1_config); + let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); + let serving_node_1_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_1_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); + serving_node_1_receivable_dao + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .unwrap(); + open_all_file_permissions(serving_node_1_path.clone().into()); + + let (serving_node_2_name, serving_node_2_index) = + cluster.prepare_real_node(&serving_node_2_config); + let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); + let serving_node_2_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_2_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); + serving_node_2_receivable_dao + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .unwrap(); + open_all_file_permissions(serving_node_2_path.clone().into()); + + let (serving_node_3_name, serving_node_3_index) = + cluster.prepare_real_node(&serving_node_3_config); + let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); + let serving_node_3_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_3_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); + serving_node_3_receivable_dao + .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .unwrap(); + open_all_file_permissions(serving_node_3_path.clone().into()); + + expire_payables(consuming_node_path.into()); + expire_receivables(serving_node_1_path.into()); + expire_receivables(serving_node_2_path.into()); + expire_receivables(serving_node_3_path.into()); + + let expected_deployed_token_initial_supply = 472_000_000_000_000_000_000_000_000_u128; + + assert_balances( + &contract_owner_wallet, + &blockchain_interface, + "99998381140000000000", + &expected_deployed_token_initial_supply.to_string(), + ); + + assert_balances( + &serving_node_1_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + assert_balances( + &serving_node_2_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + assert_balances( + &serving_node_3_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + let real_consuming_node = + cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); + + // Now let's command the consuming Node to check its payables + let ui_client = real_consuming_node.make_ui(consuming_node_ui_port); + ui_client.send_request( + UiScanRequest { + scan_type: ScanType::Payables, + } + .tmb(5555), + ); + let response = ui_client.wait_for_response(5555, Duration::from_secs(10)); + UiScanResponse::fmb(response).unwrap(); + + let now = Instant::now(); + while !consuming_payable_dao.non_pending_payables().is_empty() + && now.elapsed() < Duration::from_secs(10) + { + thread::sleep(Duration::from_millis(400)); + } + + let expected_final_token_balance_consuming_node = + expected_deployed_token_initial_supply - 3 * amount; + + assert_balances( + &contract_owner_wallet, + &blockchain_interface, + "99998223682000000000", + &expected_final_token_balance_consuming_node.to_string(), + ); + + assert_balances( + &serving_node_1_wallet, + &blockchain_interface, + "100000000000000000000", + &amount.to_string(), + ); + + assert_balances( + &serving_node_2_wallet, + &blockchain_interface, + "100000000000000000000", + &amount.to_string(), ); assert_balances( @@ -260,6 +592,7 @@ fn verify_bill_payment() { amount.to_string().as_str(), ); + //TODO we can do this by a client call --scans payables let serving_node_1 = cluster.start_named_real_node( &serving_node_1_name, serving_node_1_index, @@ -410,9 +743,10 @@ fn build_config( seed: &Seed, payment_thresholds: PaymentThresholds, wallet_derivation_path: String, + port_opt: Option, ) -> (NodeStartupConfig, Wallet) { let (node_wallet, node_secret) = make_node_wallet(seed, wallet_derivation_path.as_str()); - let config = NodeStartupConfigBuilder::standard() + let pre_config = NodeStartupConfigBuilder::standard() .blockchain_service_url(server_url_holder.url()) .chain(Chain::Dev) .payment_thresholds(payment_thresholds) @@ -420,8 +754,13 @@ fn build_config( .earning_wallet_info(EarningWalletInfo::Address(format!( "{}", node_wallet.clone() - ))) - .build(); + ))); + let pre_config = if let Some(port) = port_opt { + pre_config.ui_port(port) + } else { + pre_config + }; + let config = pre_config.build(); (config, node_wallet) } diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index f5cad813d..53cce106e 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -374,7 +374,6 @@ pub fn await_messages(expected_message_count: usize, messages_arc_mutex: &Arc } } -//must stay without cfg(test) -- used in another crate pub fn wait_for(interval_ms: Option, limit_ms: Option, mut f: F) where F: FnMut() -> bool, @@ -391,7 +390,6 @@ where .unwrap(); } -//must stay without cfg(test) -- used in another crate pub fn await_value( interval_and_limit_ms: Option<(u64, u64)>, mut f: F, @@ -460,7 +458,6 @@ where set } -//must stay without cfg(test) -- used in another crate pub fn read_until_timeout(stream: &mut dyn Read) -> Vec { let mut response: Vec = vec![]; let mut buf = [0u8; 16384]; @@ -519,7 +516,6 @@ pub fn make_paying_wallet(secret: &[u8]) -> Wallet { ) } -//must stay without cfg(test) -- used in another crate pub fn make_wallet(address: &str) -> Wallet { Wallet::from_str(&dummy_address_to_hex(address)).unwrap() } @@ -530,7 +526,6 @@ pub fn assert_eq_debug(a: T, b: T) { assert_eq!(a_str, b_str); } -//must stay without cfg(test) -- used in another crate #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct TestRawTransaction { pub nonce: U256, From 7fe21cd02a3875b3a98cd86486e730174c5028b2 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:34:39 +0800 Subject: [PATCH 098/250] 711: a save-point when debugging extensivelly --- .../tests/verify_bill_payment.rs | 113 +++++++++++------- .../blockchain_interface_web3/mod.rs | 33 +---- node/src/sub_lib/blockchain_interface_web3.rs | 35 ++++++ node/src/sub_lib/mod.rs | 1 + 4 files changed, 115 insertions(+), 67 deletions(-) create mode 100644 node/src/sub_lib/blockchain_interface_web3.rs diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index a03fd7776..0f633a519 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -36,8 +36,10 @@ use std::time::{Duration, Instant, SystemTime}; use std::{thread, u128}; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; -use web3::types::{Address, Bytes, TransactionParameters}; +use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters}; use web3::Web3; +use node_lib::accountant::gwei_to_wei; +use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; #[test] fn verify_bill_payment() { @@ -62,9 +64,8 @@ fn verify_bill_payment() { let url = blockchain_server.url().to_string(); let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); let web3 = Web3::new(http.clone()); - let deriv_path = derivation_path(0, 0); let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet(&seed, &deriv_path); + let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); assert_eq!( contract_addr, @@ -73,12 +74,6 @@ fn verify_bill_payment() { contract_addr ); let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); - assert_balances( - &contract_owner_wallet, - &blockchain_interface, - "99998381140000000000", - "472000000000000000000000000", - ); let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_592_000, debt_threshold_gwei: 1_000_000_000, @@ -87,38 +82,41 @@ fn verify_bill_payment() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; - // You might've asked yourself why we assume the consuming Node is configured with the contract - // owner wallet. That does not seem right but it has likely been chosen because at the beginning, - // when the contract is deployd, only the contract owner possesses the tokens. So, avoiding extra - // complexity from "airdropping" the tokens, we pretend this is okay. Above all, it completely - // is for this kind of tests. - let (consuming_config, _) = build_config( + let (consuming_config, consuming_node_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, - deriv_path, + derivation_path(0, 1), None, ); - + let consuming_node_balance = gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); + transfer_tokens_to_address(&contract_owner_wallet, &consuming_node_wallet, consuming_node_balance, 1, &web3, cluster.chain); + thread::sleep(Duration::from_secs(10)); + // assert_balances( + // &consuming_node_wallet, + // &blockchain_interface, + // "100000000000000000000", + // &consuming_node_balance.to_string(), + // ); let (serving_node_1_config, serving_node_1_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, - derivation_path(0, 1), + derivation_path(0, 2), None, ); let (serving_node_2_config, serving_node_2_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, - derivation_path(0, 2), + derivation_path(0, 3), None, ); let (serving_node_3_config, serving_node_3_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, - derivation_path(0, 3), + derivation_path(0, 4), None, ); @@ -140,20 +138,20 @@ fn verify_bill_payment() { let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); open_all_file_permissions(consuming_node_path.clone().into()); assert_eq!( - format!("{}", &contract_owner_wallet), - "0x5a4d5df91d0124dec73dbd112f82d6077ccab47d" + format!("{}", &consuming_node_wallet), + "0x7a3cf474962646b18666b5a5be597bb0af013d81" ); assert_eq!( format!("{}", &serving_node_1_wallet), - "0x7a3cf474962646b18666b5a5be597bb0af013d81" + "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" ); assert_eq!( format!("{}", &serving_node_2_wallet), - "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" + "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" ); assert_eq!( format!("{}", &serving_node_3_wallet), - "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" + "0xb45a33ef3e3097f34c826369b74141ed268cdb5a" ); let now = SystemTime::now(); consuming_payable_dao @@ -221,7 +219,7 @@ fn verify_bill_payment() { // Note: ganache defaults the balance of each created account to 100 ETH assert_balances( - &contract_owner_wallet, + &consuming_node_wallet, &blockchain_interface, "99998381140000000000", &expected_deployed_token_initial_supply.to_string(), @@ -682,29 +680,18 @@ fn assert_balances( fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { let data = "608060405234801561001057600080fd5b5060038054600160a060020a031916331790819055604051600160a060020a0391909116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3610080336b01866de34549d620d8000000640100000000610b9461008582021704565b610156565b600160a060020a038216151561009a57600080fd5b6002546100b490826401000000006109a461013d82021704565b600255600160a060020a0382166000908152602081905260409020546100e790826401000000006109a461013d82021704565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60008282018381101561014f57600080fd5b9392505050565b610c6a806101656000396000f3006080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610100578063095ea7b31461018a57806318160ddd146101c257806323b872dd146101e95780632ff2e9dc14610213578063313ce56714610228578063395093511461025357806342966c681461027757806370a0823114610291578063715018a6146102b257806379cc6790146102c75780638da5cb5b146102eb5780638f32d59b1461031c57806395d89b4114610331578063a457c2d714610346578063a9059cbb1461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561014f578181015183820152602001610137565b50505050905090810190601f16801561017c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019657600080fd5b506101ae600160a060020a0360043516602435610436565b604080519115158252519081900360200190f35b3480156101ce57600080fd5b506101d7610516565b60408051918252519081900360200190f35b3480156101f557600080fd5b506101ae600160a060020a036004358116906024351660443561051c565b34801561021f57600080fd5b506101d76105b9565b34801561023457600080fd5b5061023d6105c9565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b506101ae600160a060020a03600435166024356105ce565b34801561028357600080fd5b5061028f60043561067e565b005b34801561029d57600080fd5b506101d7600160a060020a036004351661068b565b3480156102be57600080fd5b5061028f6106a6565b3480156102d357600080fd5b5061028f600160a060020a0360043516602435610710565b3480156102f757600080fd5b5061030061071e565b60408051600160a060020a039092168252519081900360200190f35b34801561032857600080fd5b506101ae61072d565b34801561033d57600080fd5b5061011561073e565b34801561035257600080fd5b506101ae600160a060020a0360043516602435610775565b34801561037657600080fd5b506101ae600160a060020a03600435166024356107c0565b34801561039a57600080fd5b506101d7600160a060020a03600435811690602435166107d6565b3480156103c157600080fd5b5061028f600160a060020a0360043516610801565b606060405190810160405280602481526020017f486f7420746865206e657720746f6b656e20796f75277265206c6f6f6b696e6781526020017f20666f720000000000000000000000000000000000000000000000000000000081525081565b600081158061044c575061044a33846107d6565b155b151561050557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f55736520696e637265617365417070726f76616c206f7220646563726561736560448201527f417070726f76616c20746f2070726576656e7420646f75626c652d7370656e6460648201527f2e00000000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b61050f838361081d565b9392505050565b60025490565b600160a060020a038316600090815260016020908152604080832033845290915281205482111561054c57600080fd5b600160a060020a0384166000908152600160209081526040808320338452909152902054610580908363ffffffff61089b16565b600160a060020a03851660009081526001602090815260408083203384529091529020556105af8484846108b2565b5060019392505050565b6b01866de34549d620d800000081565b601281565b6000600160a060020a03831615156105e557600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff6109a416565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b61068833826109b6565b50565b600160a060020a031660009081526020819052604090205490565b6106ae61072d565b15156106b957600080fd5b600354604051600091600160a060020a0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36003805473ffffffffffffffffffffffffffffffffffffffff19169055565b61071a8282610a84565b5050565b600354600160a060020a031690565b600354600160a060020a0316331490565b60408051808201909152600381527f484f540000000000000000000000000000000000000000000000000000000000602082015281565b6000600160a060020a038316151561078c57600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff61089b16565b60006107cd3384846108b2565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b61080961072d565b151561081457600080fd5b61068881610b16565b6000600160a060020a038316151561083457600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b600080838311156108ab57600080fd5b5050900390565b600160a060020a0383166000908152602081905260409020548111156108d757600080fd5b600160a060020a03821615156108ec57600080fd5b600160a060020a038316600090815260208190526040902054610915908263ffffffff61089b16565b600160a060020a03808516600090815260208190526040808220939093559084168152205461094a908263ffffffff6109a416565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282018381101561050f57600080fd5b600160a060020a03821615156109cb57600080fd5b600160a060020a0382166000908152602081905260409020548111156109f057600080fd5b600254610a03908263ffffffff61089b16565b600255600160a060020a038216600090815260208190526040902054610a2f908263ffffffff61089b16565b600160a060020a038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600160a060020a0382166000908152600160209081526040808320338452909152902054811115610ab457600080fd5b600160a060020a0382166000908152600160209081526040808320338452909152902054610ae8908263ffffffff61089b16565b600160a060020a038316600090815260016020908152604080832033845290915290205561071a82826109b6565b600160a060020a0381161515610b2b57600080fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600160a060020a0382161515610ba957600080fd5b600254610bbc908263ffffffff6109a416565b600255600160a060020a038216600090815260208190526040902054610be8908263ffffffff6109a416565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350505600a165627a7a72305820d4ad56dfe541fec48c3ecb02cebad565a998dfca7774c0c4f4b1f4a8e2363a590029".from_hex::>().unwrap(); - let gas_price = 2_000_000_000_u64; + let gas_price = 50_000_000_000_u64; let gas_limit = 1_000_000_u64; let tx = TransactionParameters { nonce: Some(ethereum_types::U256::try_from(0).expect("Internal error")), to: None, gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), - value: ethereum_types::U256::try_from(0).expect("Internal error"), + value: ethereum_types::U256::zero(), data: Bytes(data), chain_id: Some(chain.rec().num_chain_id), }; - - let signed_tx = web3 - .accounts() - .sign_transaction( - tx, - &wallet - .prepare_secp256k1_secret() - .expect("wallet without secret"), - ) - .wait() - .expect("transaction preparation failed"); - + let signed_tx = sign_transaction(web3, tx, wallet); match web3 .eth() .send_raw_transaction(signed_tx.raw_transaction) @@ -721,6 +708,52 @@ fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Ad } } +fn transfer_tokens_to_address( + from_wallet: &Wallet, + to_wallet: &Wallet, + amount: u128, + transaction_nonce: u64, + web3: &Web3, + chain: Chain, +){ + let data = transaction_data_web3(to_wallet, amount); + let gas_price = 150_000_000_000_u64; + let gas_limit = 1_000_000_u64; + let tx = TransactionParameters { + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + to: Some(to_wallet.address()), + gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), + gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), + value: ethereum_types::U256::zero(), + data: Bytes(data.to_vec()), + chain_id: Some(chain.rec().num_chain_id), + }; + + let signed_tx = sign_transaction(web3, tx, from_wallet); + + match web3 + .eth() + .send_raw_transaction(signed_tx.raw_transaction) + .wait() + { + Ok(tx_hash) => eprintln!("Transaction {} was sent", tx_hash), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } +} + +fn sign_transaction(web3: &Web3, tx: TransactionParameters, signing_wallet: &Wallet)->SignedTransaction{ + web3 + .accounts() + .sign_transaction( + tx, + &signing_wallet + .prepare_secp256k1_secret() + .expect("wallet without secret"), + ) + .wait() + .expect("transaction preparation failed") +} + fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { let extended_priv_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); let secret = extended_priv_key.secret().to_hex::(); 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 ab362e39c..2d55fcd49 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -38,6 +38,7 @@ use web3::types::{ use web3::{BatchTransport, Error, Web3}; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; use crate::blockchain::blockchain_interface::data_structures::{BlockchainTransaction, ProcessedPayableFallible, RpcPayablesFailure}; +use crate::sub_lib::blockchain_interface_web3::transaction_data_web3; const CONTRACT_ABI: &str = indoc!( r#"[{ @@ -64,8 +65,6 @@ const TRANSACTION_LITERAL: H256 = H256([ 0x95, 0x2b, 0xa7, 0xf1, 0x63, 0xc4, 0xa1, 0x16, 0x28, 0xf5, 0x5a, 0x4d, 0xf5, 0x23, 0xb3, 0xef, ]); -const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; - pub const REQUESTS_IN_PARALLEL: usize = 1; pub struct BlockchainInterfaceWeb3 @@ -507,7 +506,7 @@ where nonce: U256, gas_price: u64, ) -> Result { - let data = Self::transaction_data(recipient, amount); + let data = transaction_data_web3(recipient, amount); let gas_limit = self.compute_gas_limit(data.as_slice()); let gas_price = gwei_to_wei::(gas_price); let transaction_parameters = TransactionParameters { @@ -560,16 +559,6 @@ where introduction.chain(body).collect() } - fn transaction_data(recipient: &Wallet, amount: u128) -> [u8; 68] { - let mut data = [0u8; 4 + 32 + 32]; - data[0..4].copy_from_slice(&TRANSFER_METHOD_ID); - data[16..36].copy_from_slice(&recipient.address().0[..]); - U256::try_from(amount) - .expect("shouldn't overflow") - .to_big_endian(&mut data[36..68]); - data - } - fn compute_gas_limit(&self, data: &[u8]) -> U256 { ethereum_types::U256::try_from(data.iter().fold(self.gas_limit_const_part, |acc, v| { acc + if v == &0u8 { 4 } else { 68 } @@ -631,7 +620,6 @@ mod tests { use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, - TRANSFER_METHOD_ID, }; use crate::blockchain::blockchain_interface::test_utils::{ test_blockchain_interface_is_connected_and_functioning, LowBlockchainIntMock, @@ -716,7 +704,6 @@ mod tests { }; assert_eq!(CONTRACT_ABI, contract_abi_expected); assert_eq!(TRANSACTION_LITERAL, transaction_literal_expected); - assert_eq!(TRANSFER_METHOD_ID, [0xa9, 0x05, 0x9c, 0xbb]); assert_eq!(REQUESTS_IN_PARALLEL, 1); } @@ -1778,10 +1765,6 @@ mod tests { ); } - const TEST_PAYMENT_AMOUNT: u128 = 1_000_000_000_000; - const TEST_GAS_PRICE_ETH: u64 = 110; - const TEST_GAS_PRICE_POLYGON: u64 = 50; - fn test_consuming_wallet_with_secret() -> Wallet { let key_pair = Bip32EncryptionKeyProvider::from_raw_secret( &decode_hex("97923d8fd8de4a00f912bfb77ef483141dec551bd73ea59343ef5c4aac965d04") @@ -1803,6 +1786,10 @@ mod tests { nonce: u64, template: &[u8], ) { + const TEST_PAYMENT_AMOUNT: u128 = 1_000_000_000_000; + const TEST_GAS_PRICE_ETH: u64 = 110; + const TEST_GAS_PRICE_POLYGON: u64 = 50; + let transport = TestTransport::default(); let subject = BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); let consuming_wallet = test_consuming_wallet_with_secret(); @@ -2192,12 +2179,4 @@ mod tests { .pending_transaction_id_result(nonce), ) } - - #[test] - fn hash_the_smart_contract_transfer_function_signature() { - assert_eq!( - "transfer(address,uint256)".keccak256()[0..4], - TRANSFER_METHOD_ID, - ); - } } diff --git a/node/src/sub_lib/blockchain_interface_web3.rs b/node/src/sub_lib/blockchain_interface_web3.rs new file mode 100644 index 000000000..98224b49a --- /dev/null +++ b/node/src/sub_lib/blockchain_interface_web3.rs @@ -0,0 +1,35 @@ +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::sub_lib::wallet::Wallet; +use web3::types::U256; + +const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; + +pub fn transaction_data_web3(recipient: &Wallet, amount: u128) -> [u8; 68] { + let mut data = [0u8; 4 + 32 + 32]; + data[0..4].copy_from_slice(&TRANSFER_METHOD_ID); + data[16..36].copy_from_slice(&recipient.address().0[..]); + U256::try_from(amount) + .expect("shouldn't overflow") + .to_big_endian(&mut data[36..68]); + data +} + +#[cfg(test)] +mod tests { + use crate::sub_lib::blockchain_interface_web3::TRANSFER_METHOD_ID; + use ethsign_crypto::Keccak256; + + #[test] + fn constants_are_correct() { + assert_eq!(TRANSFER_METHOD_ID, [0xa9, 0x05, 0x9c, 0xbb]); + } + + #[test] + fn hash_the_smart_contract_transfer_function_signature() { + assert_eq!( + "transfer(address,uint256)".keccak256()[0..4], + TRANSFER_METHOD_ID, + ); + } +} diff --git a/node/src/sub_lib/mod.rs b/node/src/sub_lib/mod.rs index 51360357b..92c9cfd12 100644 --- a/node/src/sub_lib/mod.rs +++ b/node/src/sub_lib/mod.rs @@ -9,6 +9,7 @@ pub mod accountant; pub mod bidi_hashmap; pub mod binary_traverser; pub mod blockchain_bridge; +pub mod blockchain_interface_web3; pub mod channel_wrappers; pub mod combined_parameters; pub mod configurator; From 9779230f26190bde152747ab2960f21d9214a04d Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 22 Nov 2023 22:40:40 +0800 Subject: [PATCH 099/250] GH-711: clearer version of verify_bill_payment, with tooling allowing me to write the second test --- .../tests/verify_bill_payment.rs | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 0f633a519..49c291d80 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -18,6 +18,7 @@ use multinode_integration_tests_lib::utils::{ }; use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; +use node_lib::accountant::gwei_to_wei; use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, @@ -27,6 +28,7 @@ use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, }; use node_lib::sub_lib::accountant::PaymentThresholds; +use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; use rustc_hex::{FromHex, ToHex}; @@ -38,8 +40,6 @@ use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters}; use web3::Web3; -use node_lib::accountant::gwei_to_wei; -use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; #[test] fn verify_bill_payment() { @@ -89,15 +89,23 @@ fn verify_bill_payment() { derivation_path(0, 1), None, ); - let consuming_node_balance = gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); - transfer_tokens_to_address(&contract_owner_wallet, &consuming_node_wallet, consuming_node_balance, 1, &web3, cluster.chain); - thread::sleep(Duration::from_secs(10)); - // assert_balances( - // &consuming_node_wallet, - // &blockchain_interface, - // "100000000000000000000", - // &consuming_node_balance.to_string(), - // ); + let consuming_node_initial_service_fee_balance = + gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); + transfer_tokens_to_address( + contract_addr, + &contract_owner_wallet, + &consuming_node_wallet, + consuming_node_initial_service_fee_balance, + 1, + &web3, + cluster.chain, + ); + assert_balances( + &consuming_node_wallet, + &blockchain_interface, + "100000000000000000000", // Default at ganache + &consuming_node_initial_service_fee_balance.to_string(), + ); let (serving_node_1_config, serving_node_1_wallet) = build_config( &blockchain_server, &seed, @@ -175,7 +183,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); serving_node_1_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_1) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_1) .unwrap(); open_all_file_permissions(serving_node_1_path.clone().into()); @@ -190,7 +198,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); serving_node_2_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_2) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_2) .unwrap(); open_all_file_permissions(serving_node_2_path.clone().into()); @@ -205,7 +213,7 @@ fn verify_bill_payment() { .unwrap(); let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); serving_node_3_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount_3) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_3) .unwrap(); open_all_file_permissions(serving_node_3_path.clone().into()); @@ -214,15 +222,13 @@ fn verify_bill_payment() { expire_receivables(serving_node_2_path.into()); expire_receivables(serving_node_3_path.into()); - let expected_deployed_token_initial_supply = 472_000_000_000_000_000_000_000_000_u128; - // Note: ganache defaults the balance of each created account to 100 ETH assert_balances( &consuming_node_wallet, &blockchain_interface, - "99998381140000000000", - &expected_deployed_token_initial_supply.to_string(), + "100000000000000000000", + &consuming_node_initial_service_fee_balance.to_string(), ); assert_balances( @@ -265,12 +271,12 @@ fn verify_bill_payment() { } let expected_final_token_balance_consuming_node = - expected_deployed_token_initial_supply - (amount_1 + amount_2 + amount_3); + consuming_node_initial_service_fee_balance - (amount_1 + amount_2 + amount_3); assert_balances( - &contract_owner_wallet, + &consuming_node_wallet, &blockchain_interface, - "99998223622000000000", + "99999842482000000000", &expected_final_token_balance_consuming_node.to_string(), ); @@ -322,21 +328,21 @@ fn verify_bill_payment() { } test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_1_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false } }); test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_2_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false } }); test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_3_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false @@ -565,7 +571,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { assert_balances( &contract_owner_wallet, &blockchain_interface, - "99998223682000000000", + "99951655600000000000", &expected_final_token_balance_consuming_node.to_string(), ); @@ -590,7 +596,6 @@ fn payments_were_adjusted_due_to_insufficient_balance() { amount.to_string().as_str(), ); - //TODO we can do this by a client call --scans payables let serving_node_1 = cluster.start_named_real_node( &serving_node_1_name, serving_node_1_index, @@ -709,19 +714,20 @@ fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Ad } fn transfer_tokens_to_address( + contract_addr: Address, from_wallet: &Wallet, to_wallet: &Wallet, amount: u128, transaction_nonce: u64, web3: &Web3, chain: Chain, -){ +) { let data = transaction_data_web3(to_wallet, amount); let gas_price = 150_000_000_000_u64; let gas_limit = 1_000_000_u64; let tx = TransactionParameters { nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), - to: Some(to_wallet.address()), + to: Some(contract_addr), gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), value: ethereum_types::U256::zero(), @@ -741,9 +747,12 @@ fn transfer_tokens_to_address( } } -fn sign_transaction(web3: &Web3, tx: TransactionParameters, signing_wallet: &Wallet)->SignedTransaction{ - web3 - .accounts() +fn sign_transaction( + web3: &Web3, + tx: TransactionParameters, + signing_wallet: &Wallet, +) -> SignedTransaction { + web3.accounts() .sign_transaction( tx, &signing_wallet From c0b3eb7daa236695fe8c2a4f6bfce70fa2452c41 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:18:09 +0800 Subject: [PATCH 100/250] GH-711: trying to parametrize a huge test body --- .../tests/verify_bill_payment.rs | 557 +++++++++++++++--- 1 file changed, 485 insertions(+), 72 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 49c291d80..dd349da66 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -11,7 +11,8 @@ use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, EarningWalletInfo, NodeStartupConfig, NodeStartupConfigBuilder, + ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeStartupConfig, + NodeStartupConfigBuilder, }; use multinode_integration_tests_lib::utils::{ node_chain_specific_data_directory, open_all_file_permissions, UrlHolder, @@ -89,8 +90,10 @@ fn verify_bill_payment() { derivation_path(0, 1), None, ); + let consuming_node_initial_service_fee_balance = gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); + transfer_tokens_to_address( contract_addr, &contract_owner_wallet, @@ -100,12 +103,14 @@ fn verify_bill_payment() { &web3, cluster.chain, ); + assert_balances( &consuming_node_wallet, &blockchain_interface, "100000000000000000000", // Default at ganache &consuming_node_initial_service_fee_balance.to_string(), ); + let (serving_node_1_config, serving_node_1_wallet) = build_config( &blockchain_server, &seed, @@ -131,9 +136,9 @@ fn verify_bill_payment() { let amount = |addition: u128| { payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128 + addition }; - let amount_1 = amount(123); - let amount_2 = amount(456); - let amount_3 = amount(789); + let payment_to_serving_node_1 = amount(123); + let payment_to_serving_node_2 = amount(456); + let payment_to_serving_node_3 = amount(789); let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); @@ -163,13 +168,13 @@ fn verify_bill_payment() { ); let now = SystemTime::now(); consuming_payable_dao - .more_money_payable(now, &serving_node_1_wallet, amount_1) + .more_money_payable(now, &serving_node_1_wallet, payment_to_serving_node_1) .unwrap(); consuming_payable_dao - .more_money_payable(now, &serving_node_2_wallet, amount_2) + .more_money_payable(now, &serving_node_2_wallet, payment_to_serving_node_2) .unwrap(); consuming_payable_dao - .more_money_payable(now, &serving_node_3_wallet, amount_3) + .more_money_payable(now, &serving_node_3_wallet, payment_to_serving_node_3) .unwrap(); let (serving_node_1_name, serving_node_1_index) = @@ -183,7 +188,11 @@ fn verify_bill_payment() { .unwrap(); let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); serving_node_1_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_1) + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + payment_to_serving_node_1, + ) .unwrap(); open_all_file_permissions(serving_node_1_path.clone().into()); @@ -198,7 +207,11 @@ fn verify_bill_payment() { .unwrap(); let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); serving_node_2_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_2) + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + payment_to_serving_node_2, + ) .unwrap(); open_all_file_permissions(serving_node_2_path.clone().into()); @@ -213,7 +226,11 @@ fn verify_bill_payment() { .unwrap(); let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); serving_node_3_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount_3) + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + payment_to_serving_node_3, + ) .unwrap(); open_all_file_permissions(serving_node_3_path.clone().into()); @@ -270,8 +287,8 @@ fn verify_bill_payment() { thread::sleep(Duration::from_millis(400)); } - let expected_final_token_balance_consuming_node = - consuming_node_initial_service_fee_balance - (amount_1 + amount_2 + amount_3); + let expected_final_token_balance_consuming_node = consuming_node_initial_service_fee_balance + - (payment_to_serving_node_1 + payment_to_serving_node_2 + payment_to_serving_node_3); assert_balances( &consuming_node_wallet, @@ -284,21 +301,21 @@ fn verify_bill_payment() { &serving_node_1_wallet, &blockchain_interface, "100000000000000000000", - &amount_1.to_string(), + &payment_to_serving_node_1.to_string(), ); assert_balances( &serving_node_2_wallet, &blockchain_interface, "100000000000000000000", - &amount_2.to_string(), + &payment_to_serving_node_2.to_string(), ); assert_balances( &serving_node_3_wallet, &blockchain_interface, "100000000000000000000", - amount_3.to_string().as_str(), + payment_to_serving_node_3.to_string().as_str(), ); let serving_node_1 = cluster.start_named_real_node( @@ -375,12 +392,6 @@ fn payments_were_adjusted_due_to_insufficient_balance() { contract_addr ); let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); - assert_balances( - &contract_owner_wallet, - &blockchain_interface, - "99998381140000000000", - "472000000000000000000000000", - ); let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_592_000, debt_threshold_gwei: 1_000_000_000, @@ -389,13 +400,13 @@ fn payments_were_adjusted_due_to_insufficient_balance() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; + let consuming_node_ui_port = find_free_port(); - // You might've noticed that we assume the consuming Node is configured with the contract owner - // wallet. That does not seem right but it has likely been chosen because at the beginning, when - // the contract is deployd, only the contract owner possesses the tokens. So, avoiding extra - // complexity from "airdropping" the tokens, we pretend this is okay. Above all, it completely - // is for this kind of tests. - let (consuming_config, _) = build_config( + let serving_node_1_ui_port = find_free_port(); + let serving_node_2_ui_port = find_free_port(); + let serving_node_3_ui_port = find_free_port(); + + let (consuming_config, consuming_node_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, @@ -403,29 +414,55 @@ fn payments_were_adjusted_due_to_insufficient_balance() { Some(consuming_node_ui_port), ); + //TODO figure out how much it should be + let consuming_node_initial_service_fee_balance = + gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); + + transfer_tokens_to_address( + contract_addr, + &contract_owner_wallet, + &consuming_node_wallet, + consuming_node_initial_service_fee_balance, + 1, + &web3, + cluster.chain, + ); + + assert_balances( + &consuming_node_wallet, + &blockchain_interface, + "100000000000000000000", // Default at ganache + &consuming_node_initial_service_fee_balance.to_string(), + ); + let (serving_node_1_config, serving_node_1_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 1), - None, + Some(serving_node_1_ui_port), ); let (serving_node_2_config, serving_node_2_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 2), - None, + Some(serving_node_2_ui_port), ); let (serving_node_3_config, serving_node_3_wallet) = build_config( &blockchain_server, &seed, payment_thresholds, derivation_path(0, 3), - None, + Some(serving_node_3_ui_port), ); - let amount = payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128; + let amount = |addition: u128| -> u128 { + todo!("hmmm"); + }; + let owed_to_serving_node_1 = amount(123); + let owed_to_serving_node_2 = amount(456); + let owed_to_serving_node_3 = amount(789); let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); @@ -435,8 +472,9 @@ fn payments_were_adjusted_due_to_insufficient_balance() { make_init_config(cluster.chain), ) .unwrap(); - let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); + let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); open_all_file_permissions(consuming_node_path.clone().into()); + assert_eq!( format!("{}", &contract_owner_wallet), "0x5a4d5df91d0124dec73dbd112f82d6077ccab47d" @@ -453,16 +491,6 @@ fn payments_were_adjusted_due_to_insufficient_balance() { format!("{}", &serving_node_3_wallet), "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" ); - let now = SystemTime::now(); - consuming_payable_dao - .more_money_payable(now, &serving_node_1_wallet, amount) - .unwrap(); - consuming_payable_dao - .more_money_payable(now, &serving_node_2_wallet, amount) - .unwrap(); - consuming_payable_dao - .more_money_payable(now, &serving_node_3_wallet, amount) - .unwrap(); let (serving_node_1_name, serving_node_1_index) = cluster.prepare_real_node(&serving_node_1_config); @@ -475,7 +503,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { .unwrap(); let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); serving_node_1_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) .unwrap(); open_all_file_permissions(serving_node_1_path.clone().into()); @@ -490,7 +518,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { .unwrap(); let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); serving_node_2_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) .unwrap(); open_all_file_permissions(serving_node_2_path.clone().into()); @@ -505,7 +533,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { .unwrap(); let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); serving_node_3_receivable_dao - .more_money_receivable(SystemTime::now(), &contract_owner_wallet, amount) + .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) .unwrap(); open_all_file_permissions(serving_node_3_path.clone().into()); @@ -547,19 +575,27 @@ fn payments_were_adjusted_due_to_insufficient_balance() { let real_consuming_node = cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); - // Now let's command the consuming Node to check its payables - let ui_client = real_consuming_node.make_ui(consuming_node_ui_port); - ui_client.send_request( - UiScanRequest { - scan_type: ScanType::Payables, - } - .tmb(5555), + let process_scan_request_to_node = + |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { + let ui_client = real_consuming_node.make_ui(ui_port); + ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); + let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); + UiScanResponse::fmb(response).unwrap(); + }; + + // Commanding the consuming Node to check its payables + process_scan_request_to_node( + &real_consuming_node, + consuming_node_ui_port, + ScanType::Payables, + 5555, ); - let response = ui_client.wait_for_response(5555, Duration::from_secs(10)); - UiScanResponse::fmb(response).unwrap(); let now = Instant::now(); - while !consuming_payable_dao.non_pending_payables().is_empty() + while !consuming_node_payable_dao + .payable_dao + .non_pending_payables() + .is_empty() && now.elapsed() < Duration::from_secs(10) { thread::sleep(Duration::from_millis(400)); @@ -569,7 +605,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { expected_deployed_token_initial_supply - 3 * amount; assert_balances( - &contract_owner_wallet, + &consuming_node_wallet, &blockchain_interface, "99951655600000000000", &expected_final_token_balance_consuming_node.to_string(), @@ -579,21 +615,21 @@ fn payments_were_adjusted_due_to_insufficient_balance() { &serving_node_1_wallet, &blockchain_interface, "100000000000000000000", - &amount.to_string(), + "22222222222", ); assert_balances( &serving_node_2_wallet, &blockchain_interface, "100000000000000000000", - &amount.to_string(), + "0", ); assert_balances( &serving_node_3_wallet, &blockchain_interface, "100000000000000000000", - amount.to_string().as_str(), + "11111111111", ); let serving_node_1 = cluster.start_named_real_node( @@ -601,43 +637,51 @@ fn payments_were_adjusted_due_to_insufficient_balance() { serving_node_1_index, serving_node_1_config, ); + process_scan_request_to_node( + &serving_node_1, + serving_node_1_ui_port, + ScanType::Receivables, + 2222, + ); let serving_node_2 = cluster.start_named_real_node( &serving_node_2_name, serving_node_2_index, serving_node_2_config, ); + process_scan_request_to_node( + &serving_node_2, + serving_node_2_ui_port, + ScanType::Receivables, + 3333, + ); let serving_node_3 = cluster.start_named_real_node( &serving_node_3_name, serving_node_3_index, serving_node_3_config, ); - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(serving_node_1.node_reference()) - .neighbor(serving_node_2.node_reference()) - .neighbor(serving_node_3.node_reference()) - .build(), - ); - } + process_scan_request_to_node( + &serving_node_3, + serving_node_3_ui_port, + ScanType::Receivables, + 4444, + ); test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_1_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false } }); test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_2_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false } }); test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_3_receivable_dao.account_status(&contract_owner_wallet) { + if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { status.balance_wei == 0 } else { false @@ -835,3 +879,372 @@ fn expire_receivables(path: PathBuf) { .unwrap(); config_stmt.execute([]).unwrap(); } + +struct TestInputsOutputsConfig { + ui_ports_opt: Option, + cons_node_initial_transaction_fee_balance_minor: u128, + cons_node_initial_service_fee_balance_minor: u128, + service_fee_owed_to_serv_node_1_minor: u128, + service_fee_owed_to_serv_node_2_minor: u128, + service_fee_owed_to_serv_node_3_minor: u128, + + exp_final_cons_node_transaction_fee_balance_minor: u128, + exp_final_cons_node_service_fee_balance_minor: u128, + exp_final_service_fee_balance_serv_node_1_minor: u128, + exp_final_service_fee_balance_serv_node_2_minor: u128, + exp_final_service_fee_balance_serv_node_3_minor: u128, +} + +enum UiPorts { + ConsNode, + ServNode1, + ServNode2, + ServNode3, +} + +impl TestInputsOutputsConfig { + fn port(&self, requested: UiPorts) -> Option { + self.ui_ports_opt.map(|ports| match requested { + UiPorts::ConsNode => ports.consuming_node, + UiPorts::ServNode1 => ports.serving_node_1, + UiPorts::ServNode2 => ports.serving_node_2, + UiPorts::ServNode3 => ports.serving_node_3, + }) + } +} + +struct Ports { + consuming_node: u16, + serving_node_1: u16, + serving_node_2: u16, + serving_node_3: u16, +} + +struct ServingNodeAttributes<'a> { + name: String, + index: usize, + config: &'a NodeStartupConfig, +} + +fn test_body( + global_config: TestInputsOutputsConfig, + stimulate_payments: StimulateConsumingNodePayments, + start_serving_nodes_and_run_check: StartServingNodesAndLetThemPerformReceivablesCheck, +) where + StimulateConsumingNodePayments: FnOnce(&MASQNodeCluster, &MASQRealNode), + StartServingNodesAndLetThemPerformReceivablesCheck: + FnOnce( + &MASQNodeCluster, + ServingNodeAttributes, + ServingNodeAttributes, + ServingNodeAttributes, + &TestInputsOutputsConfig, + ) -> (MASQRealNode, MASQRealNode, MASQRealNode), +{ + let mut cluster = match MASQNodeCluster::start() { + Ok(cluster) => cluster, + Err(e) => panic!("{}", e), + }; + let blockchain_server = BlockchainServer { + name: "ganache-cli", + }; + blockchain_server.start(); + blockchain_server.wait_until_ready(); + let url = blockchain_server.url().to_string(); + let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); + let web3 = Web3::new(http.clone()); + let deriv_path = derivation_path(0, 0); + let seed = make_seed(); + let (contract_owner_wallet, _) = make_node_wallet(&seed, &deriv_path); + let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); + assert_eq!( + contract_addr, + cluster.chain.rec().contract, + "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", + contract_addr + ); + let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); + let payment_thresholds = PaymentThresholds { + threshold_interval_sec: 2_592_000, + debt_threshold_gwei: 1_000_000_000, + payment_grace_period_sec: 86_400, + maturity_threshold_sec: 86_400, + permanent_debt_allowed_gwei: 10_000_000, + unban_below_gwei: 10_000_000, + }; + + let (consuming_config, consuming_node_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + deriv_path, + global_config.port(UiPorts::ConsNode), + ); + + transfer_tokens_to_address( + contract_addr, + &contract_owner_wallet, + &consuming_node_wallet, + global_config.cons_node_initial_service_fee_balance_minor, + 1, + &web3, + cluster.chain, + ); + + assert_balances( + &consuming_node_wallet, + &blockchain_interface, + "100000000000000000000", // Default at ganache + &global_config + .cons_node_initial_service_fee_balance_minor + .to_string(), + ); + + let (serving_node_1_config, serving_node_1_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 1), + global_config.port(UiPorts::ServNode1), + ); + let (serving_node_2_config, serving_node_2_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 2), + global_config.port(UiPorts::ServNode2), + ); + let (serving_node_3_config, serving_node_3_wallet) = build_config( + &blockchain_server, + &seed, + payment_thresholds, + derivation_path(0, 3), + global_config.port(UiPorts::ServNode3), + ); + + let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); + let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); + let consuming_node_connection = DbInitializerReal::default() + .initialize( + Path::new(&consuming_node_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); + open_all_file_permissions(consuming_node_path.clone().into()); + + assert_eq!( + format!("{}", &contract_owner_wallet), + "0x5a4d5df91d0124dec73dbd112f82d6077ccab47d" + ); + assert_eq!( + format!("{}", &serving_node_1_wallet), + "0x7a3cf474962646b18666b5a5be597bb0af013d81" + ); + assert_eq!( + format!("{}", &serving_node_2_wallet), + "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" + ); + assert_eq!( + format!("{}", &serving_node_3_wallet), + "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" + ); + + let (serving_node_1_name, serving_node_1_index) = + cluster.prepare_real_node(&serving_node_1_config); + let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); + let serving_node_1_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_1_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); + serving_node_1_receivable_dao + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + global_config.service_fee_owed_to_serv_node_1_minor, + ) + .unwrap(); + open_all_file_permissions(serving_node_1_path.clone().into()); + + let (serving_node_2_name, serving_node_2_index) = + cluster.prepare_real_node(&serving_node_2_config); + let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); + let serving_node_2_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_2_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); + serving_node_2_receivable_dao + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + global_config.service_fee_owed_to_serv_node_2_minor, + ) + .unwrap(); + open_all_file_permissions(serving_node_2_path.clone().into()); + + let (serving_node_3_name, serving_node_3_index) = + cluster.prepare_real_node(&serving_node_3_config); + let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); + let serving_node_3_connection = DbInitializerReal::default() + .initialize( + Path::new(&serving_node_3_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); + serving_node_3_receivable_dao + .more_money_receivable( + SystemTime::now(), + &consuming_node_wallet, + global_config.service_fee_owed_to_serv_node_3_minor, + ) + .unwrap(); + open_all_file_permissions(serving_node_3_path.clone().into()); + + expire_payables(consuming_node_path.into()); + expire_receivables(serving_node_1_path.into()); + expire_receivables(serving_node_2_path.into()); + expire_receivables(serving_node_3_path.into()); + + assert_balances( + &consuming_node_wallet, + &blockchain_interface, + &global_config + .cons_node_initial_transaction_fee_balance_minor + .to_string(), + &global_config + .cons_node_initial_transaction_fee_balance_minor + .to_string(), + ); + + assert_balances( + &serving_node_1_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + assert_balances( + &serving_node_2_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + assert_balances( + &serving_node_3_wallet, + &blockchain_interface, + "100000000000000000000", + "0", + ); + + let real_consuming_node = + cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); + + stimulate_payments(&cluster, &real_consuming_node); + + let now = Instant::now(); + while !consuming_node_payable_dao + .payable_dao + .non_pending_payables() + .is_empty() + && now.elapsed() < Duration::from_secs(10) + { + thread::sleep(Duration::from_millis(400)); + } + + assert_balances( + &consuming_node_wallet, + &blockchain_interface, + &global_config + .exp_final_cons_node_transaction_fee_balance_minor + .to_string(), + &global_config + .exp_final_cons_node_service_fee_balance_minor + .to_string(), + ); + + assert_balances( + &serving_node_1_wallet, + &blockchain_interface, + "100000000000000000000", + &global_config + .exp_final_service_fee_balance_serv_node_1_minor + .to_string(), + ); + + assert_balances( + &serving_node_2_wallet, + &blockchain_interface, + "100000000000000000000", + &global_config + .exp_final_cons_node_service_fee_balance_minor + .to_string(), + ); + + assert_balances( + &serving_node_3_wallet, + &blockchain_interface, + "100000000000000000000", + &global_config + .exp_final_cons_node_service_fee_balance_minor + .to_string(), + ); + + let serving_node_1_attributes = ServingNodeAttributes { + name: "".to_string(), + index: serving_node_1_index, + config: &serving_node_1_config, + }; + let serving_node_2_attributes = ServingNodeAttributes { + name: "".to_string(), + index: serving_node_2_index, + config: &serving_node_2_config, + }; + let serving_node_3_attributes = ServingNodeAttributes { + name: "".to_string(), + index: serving_node_3_index, + config: &serving_node_1_config, + }; + start_serving_nodes_and_run_check( + &cluster, + serving_node_1_attributes, + serving_node_2_attributes, + serving_node_3_attributes, + &global_config, + ); + + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { + status.balance_wei + == (global_config.service_fee_owed_to_serv_node_1_minor + - global_config.exp_final_service_fee_balance_serv_node_1_minor) + } else { + false + } + }); + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { + status.balance_wei + == (global_config.service_fee_owed_to_serv_node_2_minor + - global_config.exp_final_service_fee_balance_serv_node_2_minor) + } else { + false + } + }); + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { + status.balance_wei + == (global_config.service_fee_owed_to_serv_node_3_minor + - global_config.exp_final_service_fee_balance_serv_node_3_minor) + } else { + false + } + }); +} From 0e56c040bd367127c17dbde62039cb4e15704bb3 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 26 Nov 2023 19:20:05 +0800 Subject: [PATCH 101/250] GH-711: remake of the old test is pleasure to run now --- .../tests/verify_bill_payment.rs | 1297 ++++++++--------- 1 file changed, 579 insertions(+), 718 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index dd349da66..0ab24db80 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -2,11 +2,7 @@ use bip39::{Language, Mnemonic, Seed}; use futures::Future; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::WEIS_IN_GWEI; -use masq_lib::messages::FromMessageBody; -use masq_lib::messages::ToMessageBody; -use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; -use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; +use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; @@ -53,640 +49,505 @@ fn verify_bill_payment() { // into a simpler one by using directly the `scans` command from a UI through // which we would achieve the same with less PCU work an in a shorter time. // See the second test in this file. - let mut cluster = match MASQNodeCluster::start() { - Ok(cluster) => cluster, - Err(e) => panic!("{}", e), - }; - let blockchain_server = BlockchainServer { - name: "ganache-cli", - }; - blockchain_server.start(); - blockchain_server.wait_until_ready(); - let url = blockchain_server.url().to_string(); - let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); - let web3 = Web3::new(http.clone()); - let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); - let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - assert_eq!( - contract_addr, - cluster.chain.rec().contract, - "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", - contract_addr - ); - let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); let payment_thresholds = PaymentThresholds { - threshold_interval_sec: 2_592_000, + threshold_interval_sec: 2_500_000, debt_threshold_gwei: 1_000_000_000, - payment_grace_period_sec: 86_400, - maturity_threshold_sec: 86_400, + payment_grace_period_sec: 85_000, + maturity_threshold_sec: 85_000, permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; - let (consuming_config, consuming_node_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 1), - None, - ); - - let consuming_node_initial_service_fee_balance = - gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); - - transfer_tokens_to_address( - contract_addr, - &contract_owner_wallet, - &consuming_node_wallet, - consuming_node_initial_service_fee_balance, - 1, - &web3, - cluster.chain, - ); - - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - "100000000000000000000", // Default at ganache - &consuming_node_initial_service_fee_balance.to_string(), - ); - - let (serving_node_1_config, serving_node_1_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 2), - None, - ); - let (serving_node_2_config, serving_node_2_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 3), - None, - ); - let (serving_node_3_config, serving_node_3_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 4), - None, - ); - - let amount = |addition: u128| { - payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128 + addition + let service_fee_owed_to_serv_node_1_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 123_456; + let service_fee_owed_to_serv_node_2_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 456_789; + let service_fee_owed_to_serv_node_3_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 789_012; + let cons_node_initial_service_fee_balance_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei) * 4; + let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor + - (service_fee_owed_to_serv_node_1_minor + + service_fee_owed_to_serv_node_2_minor + + service_fee_owed_to_serv_node_3_minor); + let test_global_config = TestInputsOutputsConfig { + ui_ports_opt: None, + cons_node_initial_transaction_fee_balance_minor_opt: None, + cons_node_initial_service_fee_balance_minor, + service_fee_owed_to_serv_node_1_minor, + service_fee_owed_to_serv_node_2_minor, + service_fee_owed_to_serv_node_3_minor, + payment_thresholds_all_nodes: payment_thresholds, + exp_final_cons_node_transaction_fee_balance_minor: 99_999_842_470_000_000_000, + exp_final_cons_node_service_fee_balance_minor, + exp_final_service_fee_balance_serv_node_1_minor: service_fee_owed_to_serv_node_1_minor, + exp_final_service_fee_balance_serv_node_2_minor: service_fee_owed_to_serv_node_2_minor, + exp_final_service_fee_balance_serv_node_3_minor: service_fee_owed_to_serv_node_3_minor, }; - let payment_to_serving_node_1 = amount(123); - let payment_to_serving_node_2 = amount(456); - let payment_to_serving_node_3 = amount(789); - - let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); - let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); - let consuming_node_connection = DbInitializerReal::default() - .initialize( - Path::new(&consuming_node_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); - open_all_file_permissions(consuming_node_path.clone().into()); - assert_eq!( - format!("{}", &consuming_node_wallet), - "0x7a3cf474962646b18666b5a5be597bb0af013d81" - ); - assert_eq!( - format!("{}", &serving_node_1_wallet), - "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" - ); - assert_eq!( - format!("{}", &serving_node_2_wallet), - "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" - ); - assert_eq!( - format!("{}", &serving_node_3_wallet), - "0xb45a33ef3e3097f34c826369b74141ed268cdb5a" - ); - let now = SystemTime::now(); - consuming_payable_dao - .more_money_payable(now, &serving_node_1_wallet, payment_to_serving_node_1) - .unwrap(); - consuming_payable_dao - .more_money_payable(now, &serving_node_2_wallet, payment_to_serving_node_2) - .unwrap(); - consuming_payable_dao - .more_money_payable(now, &serving_node_3_wallet, payment_to_serving_node_3) - .unwrap(); - - let (serving_node_1_name, serving_node_1_index) = - cluster.prepare_real_node(&serving_node_1_config); - let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); - let serving_node_1_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_1_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); - serving_node_1_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - payment_to_serving_node_1, - ) - .unwrap(); - open_all_file_permissions(serving_node_1_path.clone().into()); - - let (serving_node_2_name, serving_node_2_index) = - cluster.prepare_real_node(&serving_node_2_config); - let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); - let serving_node_2_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_2_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); - serving_node_2_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - payment_to_serving_node_2, - ) - .unwrap(); - open_all_file_permissions(serving_node_2_path.clone().into()); - - let (serving_node_3_name, serving_node_3_index) = - cluster.prepare_real_node(&serving_node_3_config); - let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); - let serving_node_3_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_3_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); - serving_node_3_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - payment_to_serving_node_3, - ) - .unwrap(); - open_all_file_permissions(serving_node_3_path.clone().into()); - - expire_payables(consuming_node_path.into()); - expire_receivables(serving_node_1_path.into()); - expire_receivables(serving_node_2_path.into()); - expire_receivables(serving_node_3_path.into()); - - // Note: ganache defaults the balance of each created account to 100 ETH - - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - "100000000000000000000", - &consuming_node_initial_service_fee_balance.to_string(), - ); - - assert_balances( - &serving_node_1_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - assert_balances( - &serving_node_2_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - assert_balances( - &serving_node_3_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - let real_consuming_node = - cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(real_consuming_node.node_reference()) - .build(), - ); - } - - let now = Instant::now(); - while !consuming_payable_dao.non_pending_payables().is_empty() - && now.elapsed() < Duration::from_secs(10) - { - thread::sleep(Duration::from_millis(400)); - } - - let expected_final_token_balance_consuming_node = consuming_node_initial_service_fee_balance - - (payment_to_serving_node_1 + payment_to_serving_node_2 + payment_to_serving_node_3); - - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - "99999842482000000000", - &expected_final_token_balance_consuming_node.to_string(), - ); - - assert_balances( - &serving_node_1_wallet, - &blockchain_interface, - "100000000000000000000", - &payment_to_serving_node_1.to_string(), - ); - - assert_balances( - &serving_node_2_wallet, - &blockchain_interface, - "100000000000000000000", - &payment_to_serving_node_2.to_string(), - ); - assert_balances( - &serving_node_3_wallet, - &blockchain_interface, - "100000000000000000000", - payment_to_serving_node_3.to_string().as_str(), - ); - - let serving_node_1 = cluster.start_named_real_node( - &serving_node_1_name, - serving_node_1_index, - serving_node_1_config, - ); - let serving_node_2 = cluster.start_named_real_node( - &serving_node_2_name, - serving_node_2_index, - serving_node_2_config, - ); - let serving_node_3 = cluster.start_named_real_node( - &serving_node_3_name, - serving_node_3_index, - serving_node_3_config, - ); - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(serving_node_1.node_reference()) - .neighbor(serving_node_2.node_reference()) - .neighbor(serving_node_3.node_reference()) - .build(), - ); - } - - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false + let stimulate_payments = |cluster: &mut MASQNodeCluster, real_consuming_node: &MASQRealNode| { + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(real_consuming_node.node_reference()) + .build(), + ); } - }); -} - -#[test] -fn payments_were_adjusted_due_to_insufficient_balance() { - let mut cluster = match MASQNodeCluster::start() { - Ok(cluster) => cluster, - Err(e) => panic!("{}", e), - }; - let blockchain_server = BlockchainServer { - name: "ganache-cli", - }; - blockchain_server.start(); - blockchain_server.wait_until_ready(); - let url = blockchain_server.url().to_string(); - let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); - let web3 = Web3::new(http.clone()); - let deriv_path = derivation_path(0, 0); - let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet(&seed, &deriv_path); - let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - assert_eq!( - contract_addr, - cluster.chain.rec().contract, - "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", - contract_addr - ); - let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); - let payment_thresholds = PaymentThresholds { - threshold_interval_sec: 2_592_000, - debt_threshold_gwei: 1_000_000_000, - payment_grace_period_sec: 86_400, - maturity_threshold_sec: 86_400, - permanent_debt_allowed_gwei: 10_000_000, - unban_below_gwei: 10_000_000, - }; - - let consuming_node_ui_port = find_free_port(); - let serving_node_1_ui_port = find_free_port(); - let serving_node_2_ui_port = find_free_port(); - let serving_node_3_ui_port = find_free_port(); - - let (consuming_config, consuming_node_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - deriv_path, - Some(consuming_node_ui_port), - ); - - //TODO figure out how much it should be - let consuming_node_initial_service_fee_balance = - gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); - - transfer_tokens_to_address( - contract_addr, - &contract_owner_wallet, - &consuming_node_wallet, - consuming_node_initial_service_fee_balance, - 1, - &web3, - cluster.chain, - ); - - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - "100000000000000000000", // Default at ganache - &consuming_node_initial_service_fee_balance.to_string(), - ); - - let (serving_node_1_config, serving_node_1_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 1), - Some(serving_node_1_ui_port), - ); - let (serving_node_2_config, serving_node_2_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 2), - Some(serving_node_2_ui_port), - ); - let (serving_node_3_config, serving_node_3_wallet) = build_config( - &blockchain_server, - &seed, - payment_thresholds, - derivation_path(0, 3), - Some(serving_node_3_ui_port), - ); - - let amount = |addition: u128| -> u128 { - todo!("hmmm"); }; - let owed_to_serving_node_1 = amount(123); - let owed_to_serving_node_2 = amount(456); - let owed_to_serving_node_3 = amount(789); - - let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); - let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); - let consuming_node_connection = DbInitializerReal::default() - .initialize( - Path::new(&consuming_node_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); - open_all_file_permissions(consuming_node_path.clone().into()); - - assert_eq!( - format!("{}", &contract_owner_wallet), - "0x5a4d5df91d0124dec73dbd112f82d6077ccab47d" - ); - assert_eq!( - format!("{}", &serving_node_1_wallet), - "0x7a3cf474962646b18666b5a5be597bb0af013d81" - ); - assert_eq!( - format!("{}", &serving_node_2_wallet), - "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" - ); - assert_eq!( - format!("{}", &serving_node_3_wallet), - "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" - ); - - let (serving_node_1_name, serving_node_1_index) = - cluster.prepare_real_node(&serving_node_1_config); - let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); - let serving_node_1_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_1_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); - serving_node_1_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) - .unwrap(); - open_all_file_permissions(serving_node_1_path.clone().into()); - - let (serving_node_2_name, serving_node_2_index) = - cluster.prepare_real_node(&serving_node_2_config); - let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); - let serving_node_2_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_2_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); - serving_node_2_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) - .unwrap(); - open_all_file_permissions(serving_node_2_path.clone().into()); - - let (serving_node_3_name, serving_node_3_index) = - cluster.prepare_real_node(&serving_node_3_config); - let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); - let serving_node_3_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_3_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); - serving_node_3_receivable_dao - .more_money_receivable(SystemTime::now(), &consuming_node_wallet, amount) - .unwrap(); - open_all_file_permissions(serving_node_3_path.clone().into()); - - expire_payables(consuming_node_path.into()); - expire_receivables(serving_node_1_path.into()); - expire_receivables(serving_node_2_path.into()); - expire_receivables(serving_node_3_path.into()); - - let expected_deployed_token_initial_supply = 472_000_000_000_000_000_000_000_000_u128; - - assert_balances( - &contract_owner_wallet, - &blockchain_interface, - "99998381140000000000", - &expected_deployed_token_initial_supply.to_string(), - ); - - assert_balances( - &serving_node_1_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - assert_balances( - &serving_node_2_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - assert_balances( - &serving_node_3_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - let real_consuming_node = - cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); - let process_scan_request_to_node = - |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { - let ui_client = real_consuming_node.make_ui(ui_port); - ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); - let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); - UiScanResponse::fmb(response).unwrap(); + let start_serving_nodes_and_run_check = + |cluster: &mut MASQNodeCluster, + serving_node_1_attributes: ServingNodeAttributes, + serving_node_2_attributes: ServingNodeAttributes, + serving_node_3_attributes: ServingNodeAttributes, + _test_global_config: &TestInputsOutputsConfig| { + let serving_node_1 = cluster.start_named_real_node( + &serving_node_1_attributes.name, + serving_node_1_attributes.index, + serving_node_1_attributes.config, + ); + let serving_node_2 = cluster.start_named_real_node( + &serving_node_2_attributes.name, + serving_node_2_attributes.index, + serving_node_2_attributes.config, + ); + let serving_node_3 = cluster.start_named_real_node( + &serving_node_3_attributes.name, + serving_node_3_attributes.index, + serving_node_3_attributes.config, + ); + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(serving_node_1.node_reference()) + .neighbor(serving_node_2.node_reference()) + .neighbor(serving_node_3.node_reference()) + .build(), + ); + } + + (serving_node_1, serving_node_2, serving_node_3) }; - // Commanding the consuming Node to check its payables - process_scan_request_to_node( - &real_consuming_node, - consuming_node_ui_port, - ScanType::Payables, - 5555, - ); - - let now = Instant::now(); - while !consuming_node_payable_dao - .payable_dao - .non_pending_payables() - .is_empty() - && now.elapsed() < Duration::from_secs(10) - { - thread::sleep(Duration::from_millis(400)); - } - - let expected_final_token_balance_consuming_node = - expected_deployed_token_initial_supply - 3 * amount; - - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - "99951655600000000000", - &expected_final_token_balance_consuming_node.to_string(), - ); - - assert_balances( - &serving_node_1_wallet, - &blockchain_interface, - "100000000000000000000", - "22222222222", - ); - - assert_balances( - &serving_node_2_wallet, - &blockchain_interface, - "100000000000000000000", - "0", - ); - - assert_balances( - &serving_node_3_wallet, - &blockchain_interface, - "100000000000000000000", - "11111111111", - ); - - let serving_node_1 = cluster.start_named_real_node( - &serving_node_1_name, - serving_node_1_index, - serving_node_1_config, - ); - process_scan_request_to_node( - &serving_node_1, - serving_node_1_ui_port, - ScanType::Receivables, - 2222, - ); - let serving_node_2 = cluster.start_named_real_node( - &serving_node_2_name, - serving_node_2_index, - serving_node_2_config, - ); - process_scan_request_to_node( - &serving_node_2, - serving_node_2_ui_port, - ScanType::Receivables, - 3333, - ); - let serving_node_3 = cluster.start_named_real_node( - &serving_node_3_name, - serving_node_3_index, - serving_node_3_config, - ); - process_scan_request_to_node( - &serving_node_3, - serving_node_3_ui_port, - ScanType::Receivables, - 4444, - ); + test_body( + test_global_config, + stimulate_payments, + start_serving_nodes_and_run_check, + ); + // + // let mut cluster = match MASQNodeCluster::start() { + // Ok(cluster) => cluster, + // Err(e) => panic!("{}", e), + // }; + // let blockchain_server = BlockchainServer { + // name: "ganache-cli", + // }; + // blockchain_server.start(); + // blockchain_server.wait_until_ready(); + // let url = blockchain_server.url().to_string(); + // let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); + // let web3 = Web3::new(http.clone()); + // let seed = make_seed(); + // let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); + // let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); + // assert_eq!( + // contract_addr, + // cluster.chain.rec().contract, + // "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", + // contract_addr + // ); + // let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); + // let payment_thresholds = PaymentThresholds { + // threshold_interval_sec: 2_500_000, + // debt_threshold_gwei: 1_000_000_000, + // payment_grace_period_sec: 85_000, + // maturity_threshold_sec: 85_000, + // permanent_debt_allowed_gwei: 10_000_000, + // unban_below_gwei: 10_000_000, + // }; + // let (consuming_config, consuming_node_wallet) = build_config( + // &blockchain_server, + // &seed, + // payment_thresholds, + // derivation_path(0, 1), + // None, + // ); + // + // let consuming_node_initial_service_fee_balance = + // gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); + // + // transfer_tokens_to_address( + // contract_addr, + // &contract_owner_wallet, + // &consuming_node_wallet, + // consuming_node_initial_service_fee_balance, + // 1, + // &web3, + // cluster.chain, + // ); + // + // assert_balances( + // &consuming_node_wallet, + // &blockchain_interface, + // "100000000000000000000", // Default at ganache + // &consuming_node_initial_service_fee_balance.to_string(), + // ); + // + // let (serving_node_1_config, serving_node_1_wallet) = build_config( + // &blockchain_server, + // &seed, + // payment_thresholds, + // derivation_path(0, 2), + // None, + // ); + // let (serving_node_2_config, serving_node_2_wallet) = build_config( + // &blockchain_server, + // &seed, + // payment_thresholds, + // derivation_path(0, 3), + // None, + // ); + // let (serving_node_3_config, serving_node_3_wallet) = build_config( + // &blockchain_server, + // &seed, + // payment_thresholds, + // derivation_path(0, 4), + // None, + // ); + // + // let amount = |addition: u128| { + // payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128 + addition + // }; + // let payment_to_serving_node_1 = amount(123); + // let payment_to_serving_node_2 = amount(456); + // let payment_to_serving_node_3 = amount(789); + // + // let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); + // let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); + // let consuming_node_connection = DbInitializerReal::default() + // .initialize( + // Path::new(&consuming_node_path), + // make_init_config(cluster.chain), + // ) + // .unwrap(); + // let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); + // open_all_file_permissions(consuming_node_path.clone().into()); + // assert_eq!( + // format!("{}", &consuming_node_wallet), + // "0x7a3cf474962646b18666b5a5be597bb0af013d81" + // ); + // assert_eq!( + // format!("{}", &serving_node_1_wallet), + // "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" + // ); + // assert_eq!( + // format!("{}", &serving_node_2_wallet), + // "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" + // ); + // assert_eq!( + // format!("{}", &serving_node_3_wallet), + // "0xb45a33ef3e3097f34c826369b74141ed268cdb5a" + // ); + // let now = SystemTime::now(); + // consuming_payable_dao + // .more_money_payable(now, &serving_node_1_wallet, payment_to_serving_node_1) + // .unwrap(); + // consuming_payable_dao + // .more_money_payable(now, &serving_node_2_wallet, payment_to_serving_node_2) + // .unwrap(); + // consuming_payable_dao + // .more_money_payable(now, &serving_node_3_wallet, payment_to_serving_node_3) + // .unwrap(); + // + // let (serving_node_1_name, serving_node_1_index) = + // cluster.prepare_real_node(&serving_node_1_config); + // let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); + // let serving_node_1_connection = DbInitializerReal::default() + // .initialize( + // Path::new(&serving_node_1_path), + // make_init_config(cluster.chain), + // ) + // .unwrap(); + // let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); + // serving_node_1_receivable_dao + // .more_money_receivable( + // SystemTime::now(), + // &consuming_node_wallet, + // payment_to_serving_node_1, + // ) + // .unwrap(); + // open_all_file_permissions(serving_node_1_path.clone().into()); + // + // let (serving_node_2_name, serving_node_2_index) = + // cluster.prepare_real_node(&serving_node_2_config); + // let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); + // let serving_node_2_connection = DbInitializerReal::default() + // .initialize( + // Path::new(&serving_node_2_path), + // make_init_config(cluster.chain), + // ) + // .unwrap(); + // let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); + // serving_node_2_receivable_dao + // .more_money_receivable( + // SystemTime::now(), + // &consuming_node_wallet, + // payment_to_serving_node_2, + // ) + // .unwrap(); + // open_all_file_permissions(serving_node_2_path.clone().into()); + // + // let (serving_node_3_name, serving_node_3_index) = + // cluster.prepare_real_node(&serving_node_3_config); + // let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); + // let serving_node_3_connection = DbInitializerReal::default() + // .initialize( + // Path::new(&serving_node_3_path), + // make_init_config(cluster.chain), + // ) + // .unwrap(); + // let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); + // serving_node_3_receivable_dao + // .more_money_receivable( + // SystemTime::now(), + // &consuming_node_wallet, + // payment_to_serving_node_3, + // ) + // .unwrap(); + // open_all_file_permissions(serving_node_3_path.clone().into()); + // + // expire_payables(consuming_node_path.into()); + // expire_receivables(serving_node_1_path.into()); + // expire_receivables(serving_node_2_path.into()); + // expire_receivables(serving_node_3_path.into()); + // + // // Note: ganache defaults the balance of each created account to 100 ETH + // + // assert_balances( + // &consuming_node_wallet, + // &blockchain_interface, + // "100000000000000000000", + // &consuming_node_initial_service_fee_balance.to_string(), + // ); + // + // assert_balances( + // &serving_node_1_wallet, + // &blockchain_interface, + // "100000000000000000000", + // "0", + // ); + // + // assert_balances( + // &serving_node_2_wallet, + // &blockchain_interface, + // "100000000000000000000", + // "0", + // ); + // + // assert_balances( + // &serving_node_3_wallet, + // &blockchain_interface, + // "100000000000000000000", + // "0", + // ); + // + // let real_consuming_node = + // cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); + // for _ in 0..6 { + // cluster.start_real_node( + // NodeStartupConfigBuilder::standard() + // .chain(Chain::Dev) + // .neighbor(real_consuming_node.node_reference()) + // .build(), + // ); + // } + // + // let now = Instant::now(); + // while !consuming_payable_dao.non_pending_payables().is_empty() + // && now.elapsed() < Duration::from_secs(10) + // { + // thread::sleep(Duration::from_millis(400)); + // } + // + // let expected_final_token_balance_consuming_node = consuming_node_initial_service_fee_balance + // - (payment_to_serving_node_1 + payment_to_serving_node_2 + payment_to_serving_node_3); + // + // assert_balances( + // &consuming_node_wallet, + // &blockchain_interface, + // "99999842482000000000", + // &expected_final_token_balance_consuming_node.to_string(), + // ); + // + // assert_balances( + // &serving_node_1_wallet, + // &blockchain_interface, + // "100000000000000000000", + // &payment_to_serving_node_1.to_string(), + // ); + // + // assert_balances( + // &serving_node_2_wallet, + // &blockchain_interface, + // "100000000000000000000", + // &payment_to_serving_node_2.to_string(), + // ); + // + // assert_balances( + // &serving_node_3_wallet, + // &blockchain_interface, + // "100000000000000000000", + // payment_to_serving_node_3.to_string().as_str(), + // ); + // + // let serving_node_1 = cluster.start_named_real_node( + // &serving_node_1_name, + // serving_node_1_index, + // serving_node_1_config, + // ); + // let serving_node_2 = cluster.start_named_real_node( + // &serving_node_2_name, + // serving_node_2_index, + // serving_node_2_config, + // ); + // let serving_node_3 = cluster.start_named_real_node( + // &serving_node_3_name, + // serving_node_3_index, + // serving_node_3_config, + // ); + // for _ in 0..6 { + // cluster.start_real_node( + // NodeStartupConfigBuilder::standard() + // .chain(Chain::Dev) + // .neighbor(serving_node_1.node_reference()) + // .neighbor(serving_node_2.node_reference()) + // .neighbor(serving_node_3.node_reference()) + // .build(), + // ); + // } + // + // test_utils::wait_for(Some(1000), Some(15000), || { + // if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { + // status.balance_wei == 0 + // } else { + // false + // } + // }); + // test_utils::wait_for(Some(1000), Some(15000), || { + // if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { + // status.balance_wei == 0 + // } else { + // false + // } + // }); + // test_utils::wait_for(Some(1000), Some(15000), || { + // if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { + // status.balance_wei == 0 + // } else { + // false + // } + // }); +} - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == 0 - } else { - false - } - }); +#[test] +fn payments_were_adjusted_due_to_insufficient_balance() { + // + // let consuming_node_ui_port = find_free_port(); + // let serving_node_1_ui_port = find_free_port(); + // let serving_node_2_ui_port = find_free_port(); + // let serving_node_3_ui_port = find_free_port(); + // + // + // + // + // + // + // transfer_tokens_to_address( + // contract_addr, + // &contract_owner_wallet, + // &consuming_node_wallet, + // consuming_node_initial_service_fee_balance, + // 1, + // &web3, + // cluster.chain, + // ); + // + // assert_balances( + // &consuming_node_wallet, + // &blockchain_interface, + // "100000000000000000000", // Default at ganache + // &consuming_node_initial_service_fee_balance.to_string(), + // ); + // + // + // + // + // + // let amount = |addition: u128| -> u128 { + // todo!("hmmm"); + // }; + // let owed_to_serving_node_1 = amount(123); + // let owed_to_serving_node_2 = amount(456); + // let owed_to_serving_node_3 = amount(789); + // + // + // + // + // + // let process_scan_request_to_node = + // |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { + // let ui_client = real_consuming_node.make_ui(ui_port); + // ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); + // let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); + // UiScanResponse::fmb(response).unwrap(); + // }; + // + // // Commanding the consuming Node to check its payables + // process_scan_request_to_node( + // &real_consuming_node, + // consuming_node_ui_port, + // ScanType::Payables, + // 5555, + // ); + // + // + // + // + // + // let serving_node_1 = cluster.start_named_real_node( + // &serving_node_1_name, + // serving_node_1_index, + // serving_node_1_config, + // ); + // process_scan_request_to_node( + // &serving_node_1, + // serving_node_1_ui_port, + // ScanType::Receivables, + // 2222, + // ); + // let serving_node_2 = cluster.start_named_real_node( + // &serving_node_2_name, + // serving_node_2_index, + // serving_node_2_config, + // ); + // process_scan_request_to_node( + // &serving_node_2, + // serving_node_2_ui_port, + // ScanType::Receivables, + // 3333, + // ); + // let serving_node_3 = cluster.start_named_real_node( + // &serving_node_3_name, + // serving_node_3_index, + // serving_node_3_config, + // ); + // process_scan_request_to_node( + // &serving_node_3, + // serving_node_3_ui_port, + // ScanType::Receivables, + // 4444, + // ); } fn make_init_config(chain: Chain) -> DbInitializationConfig { @@ -700,16 +561,16 @@ fn make_init_config(chain: Chain) -> DbInitializationConfig { fn assert_balances( wallet: &Wallet, blockchain_interface: &BlockchainInterfaceWeb3, - expected_eth_balance: &str, - expected_token_balance: &str, + expected_eth_balance: u128, + expected_token_balance: u128, ) { let eth_balance = blockchain_interface .lower_interface() .get_transaction_fee_balance(&wallet) .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); assert_eq!( - format!("{}", eth_balance), - String::from(expected_eth_balance), + eth_balance, + web3::types::U256::from(expected_eth_balance), "Actual EthBalance {} doesn't much with expected {}", eth_balance, expected_eth_balance @@ -720,7 +581,7 @@ fn assert_balances( .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); assert_eq!( token_balance, - web3::types::U256::from_dec_str(expected_token_balance).unwrap(), + web3::types::U256::from(expected_token_balance), "Actual TokenBalance {} doesn't match with expected {}", token_balance, expected_token_balance @@ -880,13 +741,22 @@ fn expire_receivables(path: PathBuf) { config_stmt.execute([]).unwrap(); } +const DEFAULT_GANACHE_TRANSACTION_FEE_BALANCE_WEI: u128 = 100_000_000_000_000_000_000; + struct TestInputsOutputsConfig { ui_ports_opt: Option, - cons_node_initial_transaction_fee_balance_minor: u128, + // Gets ganache default 100 ETH if None. + // Uses an arg where the private key of the wallet and the amount in + // wei must be specified (in these tests the key is hardcoded and + // corresponds to the path m/44'/60'/0'/0/0/1 that belongs to + // the consuming Node). + // Specify number of wei this account should possess at its initialisation + cons_node_initial_transaction_fee_balance_minor_opt: Option, cons_node_initial_service_fee_balance_minor: u128, service_fee_owed_to_serv_node_1_minor: u128, service_fee_owed_to_serv_node_2_minor: u128, service_fee_owed_to_serv_node_3_minor: u128, + payment_thresholds_all_nodes: PaymentThresholds, exp_final_cons_node_transaction_fee_balance_minor: u128, exp_final_cons_node_service_fee_balance_minor: u128, @@ -904,7 +774,7 @@ enum UiPorts { impl TestInputsOutputsConfig { fn port(&self, requested: UiPorts) -> Option { - self.ui_ports_opt.map(|ports| match requested { + self.ui_ports_opt.as_ref().map(|ports| match requested { UiPorts::ConsNode => ports.consuming_node, UiPorts::ServNode1 => ports.serving_node_1, UiPorts::ServNode2 => ports.serving_node_2, @@ -920,10 +790,10 @@ struct Ports { serving_node_3: u16, } -struct ServingNodeAttributes<'a> { +struct ServingNodeAttributes { name: String, index: usize, - config: &'a NodeStartupConfig, + config: NodeStartupConfig, } fn test_body( @@ -931,10 +801,10 @@ fn test_body Date: Mon, 27 Nov 2023 23:31:55 +0800 Subject: [PATCH 102/250] GH-711: got further, still need to work away on the second one; test config unset and ganache server missing parameter --- .../tests/verify_bill_payment.rs | 469 +++++++++++------- 1 file changed, 285 insertions(+), 184 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 0ab24db80..83157708c 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -1,8 +1,12 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use bip39::{Language, Mnemonic, Seed}; use futures::Future; +use itertools::Either; use masq_lib::blockchains::chains::Chain; -use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; +use masq_lib::messages::FromMessageBody; +use masq_lib::messages::ToMessageBody; +use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; +use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; @@ -15,6 +19,7 @@ use multinode_integration_tests_lib::utils::{ }; use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; +use node_lib::accountant::db_access_objects::utils::to_time_t; use node_lib::accountant::gwei_to_wei; use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ @@ -28,6 +33,7 @@ use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; +use rusqlite::{ToSql, Transaction}; use rustc_hex::{FromHex, ToHex}; use std::convert::TryFrom; use std::path::{Path, PathBuf}; @@ -57,43 +63,48 @@ fn verify_bill_payment() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; - let service_fee_owed_to_serv_node_1_minor = + let owed_to_serving_node_1_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 123_456; - let service_fee_owed_to_serv_node_2_minor = + let owed_to_serving_node_2_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 456_789; - let service_fee_owed_to_serv_node_3_minor = + let owed_to_serving_node_3_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 789_012; let cons_node_initial_service_fee_balance_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) * 4; let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor - - (service_fee_owed_to_serv_node_1_minor - + service_fee_owed_to_serv_node_2_minor - + service_fee_owed_to_serv_node_3_minor); + - (owed_to_serving_node_1_minor + + owed_to_serving_node_2_minor + + owed_to_serving_node_3_minor); let test_global_config = TestInputsOutputsConfig { ui_ports_opt: None, cons_node_initial_transaction_fee_balance_minor_opt: None, cons_node_initial_service_fee_balance_minor, - service_fee_owed_to_serv_node_1_minor, - service_fee_owed_to_serv_node_2_minor, - service_fee_owed_to_serv_node_3_minor, + debts_config: Either::Left(SimpleSimulatedDebts { + owed_to_serving_node_1_minor, + owed_to_serving_node_2_minor, + owed_to_serving_node_3_minor, + }), payment_thresholds_all_nodes: payment_thresholds, exp_final_cons_node_transaction_fee_balance_minor: 99_999_842_470_000_000_000, exp_final_cons_node_service_fee_balance_minor, - exp_final_service_fee_balance_serv_node_1_minor: service_fee_owed_to_serv_node_1_minor, - exp_final_service_fee_balance_serv_node_2_minor: service_fee_owed_to_serv_node_2_minor, - exp_final_service_fee_balance_serv_node_3_minor: service_fee_owed_to_serv_node_3_minor, + exp_final_service_fee_balance_serv_node_1_minor: owed_to_serving_node_1_minor, + exp_final_service_fee_balance_serv_node_2_minor: owed_to_serving_node_2_minor, + exp_final_service_fee_balance_serv_node_3_minor: owed_to_serving_node_3_minor, }; - let stimulate_payments = |cluster: &mut MASQNodeCluster, real_consuming_node: &MASQRealNode| { - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(real_consuming_node.node_reference()) - .build(), - ); - } - }; + let stimulate_payments = + |cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + _global_test_config: &TestInputsOutputsConfig| { + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(real_consuming_node.node_reference()) + .build(), + ); + } + }; let start_serving_nodes_and_run_check = |cluster: &mut MASQNodeCluster, @@ -452,102 +463,122 @@ fn verify_bill_payment() { #[test] fn payments_were_adjusted_due_to_insufficient_balance() { - // - // let consuming_node_ui_port = find_free_port(); - // let serving_node_1_ui_port = find_free_port(); - // let serving_node_2_ui_port = find_free_port(); - // let serving_node_3_ui_port = find_free_port(); - // - // - // - // - // - // - // transfer_tokens_to_address( - // contract_addr, - // &contract_owner_wallet, - // &consuming_node_wallet, - // consuming_node_initial_service_fee_balance, - // 1, - // &web3, - // cluster.chain, - // ); - // - // assert_balances( - // &consuming_node_wallet, - // &blockchain_interface, - // "100000000000000000000", // Default at ganache - // &consuming_node_initial_service_fee_balance.to_string(), - // ); - // - // - // - // - // - // let amount = |addition: u128| -> u128 { - // todo!("hmmm"); - // }; - // let owed_to_serving_node_1 = amount(123); - // let owed_to_serving_node_2 = amount(456); - // let owed_to_serving_node_3 = amount(789); - // - // - // - // - // - // let process_scan_request_to_node = - // |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { - // let ui_client = real_consuming_node.make_ui(ui_port); - // ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); - // let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); - // UiScanResponse::fmb(response).unwrap(); - // }; - // - // // Commanding the consuming Node to check its payables - // process_scan_request_to_node( - // &real_consuming_node, - // consuming_node_ui_port, - // ScanType::Payables, - // 5555, - // ); - // - // - // - // - // - // let serving_node_1 = cluster.start_named_real_node( - // &serving_node_1_name, - // serving_node_1_index, - // serving_node_1_config, - // ); - // process_scan_request_to_node( - // &serving_node_1, - // serving_node_1_ui_port, - // ScanType::Receivables, - // 2222, - // ); - // let serving_node_2 = cluster.start_named_real_node( - // &serving_node_2_name, - // serving_node_2_index, - // serving_node_2_config, - // ); - // process_scan_request_to_node( - // &serving_node_2, - // serving_node_2_ui_port, - // ScanType::Receivables, - // 3333, - // ); - // let serving_node_3 = cluster.start_named_real_node( - // &serving_node_3_name, - // serving_node_3_index, - // serving_node_3_config, - // ); - // process_scan_request_to_node( - // &serving_node_3, - // serving_node_3_ui_port, - // ScanType::Receivables, - // 4444, - // ); + let consuming_node_ui_port = find_free_port(); + let serving_node_1_ui_port = find_free_port(); + let serving_node_2_ui_port = find_free_port(); + let serving_node_3_ui_port = find_free_port(); + let payment_thresholds = PaymentThresholds { + threshold_interval_sec: 2_500_000, + debt_threshold_gwei: 1_000_000_000, + payment_grace_period_sec: 85_000, + maturity_threshold_sec: 85_000, + permanent_debt_allowed_gwei: 10_000_000, + unban_below_gwei: 10_000_000, + }; + let owed_to_serv_node_1_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 10_000); + let owed_to_serv_node_2_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 20_000); + let owed_to_serv_node_3_minor = + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 30_000); + // Assuming all Nodes rely on the same set of payment thresholds + let cons_node_initial_service_fee_balance_minor = + (owed_to_serv_node_2_minor + owed_to_serv_node_3_minor) - 150_000; + let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor + - (owed_to_serv_node_1_minor + owed_to_serv_node_2_minor + owed_to_serv_node_3_minor); + let test_global_config = TestInputsOutputsConfig { + ui_ports_opt: Some(Ports { + consuming_node: consuming_node_ui_port, + serving_node_1: serving_node_1_ui_port, + serving_node_2: serving_node_2_ui_port, + serving_node_3: serving_node_3_ui_port, + }), + // Shouldn't be enough for three payments therefore the least significant will be eliminated + cons_node_initial_transaction_fee_balance_minor_opt: Some(3_000_000_000_000_000), + cons_node_initial_service_fee_balance_minor, + debts_config: Either::Right(FullySpecifiedSimulatedDebts { + // This account will be the least significant and be eliminated for the reasons + // said above + owed_to_serving_node_1: AccountedDebt { + balance_minor: owed_to_serv_node_1_minor, + age_s: payment_thresholds.maturity_threshold_sec + 1000, + }, + // This account has the middle amount in the balance but + // it is stressed by the age, which will cause this one will + // evaluate with the highest significance + owed_to_serving_node_2: AccountedDebt { + balance_minor: owed_to_serv_node_2_minor, + age_s: payment_thresholds.maturity_threshold_sec + 100_000, + }, + // This balance is biggest but in the adjusted payment it will + // be reduced on the account of the second serving node, + // gaining the biggest portion from the available means for fees + owed_to_serving_node_3: AccountedDebt { + balance_minor: owed_to_serv_node_3_minor, + age_s: payment_thresholds.maturity_threshold_sec + 30_000, + }, + }), + payment_thresholds_all_nodes: payment_thresholds, + exp_final_cons_node_transaction_fee_balance_minor: 99_999_842_470_000_000_000, + exp_final_cons_node_service_fee_balance_minor, + exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, + exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor, + exp_final_service_fee_balance_serv_node_3_minor: owed_to_serv_node_3_minor, + }; + + let process_scan_request_to_node = + |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { + let ui_client = real_node.make_ui(ui_port); + ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); + let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); + UiScanResponse::fmb(response).unwrap(); + }; + + let stimulate_payments = + |_cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + _global_test_config: &TestInputsOutputsConfig| { + process_scan_request_to_node( + &real_consuming_node, + consuming_node_ui_port, + ScanType::Payables, + 5555, + ) + }; + + let start_serving_nodes_and_run_check = |cluster: &mut MASQNodeCluster, + serving_node_1_attributes: ServingNodeAttributes, + serving_node_2_attributes: ServingNodeAttributes, + serving_node_3_attributes: ServingNodeAttributes, + test_global_config: &TestInputsOutputsConfig| + -> (MASQRealNode, MASQRealNode, MASQRealNode) { + let ports = test_global_config.ui_ports_opt.as_ref().unwrap(); + let mut vec: Vec = vec![ + (serving_node_1_attributes, ports.serving_node_1, 1111), + (serving_node_2_attributes, ports.serving_node_2, 2222), + (serving_node_3_attributes, ports.serving_node_3, 3333), + ] + .into_iter() + .map(|(serving_node_attributes, ui_port, context_id)| { + let serving_node = cluster.start_named_real_node( + &serving_node_attributes.name, + serving_node_attributes.index, + serving_node_attributes.config, + ); + + process_scan_request_to_node(&serving_node, ui_port, ScanType::Receivables, context_id); + + serving_node + }) + .collect(); + (vec.remove(0), vec.remove(0), vec.remove(0)) + }; + + test_body( + test_global_config, + stimulate_payments, + start_serving_nodes_and_run_check, + ); } fn make_init_config(chain: Chain) -> DbInitializationConfig { @@ -711,14 +742,50 @@ fn build_config( (config, node_wallet) } -fn expire_payables(path: PathBuf) { +fn expire_payables( + path: PathBuf, + debts_config: &Either, + now: SystemTime, + serving_node_1_wallet: &Wallet, + serving_node_2_wallet: &Wallet, + serving_node_3_wallet: &Wallet, +) { let conn = DbInitializerReal::default() .initialize(&path, DbInitializationConfig::panic_on_migration()) .unwrap(); - let mut statement = conn - .prepare("update payable set last_paid_timestamp = 0 where pending_payable_rowid is null") - .unwrap(); - statement.execute([]).unwrap(); + match debts_config { + Either::Left(_) => { + let _ = conn + .prepare( + "update payable set last_paid_timestamp = 0 where pending_payable_rowid is null", + ) + .unwrap() + .execute([]) + .unwrap(); + } + Either::Right(fully_specified_config) => vec![ + ( + serving_node_1_wallet, + fully_specified_config.owed_to_serving_node_1.age_s, + ), + ( + serving_node_2_wallet, + fully_specified_config.owed_to_serving_node_2.age_s, + ), + ( + serving_node_3_wallet, + fully_specified_config.owed_to_serving_node_3.age_s, + ), + ] + .iter() + .for_each(|(wallet, age_s)| { + let time_t = to_time_t(now.checked_sub(Duration::from_secs(*age_s)).unwrap()); + conn.prepare("update payable set last_paid_timestamp = ? where wallet_address = ?") + .unwrap() + .execute(&[&time_t as &dyn ToSql, &(wallet.to_string())]) + .unwrap(); + }), + } let mut config_stmt = conn .prepare("update config set value = '0' where name = 'start_block'") @@ -753,9 +820,7 @@ struct TestInputsOutputsConfig { // Specify number of wei this account should possess at its initialisation cons_node_initial_transaction_fee_balance_minor_opt: Option, cons_node_initial_service_fee_balance_minor: u128, - service_fee_owed_to_serv_node_1_minor: u128, - service_fee_owed_to_serv_node_2_minor: u128, - service_fee_owed_to_serv_node_3_minor: u128, + debts_config: Either, payment_thresholds_all_nodes: PaymentThresholds, exp_final_cons_node_transaction_fee_balance_minor: u128, @@ -765,22 +830,63 @@ struct TestInputsOutputsConfig { exp_final_service_fee_balance_serv_node_3_minor: u128, } -enum UiPorts { +enum NodeByRole { ConsNode, ServNode1, ServNode2, ServNode3, } +struct SimpleSimulatedDebts { + owed_to_serving_node_1_minor: u128, + owed_to_serving_node_2_minor: u128, + owed_to_serving_node_3_minor: u128, +} + +struct FullySpecifiedSimulatedDebts { + owed_to_serving_node_1: AccountedDebt, + owed_to_serving_node_2: AccountedDebt, + owed_to_serving_node_3: AccountedDebt, +} + +struct AccountedDebt { + balance_minor: u128, + age_s: u64, +} + impl TestInputsOutputsConfig { - fn port(&self, requested: UiPorts) -> Option { + fn port(&self, requested: NodeByRole) -> Option { self.ui_ports_opt.as_ref().map(|ports| match requested { - UiPorts::ConsNode => ports.consuming_node, - UiPorts::ServNode1 => ports.serving_node_1, - UiPorts::ServNode2 => ports.serving_node_2, - UiPorts::ServNode3 => ports.serving_node_3, + NodeByRole::ConsNode => ports.consuming_node, + NodeByRole::ServNode1 => ports.serving_node_1, + NodeByRole::ServNode2 => ports.serving_node_2, + NodeByRole::ServNode3 => ports.serving_node_3, }) } + + fn debt_size(&self, requested: NodeByRole) -> u128 { + match self.debts_config.as_ref() { + Either::Left(simple_config) => match requested { + NodeByRole::ServNode1 => simple_config.owed_to_serving_node_1_minor, + NodeByRole::ServNode2 => simple_config.owed_to_serving_node_2_minor, + NodeByRole::ServNode3 => simple_config.owed_to_serving_node_3_minor, + NodeByRole::ConsNode => panic!( + "Version simple: These configs are \ + serve to set owed to the consuming node, while that one should not be here." + ), + }, + Either::Right(fully_specified) => match requested { + NodeByRole::ServNode1 => fully_specified.owed_to_serving_node_1.balance_minor, + NodeByRole::ServNode2 => fully_specified.owed_to_serving_node_2.balance_minor, + NodeByRole::ServNode3 => fully_specified.owed_to_serving_node_3.balance_minor, + NodeByRole::ConsNode => panic!( + "Version fully specified: These configs \ + are serve to set owed to the consuming node, while that one should not \ + be here." + ), + }, + } + } } struct Ports { @@ -798,12 +904,13 @@ struct ServingNodeAttributes { fn test_body( global_config: TestInputsOutputsConfig, - stimulate_payments: StimulateConsumingNodePayments, + stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, start_serving_nodes_and_run_check: StartServingNodesAndLetThemPerformReceivablesCheck, ) where - StimulateConsumingNodePayments: FnOnce(&mut MASQNodeCluster, &MASQRealNode), + StimulateConsumingNodePayments: + FnOnce(&mut MASQNodeCluster, &MASQRealNode, &TestInputsOutputsConfig), StartServingNodesAndLetThemPerformReceivablesCheck: - FnOnce( + FnOnce( &mut MASQNodeCluster, ServingNodeAttributes, ServingNodeAttributes, @@ -833,15 +940,13 @@ fn test_body Date: Wed, 29 Nov 2023 14:31:21 +0800 Subject: [PATCH 103/250] GH-711: ready roughly for final tuning the tests; made the docker scripts smoother with comments --- .../docker/blockchain/Dockerfile | 4 +++ .../docker/blockchain/entrypoint.sh | 27 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/multinode_integration_tests/docker/blockchain/Dockerfile b/multinode_integration_tests/docker/blockchain/Dockerfile index 54a4dfcc9..5c2375576 100644 --- a/multinode_integration_tests/docker/blockchain/Dockerfile +++ b/multinode_integration_tests/docker/blockchain/Dockerfile @@ -1,5 +1,9 @@ # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +# linux/amd64 #FROM trufflesuite/ganache-cli:v6.7.0 + +# linux/arm64 (for MacOs VMs, in case the upper one did not work for you) FROM --platform=linux/arm64 nutrina/ganache-cli:0.3 ADD ./entrypoint.sh /app/ diff --git a/multinode_integration_tests/docker/blockchain/entrypoint.sh b/multinode_integration_tests/docker/blockchain/entrypoint.sh index f80123f9d..fbeeb2199 100755 --- a/multinode_integration_tests/docker/blockchain/entrypoint.sh +++ b/multinode_integration_tests/docker/blockchain/entrypoint.sh @@ -1,5 +1,28 @@ #!/bin/sh -#node /app/ganache-core.docker.cli.js -p 18545 --networkId 2 --verbose --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" +# All ordinal wallets begin with a zero balance, +# except the contract owner wallet whose initial gas balance is supposed to be redistributed from +# to accounts that will need it ( --account ',' ) +# which follows the principle how accounts are fed by service fee amounts from this unique wallet, +# required by the state resulted from the smart contract deployment. -ganache-cli -h 0.0.0.0 -p 18545 --networkId 2 --verbose -m "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ No newline at end of file +# linux/amd64 +###################################### +#node /app/ganache-core.docker.cli.js \ +# -p 18545 \ +# --networkId 2 \ +# --verbose \ +# --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ +# --defaultBalanceEther 0 \ +# --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' + +# linux/arm64 (for MacOs VMs, in case the upper one did not work for you) +###################################### +ganache-cli \ + -h 0.0.0.0 \ + -p 18545 \ + --networkId 2 \ + --verbose \ + -m "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ + --defaultBalanceEther 0 \ + --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' From ad09398daef4e020a2fcbf3092186d67ef1db86f Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:19:36 +0800 Subject: [PATCH 104/250] GH-711: fixes in Node --- multinode_integration_tests/ci/all.sh | 2 +- .../src/masq_real_node.rs | 32 +- .../tests/verify_bill_payment.rs | 476 ++++-------------- node/src/accountant/mod.rs | 11 +- .../payment_adjuster/adjustment_runners.rs | 4 +- .../payment_adjuster/diagnostics.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 68 +-- .../miscellaneous/helper_functions.rs | 6 +- node/src/accountant/payment_adjuster/mod.rs | 125 ++--- .../{verifier.rs => possibility_verifier.rs} | 15 +- .../payable_scanner/agent_null.rs | 36 +- .../payable_scanner/agent_web3.rs | 27 +- .../payable_scanner/blockchain_agent.rs | 6 +- .../payable_scanner/test_utils.rs | 36 +- .../scanners/payable_scan_setup_msgs.rs | 37 -- .../scanners/scan_mid_procedures.rs | 49 -- .../src/accountant/scanners/scanners_utils.rs | 2 +- .../blockchain_interface_web3/mod.rs | 15 +- 18 files changed, 335 insertions(+), 616 deletions(-) rename node/src/accountant/payment_adjuster/{verifier.rs => possibility_verifier.rs} (87%) delete mode 100644 node/src/accountant/scanners/payable_scan_setup_msgs.rs delete mode 100644 node/src/accountant/scanners/scan_mid_procedures.rs diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index 70d5272d2..cce56ab55 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -46,5 +46,5 @@ popd pushd "$CI_DIR/.." export RUSTFLAGS="-D warnings -Anon-snake-case" ci/lint.sh -cargo test verify_bill_payment --release -- --nocapture --test-threads=1 +cargo test payments_were_adjusted_due_to_insufficient_balance --release -- --nocapture --test-threads=1 popd diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 77b82054c..f7a528a6b 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -126,6 +126,7 @@ pub struct NodeStartupConfig { pub consuming_wallet_info: ConsumingWalletInfo, pub rate_pack: RatePack, pub payment_thresholds: PaymentThresholds, + pub gas_price_opt: Option, pub firewall_opt: Option, pub memory_opt: Option, pub fake_public_key_opt: Option, @@ -158,6 +159,7 @@ impl NodeStartupConfig { consuming_wallet_info: ConsumingWalletInfo::None, rate_pack: DEFAULT_RATE_PACK, payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, + gas_price_opt: None, firewall_opt: None, memory_opt: None, fake_public_key_opt: None, @@ -205,6 +207,10 @@ impl NodeStartupConfig { args.push(format!("\"{}\"", self.rate_pack)); args.push("--payment-thresholds".to_string()); args.push(format!("\"{}\"", self.payment_thresholds)); + args.push("--gas-price".to_string()); + if let Some(price) = self.gas_price_opt { + args.push(price.to_string()); + } if let EarningWalletInfo::Address(ref address) = self.earning_wallet_info { args.push("--earning-wallet".to_string()); args.push(address.to_string()); @@ -421,6 +427,7 @@ pub struct NodeStartupConfigBuilder { consuming_wallet_info: ConsumingWalletInfo, rate_pack: RatePack, payment_thresholds: PaymentThresholds, + gas_price_opt: Option, firewall: Option, memory: Option, fake_public_key: Option, @@ -477,6 +484,7 @@ impl NodeStartupConfigBuilder { consuming_wallet_info: ConsumingWalletInfo::None, rate_pack: DEFAULT_RATE_PACK, payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, + gas_price_opt: None, firewall: None, memory: None, fake_public_key: None, @@ -503,6 +511,7 @@ impl NodeStartupConfigBuilder { consuming_wallet_info: config.consuming_wallet_info.clone(), rate_pack: config.rate_pack, payment_thresholds: config.payment_thresholds, + gas_price_opt: config.gas_price_opt, firewall: config.firewall_opt.clone(), memory: config.memory_opt.clone(), fake_public_key: config.fake_public_key_opt.clone(), @@ -596,6 +605,11 @@ impl NodeStartupConfigBuilder { self } + pub fn gas_price(mut self, value: u64) -> Self { + self.gas_price_opt = Some(value); + self + } + // This method is currently disabled. See multinode_integration_tests/docker/Dockerfile. pub fn open_firewall_port(mut self, port: u16) -> Self { if self.firewall.is_none() { @@ -660,6 +674,7 @@ impl NodeStartupConfigBuilder { consuming_wallet_info: self.consuming_wallet_info, rate_pack: self.rate_pack, payment_thresholds: self.payment_thresholds, + gas_price_opt: self.gas_price_opt, firewall_opt: self.firewall, memory_opt: self.memory, fake_public_key_opt: self.fake_public_key, @@ -1384,6 +1399,7 @@ mod tests { threshold_interval_sec: 10, unban_below_gwei: 60, }, + gas_price_opt: Some(151), firewall_opt: Some(Firewall { ports_to_open: vec![HTTP_PORT, TLS_PORT], }), @@ -1468,7 +1484,17 @@ mod tests { permanent_debt_allowed_gwei: 50, unban_below_gwei: 60 } - ) + ); + assert_eq!( + result.rate_pack, + RatePack { + routing_byte_rate: 10, + routing_service_rate: 20, + exit_byte_rate: 30, + exit_service_rate: 40, + } + ); + assert_eq!(result.gas_price_opt, Some(151)) } #[test] @@ -1499,6 +1525,7 @@ mod tests { threshold_interval_sec: 2592000, unban_below_gwei: 490000000, }; + let gas_price = 233; let subject = NodeStartupConfigBuilder::standard() .neighborhood_mode("consume-only") @@ -1506,6 +1533,7 @@ mod tests { .ip(IpAddr::from_str("1.3.5.7").unwrap()) .neighbor(one_neighbor.clone()) .neighbor(another_neighbor.clone()) + .gas_price(gas_price) .rate_pack(rate_pack) .payment_thresholds(payment_thresholds) .consuming_wallet_info(default_consuming_wallet_info()) @@ -1532,6 +1560,8 @@ mod tests { "\"1|90|3|250\"", "--payment-thresholds", "\"10000000000|1200|1200|490000000|2592000|490000000\"", + "--gas-price", + "233", "--consuming-private-key", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "--chain", diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 83157708c..4737868d0 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -33,7 +33,7 @@ use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; -use rusqlite::{ToSql, Transaction}; +use rusqlite::ToSql; use rustc_hex::{FromHex, ToHex}; use std::convert::TryFrom; use std::path::{Path, PathBuf}; @@ -41,7 +41,7 @@ use std::time::{Duration, Instant, SystemTime}; use std::{thread, u128}; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; -use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters}; +use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; use web3::Web3; #[test] @@ -85,7 +85,8 @@ fn verify_bill_payment() { owed_to_serving_node_3_minor, }), payment_thresholds_all_nodes: payment_thresholds, - exp_final_cons_node_transaction_fee_balance_minor: 99_999_842_470_000_000_000, + cons_node_transaction_fee_agreed_unit_price_opt: None, + exp_final_cons_node_transaction_fee_balance_minor: 999_842_470_000_000_000, exp_final_cons_node_service_fee_balance_minor, exp_final_service_fee_balance_serv_node_1_minor: owed_to_serving_node_1_minor, exp_final_service_fee_balance_serv_node_2_minor: owed_to_serving_node_2_minor, @@ -146,344 +147,32 @@ fn verify_bill_payment() { stimulate_payments, start_serving_nodes_and_run_check, ); - // - // let mut cluster = match MASQNodeCluster::start() { - // Ok(cluster) => cluster, - // Err(e) => panic!("{}", e), - // }; - // let blockchain_server = BlockchainServer { - // name: "ganache-cli", - // }; - // blockchain_server.start(); - // blockchain_server.wait_until_ready(); - // let url = blockchain_server.url().to_string(); - // let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); - // let web3 = Web3::new(http.clone()); - // let seed = make_seed(); - // let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); - // let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - // assert_eq!( - // contract_addr, - // cluster.chain.rec().contract, - // "Ganache is not as predictable as we thought: Update blockchain_interface::MULTINODE_CONTRACT_ADDRESS with {:?}", - // contract_addr - // ); - // let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); - // let payment_thresholds = PaymentThresholds { - // threshold_interval_sec: 2_500_000, - // debt_threshold_gwei: 1_000_000_000, - // payment_grace_period_sec: 85_000, - // maturity_threshold_sec: 85_000, - // permanent_debt_allowed_gwei: 10_000_000, - // unban_below_gwei: 10_000_000, - // }; - // let (consuming_config, consuming_node_wallet) = build_config( - // &blockchain_server, - // &seed, - // payment_thresholds, - // derivation_path(0, 1), - // None, - // ); - // - // let consuming_node_initial_service_fee_balance = - // gwei_to_wei(payment_thresholds.debt_threshold_gwei * 10); - // - // transfer_tokens_to_address( - // contract_addr, - // &contract_owner_wallet, - // &consuming_node_wallet, - // consuming_node_initial_service_fee_balance, - // 1, - // &web3, - // cluster.chain, - // ); - // - // assert_balances( - // &consuming_node_wallet, - // &blockchain_interface, - // "100000000000000000000", // Default at ganache - // &consuming_node_initial_service_fee_balance.to_string(), - // ); - // - // let (serving_node_1_config, serving_node_1_wallet) = build_config( - // &blockchain_server, - // &seed, - // payment_thresholds, - // derivation_path(0, 2), - // None, - // ); - // let (serving_node_2_config, serving_node_2_wallet) = build_config( - // &blockchain_server, - // &seed, - // payment_thresholds, - // derivation_path(0, 3), - // None, - // ); - // let (serving_node_3_config, serving_node_3_wallet) = build_config( - // &blockchain_server, - // &seed, - // payment_thresholds, - // derivation_path(0, 4), - // None, - // ); - // - // let amount = |addition: u128| { - // payment_thresholds.debt_threshold_gwei as u128 * WEIS_IN_GWEI as u128 + addition - // }; - // let payment_to_serving_node_1 = amount(123); - // let payment_to_serving_node_2 = amount(456); - // let payment_to_serving_node_3 = amount(789); - // - // let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); - // let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); - // let consuming_node_connection = DbInitializerReal::default() - // .initialize( - // Path::new(&consuming_node_path), - // make_init_config(cluster.chain), - // ) - // .unwrap(); - // let consuming_payable_dao = PayableDaoReal::new(consuming_node_connection); - // open_all_file_permissions(consuming_node_path.clone().into()); - // assert_eq!( - // format!("{}", &consuming_node_wallet), - // "0x7a3cf474962646b18666b5a5be597bb0af013d81" - // ); - // assert_eq!( - // format!("{}", &serving_node_1_wallet), - // "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" - // ); - // assert_eq!( - // format!("{}", &serving_node_2_wallet), - // "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" - // ); - // assert_eq!( - // format!("{}", &serving_node_3_wallet), - // "0xb45a33ef3e3097f34c826369b74141ed268cdb5a" - // ); - // let now = SystemTime::now(); - // consuming_payable_dao - // .more_money_payable(now, &serving_node_1_wallet, payment_to_serving_node_1) - // .unwrap(); - // consuming_payable_dao - // .more_money_payable(now, &serving_node_2_wallet, payment_to_serving_node_2) - // .unwrap(); - // consuming_payable_dao - // .more_money_payable(now, &serving_node_3_wallet, payment_to_serving_node_3) - // .unwrap(); - // - // let (serving_node_1_name, serving_node_1_index) = - // cluster.prepare_real_node(&serving_node_1_config); - // let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); - // let serving_node_1_connection = DbInitializerReal::default() - // .initialize( - // Path::new(&serving_node_1_path), - // make_init_config(cluster.chain), - // ) - // .unwrap(); - // let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); - // serving_node_1_receivable_dao - // .more_money_receivable( - // SystemTime::now(), - // &consuming_node_wallet, - // payment_to_serving_node_1, - // ) - // .unwrap(); - // open_all_file_permissions(serving_node_1_path.clone().into()); - // - // let (serving_node_2_name, serving_node_2_index) = - // cluster.prepare_real_node(&serving_node_2_config); - // let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); - // let serving_node_2_connection = DbInitializerReal::default() - // .initialize( - // Path::new(&serving_node_2_path), - // make_init_config(cluster.chain), - // ) - // .unwrap(); - // let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); - // serving_node_2_receivable_dao - // .more_money_receivable( - // SystemTime::now(), - // &consuming_node_wallet, - // payment_to_serving_node_2, - // ) - // .unwrap(); - // open_all_file_permissions(serving_node_2_path.clone().into()); - // - // let (serving_node_3_name, serving_node_3_index) = - // cluster.prepare_real_node(&serving_node_3_config); - // let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); - // let serving_node_3_connection = DbInitializerReal::default() - // .initialize( - // Path::new(&serving_node_3_path), - // make_init_config(cluster.chain), - // ) - // .unwrap(); - // let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); - // serving_node_3_receivable_dao - // .more_money_receivable( - // SystemTime::now(), - // &consuming_node_wallet, - // payment_to_serving_node_3, - // ) - // .unwrap(); - // open_all_file_permissions(serving_node_3_path.clone().into()); - // - // expire_payables(consuming_node_path.into()); - // expire_receivables(serving_node_1_path.into()); - // expire_receivables(serving_node_2_path.into()); - // expire_receivables(serving_node_3_path.into()); - // - // // Note: ganache defaults the balance of each created account to 100 ETH - // - // assert_balances( - // &consuming_node_wallet, - // &blockchain_interface, - // "100000000000000000000", - // &consuming_node_initial_service_fee_balance.to_string(), - // ); - // - // assert_balances( - // &serving_node_1_wallet, - // &blockchain_interface, - // "100000000000000000000", - // "0", - // ); - // - // assert_balances( - // &serving_node_2_wallet, - // &blockchain_interface, - // "100000000000000000000", - // "0", - // ); - // - // assert_balances( - // &serving_node_3_wallet, - // &blockchain_interface, - // "100000000000000000000", - // "0", - // ); - // - // let real_consuming_node = - // cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); - // for _ in 0..6 { - // cluster.start_real_node( - // NodeStartupConfigBuilder::standard() - // .chain(Chain::Dev) - // .neighbor(real_consuming_node.node_reference()) - // .build(), - // ); - // } - // - // let now = Instant::now(); - // while !consuming_payable_dao.non_pending_payables().is_empty() - // && now.elapsed() < Duration::from_secs(10) - // { - // thread::sleep(Duration::from_millis(400)); - // } - // - // let expected_final_token_balance_consuming_node = consuming_node_initial_service_fee_balance - // - (payment_to_serving_node_1 + payment_to_serving_node_2 + payment_to_serving_node_3); - // - // assert_balances( - // &consuming_node_wallet, - // &blockchain_interface, - // "99999842482000000000", - // &expected_final_token_balance_consuming_node.to_string(), - // ); - // - // assert_balances( - // &serving_node_1_wallet, - // &blockchain_interface, - // "100000000000000000000", - // &payment_to_serving_node_1.to_string(), - // ); - // - // assert_balances( - // &serving_node_2_wallet, - // &blockchain_interface, - // "100000000000000000000", - // &payment_to_serving_node_2.to_string(), - // ); - // - // assert_balances( - // &serving_node_3_wallet, - // &blockchain_interface, - // "100000000000000000000", - // payment_to_serving_node_3.to_string().as_str(), - // ); - // - // let serving_node_1 = cluster.start_named_real_node( - // &serving_node_1_name, - // serving_node_1_index, - // serving_node_1_config, - // ); - // let serving_node_2 = cluster.start_named_real_node( - // &serving_node_2_name, - // serving_node_2_index, - // serving_node_2_config, - // ); - // let serving_node_3 = cluster.start_named_real_node( - // &serving_node_3_name, - // serving_node_3_index, - // serving_node_3_config, - // ); - // for _ in 0..6 { - // cluster.start_real_node( - // NodeStartupConfigBuilder::standard() - // .chain(Chain::Dev) - // .neighbor(serving_node_1.node_reference()) - // .neighbor(serving_node_2.node_reference()) - // .neighbor(serving_node_3.node_reference()) - // .build(), - // ); - // } - // - // test_utils::wait_for(Some(1000), Some(15000), || { - // if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { - // status.balance_wei == 0 - // } else { - // false - // } - // }); - // test_utils::wait_for(Some(1000), Some(15000), || { - // if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { - // status.balance_wei == 0 - // } else { - // false - // } - // }); - // test_utils::wait_for(Some(1000), Some(15000), || { - // if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { - // status.balance_wei == 0 - // } else { - // false - // } - // }); } #[test] -fn payments_were_adjusted_due_to_insufficient_balance() { +fn payments_were_adjusted_due_to_insufficient_balances() { let consuming_node_ui_port = find_free_port(); let serving_node_1_ui_port = find_free_port(); let serving_node_2_ui_port = find_free_port(); let serving_node_3_ui_port = find_free_port(); let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_500_000, - debt_threshold_gwei: 1_000_000_000, + debt_threshold_gwei: 100_000_000, payment_grace_period_sec: 85_000, maturity_threshold_sec: 85_000, permanent_debt_allowed_gwei: 10_000_000, - unban_below_gwei: 10_000_000, + unban_below_gwei: 1_000_000, }; let owed_to_serv_node_1_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 10_000); + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 5_000_000); let owed_to_serv_node_2_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 20_000); + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 20_000_000); let owed_to_serv_node_3_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 30_000); + gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 60_000_000); // Assuming all Nodes rely on the same set of payment thresholds - let cons_node_initial_service_fee_balance_minor = - (owed_to_serv_node_2_minor + owed_to_serv_node_3_minor) - 150_000; + let cons_node_initial_service_fee_balance_minor = (owed_to_serv_node_2_minor + + owed_to_serv_node_3_minor) + - gwei_to_wei::(20_000_000); let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor - (owed_to_serv_node_1_minor + owed_to_serv_node_2_minor + owed_to_serv_node_3_minor); let test_global_config = TestInputsOutputsConfig { @@ -493,8 +182,9 @@ fn payments_were_adjusted_due_to_insufficient_balance() { serving_node_2: serving_node_2_ui_port, serving_node_3: serving_node_3_ui_port, }), - // Shouldn't be enough for three payments therefore the least significant will be eliminated - cons_node_initial_transaction_fee_balance_minor_opt: Some(3_000_000_000_000_000), + // Should be enough only for two payments therefore the least significant one will + // need to go away + cons_node_initial_transaction_fee_balance_minor_opt: Some(6_000_000), cons_node_initial_service_fee_balance_minor, debts_config: Either::Right(FullySpecifiedSimulatedDebts { // This account will be the least significant and be eliminated for the reasons @@ -519,7 +209,8 @@ fn payments_were_adjusted_due_to_insufficient_balance() { }, }), payment_thresholds_all_nodes: payment_thresholds, - exp_final_cons_node_transaction_fee_balance_minor: 99_999_842_470_000_000_000, + cons_node_transaction_fee_agreed_unit_price_opt: Some(60), + exp_final_cons_node_transaction_fee_balance_minor: 37_514_000_000_000, exp_final_cons_node_service_fee_balance_minor, exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor, @@ -542,7 +233,7 @@ fn payments_were_adjusted_due_to_insufficient_balance() { &real_consuming_node, consuming_node_ui_port, ScanType::Payables, - 5555, + 1111, ) }; @@ -554,9 +245,9 @@ fn payments_were_adjusted_due_to_insufficient_balance() { -> (MASQRealNode, MASQRealNode, MASQRealNode) { let ports = test_global_config.ui_ports_opt.as_ref().unwrap(); let mut vec: Vec = vec![ - (serving_node_1_attributes, ports.serving_node_1, 1111), - (serving_node_2_attributes, ports.serving_node_2, 2222), - (serving_node_3_attributes, ports.serving_node_3, 3333), + (serving_node_1_attributes, ports.serving_node_1, 2222), + (serving_node_2_attributes, ports.serving_node_2, 3333), + (serving_node_3_attributes, ports.serving_node_3, 4444), ] .into_iter() .map(|(serving_node_attributes, ui_port, context_id)| { @@ -649,16 +340,16 @@ fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Ad } } -fn transfer_tokens_to_address( +fn transfer_service_fee_amount_to_address( contract_addr: Address, from_wallet: &Wallet, to_wallet: &Wallet, - amount: u128, + amount_minor: u128, transaction_nonce: u64, web3: &Web3, chain: Chain, ) { - let data = transaction_data_web3(to_wallet, amount); + let data = transaction_data_web3(to_wallet, amount_minor); let gas_price = 150_000_000_000_u64; let gas_limit = 1_000_000_u64; let tx = TransactionParameters { @@ -699,6 +390,57 @@ fn sign_transaction( .expect("transaction preparation failed") } +fn transfer_transaction_fee_amount_to_address( + from_wallet: &Wallet, + to_wallet: &Wallet, + amount_minor: u128, + transaction_nonce: u64, + web3: &Web3, +) { + let gas_price = 150_000_000_000_u64; + let gas_limit = 1_000_000_u64; + let tx = TransactionRequest { + from: from_wallet.address(), + to: Some(to_wallet.address()), + gas: Some(ethereum_types::U256::try_from(gas_limit).expect("Internal error")), + gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), + value: Some(ethereum_types::U256::from(amount_minor)), + data: None, + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + condition: None, + }; + + match web3 + .personal() + .unlock_account(from_wallet.address(), "", None) + .wait() + { + Ok(was_successful) => { + if was_successful { + eprintln!( + "Account {} unlocked for a single transaction", + from_wallet.address() + ) + } else { + panic!( + "Couldn't unlock account {} for the purpose of signing the next transaction", + from_wallet.address() + ) + } + } + Err(e) => panic!( + "Attempt to unlock account {} failed at {:?}", + from_wallet.address(), + e + ), + } + + match web3.eth().send_transaction(tx).wait() { + Ok(tx_hash) => eprintln!("Transaction {} was sent", tx_hash), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } +} + fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { let extended_priv_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); let secret = extended_priv_key.secret().to_hex::(); @@ -720,11 +462,12 @@ fn build_config( server_url_holder: &dyn UrlHolder, seed: &Seed, payment_thresholds: PaymentThresholds, + transaction_fee_agreed_price_per_unit_opt: Option, wallet_derivation_path: String, port_opt: Option, ) -> (NodeStartupConfig, Wallet) { let (node_wallet, node_secret) = make_node_wallet(seed, wallet_derivation_path.as_str()); - let pre_config = NodeStartupConfigBuilder::standard() + let cfg_to_build = NodeStartupConfigBuilder::standard() .blockchain_service_url(server_url_holder.url()) .chain(Chain::Dev) .payment_thresholds(payment_thresholds) @@ -733,12 +476,17 @@ fn build_config( "{}", node_wallet.clone() ))); - let pre_config = if let Some(port) = port_opt { - pre_config.ui_port(port) + let cfg_to_build = if let Some(port) = port_opt { + cfg_to_build.ui_port(port) + } else { + cfg_to_build + }; + let cfg_to_build = if let Some(price) = transaction_fee_agreed_price_per_unit_opt { + cfg_to_build.gas_price(price) } else { - pre_config + cfg_to_build }; - let config = pre_config.build(); + let config = cfg_to_build.build(); (config, node_wallet) } @@ -808,8 +556,6 @@ fn expire_receivables(path: PathBuf) { config_stmt.execute([]).unwrap(); } -const DEFAULT_GANACHE_TRANSACTION_FEE_BALANCE_WEI: u128 = 100_000_000_000_000_000_000; - struct TestInputsOutputsConfig { ui_ports_opt: Option, // Gets ganache default 100 ETH if None. @@ -822,6 +568,7 @@ struct TestInputsOutputsConfig { cons_node_initial_service_fee_balance_minor: u128, debts_config: Either, payment_thresholds_all_nodes: PaymentThresholds, + cons_node_transaction_fee_agreed_unit_price_opt: Option, exp_final_cons_node_transaction_fee_balance_minor: u128, exp_final_cons_node_service_fee_balance_minor: u128, @@ -944,15 +691,26 @@ fn test_body Option { - let cw_balance = payment_adjuster.inner.unallocated_cw_masq_balance_minor(); + let cw_balance = payment_adjuster + .inner + .unallocated_cw_service_fee_balance_minor(); let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { last_account.balance_wei } else { diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index d6ff35e9b..cf8e374ce 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -167,13 +167,13 @@ pub mod formulas_progressive_characteristics { use thousands::Separable; // Only for debugging; in order to see the characteristic values of distinct parameter - pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; + pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = false; // Preserve the 'static' keyword static SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER: Mutex> = Mutex::new(vec![]); // Preserve the 'static' keyword // - // The singleton ensures that we print the characteristics always only once, also if multiple + // The singleton ensures that we print the characteristics always only once, no matter how many // tests are running static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index db24df316..339d95743 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -5,16 +5,16 @@ use std::time::SystemTime; pub trait PaymentAdjusterInner { fn now(&self) -> SystemTime; fn transaction_fee_count_limit_opt(&self) -> Option; - fn original_cw_masq_balance_minor(&self) -> u128; - fn unallocated_cw_masq_balance_minor(&self) -> u128; - fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128); + fn original_cw_service_fee_balance_minor(&self) -> u128; + fn unallocated_cw_service_fee_balance_minor(&self) -> u128; + fn update_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128); } pub struct PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, - original_cw_masq_balance_minor: u128, - unallocated_cw_masq_balance_minor: u128, + original_cw_service_fee_balance_minor: u128, + unallocated_cw_service_fee_balance_minor: u128, } impl PaymentAdjusterInnerReal { @@ -26,8 +26,8 @@ impl PaymentAdjusterInnerReal { Self { now, transaction_fee_count_limit_opt, - original_cw_masq_balance_minor: cw_masq_balance_minor, - unallocated_cw_masq_balance_minor: cw_masq_balance_minor, + original_cw_service_fee_balance_minor: cw_masq_balance_minor, + unallocated_cw_service_fee_balance_minor: cw_masq_balance_minor, } } } @@ -39,18 +39,18 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn transaction_fee_count_limit_opt(&self) -> Option { self.transaction_fee_count_limit_opt } - fn original_cw_masq_balance_minor(&self) -> u128 { - self.original_cw_masq_balance_minor + fn original_cw_service_fee_balance_minor(&self) -> u128 { + self.original_cw_service_fee_balance_minor } - fn unallocated_cw_masq_balance_minor(&self) -> u128 { - self.unallocated_cw_masq_balance_minor + fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { + self.unallocated_cw_service_fee_balance_minor } - fn update_unallocated_cw_balance_minor(&mut self, subtrahend: u128) { + fn update_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128) { let updated_thought_cw_balance = self - .unallocated_cw_masq_balance_minor + .unallocated_cw_service_fee_balance_minor .checked_sub(subtrahend) .expect("got into negative numbers"); - self.unallocated_cw_masq_balance_minor = updated_thought_cw_balance + self.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance } } @@ -72,14 +72,16 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull { fn transaction_fee_count_limit_opt(&self) -> Option { PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") } - fn original_cw_masq_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("original_cw_masq_balance_minor()") + fn original_cw_service_fee_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("original_cw_service_fee_balance_minor()") } - fn unallocated_cw_masq_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_masq_balance_minor()") + fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_service_fee_balance_minor()") } - fn update_unallocated_cw_balance_minor(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation("update_unallocated_cw_balance_minor()") + fn update_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128) { + PaymentAdjusterInnerNull::panicking_operation( + "update_unallocated_cw_service_fee_balance_minor()", + ) } } @@ -103,8 +105,14 @@ mod tests { result.transaction_fee_count_limit_opt, transaction_fee_count_limit_opt ); - assert_eq!(result.original_cw_masq_balance_minor, cw_masq_balance); - assert_eq!(result.unallocated_cw_masq_balance_minor, cw_masq_balance) + assert_eq!( + result.original_cw_service_fee_balance_minor, + cw_masq_balance + ); + assert_eq!( + result.unallocated_cw_service_fee_balance_minor, + cw_masq_balance + ) } #[test] @@ -129,31 +137,31 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the original_cw_masq_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] - fn inner_null_calling_original_cw_masq_balance_minor() { + fn inner_null_calling_original_cw_service_fee_balance_minor() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.original_cw_masq_balance_minor(); + let _ = subject.original_cw_service_fee_balance_minor(); } #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_unallocated_cw_balance() { let subject = PaymentAdjusterInnerNull {}; - let _ = subject.unallocated_cw_masq_balance_minor(); + let _ = subject.unallocated_cw_service_fee_balance_minor(); } #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the update_unallocated_cw_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the update_unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] - fn inner_null_calling_update_unallocated_cw_balance_minor() { + fn inner_null_calling_update_unallocated_cw_service_fee_balance_minor() { let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.update_unallocated_cw_balance_minor(123); + let _ = subject.update_unallocated_cw_service_fee_balance_minor(123); } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 67f08f576..cfff55264 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -127,18 +127,18 @@ pub fn find_largest_nominated_account<'a>( pub fn exhaust_cw_till_the_last_drop( approved_accounts: Vec, - original_cw_masq_balance_minor: u128, + original_cw_service_fee_balance_minor: u128, ) -> Vec { let adjusted_balances_total: u128 = sum_as(&approved_accounts, |account_info| { account_info.proposed_adjusted_balance }); - let cw_reminder = original_cw_masq_balance_minor + let cw_reminder = original_cw_service_fee_balance_minor .checked_sub(adjusted_balances_total) .unwrap_or_else(|| { panic!( "Remainder should've been a positive number but wasn't after {} - {}", - original_cw_masq_balance_minor, adjusted_balances_total + original_cw_service_fee_balance_minor, adjusted_balances_total ) }); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index ace32650c..3e19205fc 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -7,8 +7,8 @@ mod diagnostics; mod inner; mod log_fns; mod miscellaneous; +mod possibility_verifier; mod test_utils; -mod verifier; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ @@ -32,7 +32,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_and_leave_naked_affordable_accounts, keep_only_transaction_fee_affordable_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; -use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; +use crate::accountant::payment_adjuster::possibility_verifier::MasqAdjustmentPossibilityVerifier; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -93,7 +93,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => return Err(e), }; - let service_fee_balance_minor = agent.service_fee_balance(); + let service_fee_balance_minor = agent.service_fee_balance_minor(); match Self::check_need_of_adjustment_by_service_fee( &self.logger, Either::Left(qualified_payables), @@ -113,7 +113,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let qualified_payables = setup.qualified_payables; let response_skeleton_opt = setup.response_skeleton_opt; let agent = setup.agent; - let initial_service_fee_balance_minor = agent.service_fee_balance(); + let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment; self.initialize_inner(initial_service_fee_balance_minor, required_adjustment, now); @@ -157,9 +157,10 @@ impl PaymentAdjusterReal { number_of_accounts: usize, logger: &Logger, ) -> Result, PaymentAdjusterError> { - let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction(); + let per_transaction_requirement_minor = + agent.estimated_transaction_fee_per_transaction_minor(); - let cw_transaction_fee_balance_minor = agent.transaction_fee_balance(); + let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); let max_possible_tx_count = u128::try_from( cw_transaction_fee_balance_minor / U256::from(per_transaction_requirement_minor), @@ -270,7 +271,7 @@ impl PaymentAdjusterReal { Either::Left(non_exhausted_accounts) => { let affordable_accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( non_exhausted_accounts, - self.inner.original_cw_masq_balance_minor(), + self.inner.original_cw_service_fee_balance_minor(), ); Ok(affordable_accounts_by_fully_exhausted_cw) } @@ -318,7 +319,7 @@ impl PaymentAdjusterReal { criteria_and_accounts_in_descending_order, already_known_affordable_transaction_count, ); - let unallocated_balance = self.inner.unallocated_cw_masq_balance_minor(); + let unallocated_balance = self.inner.unallocated_cw_service_fee_balance_minor(); let is_service_fee_adjustment_needed = match Self::check_need_of_adjustment_by_service_fee( &self.logger, @@ -476,7 +477,7 @@ impl PaymentAdjusterReal { accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, criteria_total: u128, ) -> Vec { - let cw_masq_balance = self.inner.unallocated_cw_masq_balance_minor(); + let cw_masq_balance = self.inner.unallocated_cw_service_fee_balance_minor(); let cpm_coeff = compute_fractional_numbers_preventing_mul_coefficient(cw_masq_balance, criteria_total); let multiplication_coeff_u256 = U256::from(cpm_coeff); @@ -589,13 +590,13 @@ impl PaymentAdjusterReal { account.proposed_adjusted_balance }); self.inner - .update_unallocated_cw_balance_minor(subtrahend_total); + .update_unallocated_cw_service_fee_balance_minor(subtrahend_total); diagnostics!( "LOWERED CW BALANCE", "Unallocated balance lowered by {} to {}", subtrahend_total, - self.inner.unallocated_cw_masq_balance_minor() + self.inner.unallocated_cw_service_fee_balance_minor() ) } @@ -625,8 +626,9 @@ pub enum PaymentAdjusterError { per_transaction_requirement_minor: u128, cw_transaction_fee_balance_minor: U256, }, - RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: usize, + total_amount_demanded_minor: u128, cw_service_fee_balance_minor: u128, }, AllAccountsUnexpectedlyEliminated, @@ -641,29 +643,31 @@ impl Display for PaymentAdjusterError { cw_transaction_fee_balance_minor, } => write!( f, - "Found smaller transaction fee balance than does for a single payment. \ - Number of canceled payments: {}. Transaction fee for a single account: {} wei. \ - Current consuming wallet balance: {} wei", + "Found a smaller transaction fee balance than it does for a single payment. \ + Number of canceled payments: {}. Transaction fee by single account: {} wei. \ + Consuming wallet balance: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas() ), - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts, + total_amount_demanded_minor, cw_service_fee_balance_minor: cw_masq_balance_minor, } => write!( f, - "Analysis has projected a likely unacceptable adjustment leaving each \ - of the payable accounts with too a low adjusted amount to pay. Please, proceed \ - by sending funds to your wallet. Number of canceled payments: {}. Current \ - consuming wallet balance: {} wei of MASQ", + "Analysis projected a possibility for an adjustment leaving each of the transactions \ + with an uneconomical portion of money compared to the whole bills. Please proceed \ + by sending funds to your consuming wallet. Number of canceled payments: {}. \ + Total amount demanded: {} wei. Consuming wallet balance: {} wei", number_of_accounts.separate_with_commas(), + total_amount_demanded_minor.separate_with_commas(), cw_masq_balance_minor.separate_with_commas() ), PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( f, - "Despite the positive preliminary analysis, no executable adjusted payments \ - could be arranged, the algorithm rejected each payable" + "While chances were according to the preliminary analysis, the adjustment \ + algorithm rejected each payable" ), } } @@ -700,15 +704,15 @@ mod tests { #[test] #[should_panic(expected = "Broken code: Called the null implementation of \ - the unallocated_cw_masq_balance_minor() method in PaymentAdjusterInner")] + the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner")] fn payment_adjuster_new_is_created_with_inner_null() { let result = PaymentAdjusterReal::new(); - let _ = result.inner.unallocated_cw_masq_balance_minor(); + let _ = result.inner.unallocated_cw_service_fee_balance_minor(); } #[test] - fn search_for_indispensable_adjustment_gives_negative_answer() { + fn search_for_indispensable_adjustment_happy_path() { init_test_logging(); let test_name = "search_for_indispensable_adjustment_gives_negative_answer"; let mut subject = PaymentAdjusterReal::new(); @@ -769,13 +773,12 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_positive_for_transaction_fee() { + fn search_for_indispensable_adjustment_sad_path_for_transaction_fee() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_transaction_fee"; + let test_name = "search_for_indispensable_adjustment_sad_path_positive_for_transaction_fee"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let number_of_accounts = 3; - // means a confidently big balance is picked in the behind let masq_balances_config_opt = None; let (qualified_payables, agent) = make_test_input_for_initial_check( masq_balances_config_opt, @@ -809,9 +812,9 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_positive_for_service_fee_token() { + fn search_for_indispensable_adjustment_sad_path_for_service_fee_balance() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_service_fee_token"; + let test_name = "search_for_indispensable_adjustment_positive_for_service_fee_balance"; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; @@ -841,9 +844,9 @@ mod tests { } #[test] - fn checking_three_accounts_positive_for_transaction_fee_but_service_fee_balance_is_unbearably_low( - ) { - let test_name = "checking_three_accounts_positive_for_transaction_fee_but_service_fee_balance_is_unbearably_low"; + fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low() + { + let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low"; let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; // this would normally kick a serious error let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Left(vec![120, 300, 500]), @@ -859,8 +862,9 @@ mod tests { assert_eq!( result, Err( - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, + total_amount_demanded_minor: 920_000_000_000, cw_service_fee_balance_minor } ) @@ -903,14 +907,16 @@ mod tests { fn payment_adjuster_error_implements_display() { vec![ ( - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 5, - cw_service_fee_balance_minor: 333_000_000, + total_amount_demanded_minor: 6_000_000_000, + cw_service_fee_balance_minor: 333_000_000, }, - "Analysis has projected a likely unacceptable adjustment leaving each of the payable \ - accounts with too a low adjusted amount to pay. Please, proceed by sending funds to \ - your wallet. Number of canceled payments: 5. Current consuming wallet balance: \ - 333,000,000 wei of MASQ", + "Analysis projected a possibility for an adjustment leaving each of \ + the transactions with an uneconomical portion of money compared to the whole bills. \ + Please proceed by sending funds to your consuming wallet. Number of canceled \ + payments: 5. Total amount demanded: 6,000,000,000 wei. Consuming wallet balance: \ + 333,000,000 wei", ), ( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { @@ -918,14 +924,14 @@ mod tests { per_transaction_requirement_minor: 70_000_000_000_000, cw_transaction_fee_balance_minor: U256::from(90_000), }, - "Found smaller transaction fee balance than does for a single payment. \ - Number of canceled payments: 4. Transaction fee for a single account: \ - 70,000,000,000,000 wei. Current consuming wallet balance: 90,000 wei", + "Found a smaller transaction fee balance than it does for a single payment. \ + Number of canceled payments: 4. Transaction fee by single account: \ + 70,000,000,000,000 wei. Consuming wallet balance: 90,000 wei", ), ( PaymentAdjusterError::AllAccountsUnexpectedlyEliminated, - "Despite the positive preliminary analysis, no executable adjusted payments \ - could be arranged, the algorithm rejected each payable", + "While chances were according to the preliminary analysis, the adjustment \ + algorithm rejected each payable", ), ] .into_iter() @@ -1197,8 +1203,8 @@ mod tests { // In turn, extremely small cw balance let cw_service_fee_balance = 1_000; let agent = { - let mock = - BlockchainAgentMock::default().service_fee_balance_result(cw_service_fee_balance); + let mock = BlockchainAgentMock::default() + .service_fee_balance_minor_result(cw_service_fee_balance); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1272,7 +1278,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1356,7 +1362,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(10_u128.pow(22)); + .service_fee_balance_minor_result(10_u128.pow(22)); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1427,7 +1433,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); Box::new(mock) }; let response_skeleton_opt = Some(ResponseSkeleton { @@ -1497,7 +1503,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); Box::new(mock) }; let response_skeleton_opt = Some(ResponseSkeleton { @@ -1589,7 +1595,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); let agent = { let mock = BlockchainAgentMock::default() - .service_fee_balance_result(cw_service_fee_balance_in_minor); + .service_fee_balance_minor_result(cw_service_fee_balance_in_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1727,7 +1733,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor); + .service_fee_balance_minor_result(service_fee_balance_in_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1802,7 +1808,7 @@ mod tests { let service_fee_balance_in_minor_units = (111_000_000_000_000 / 2) - 1; let agent = { let mock = BlockchainAgentMock::default() - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1822,8 +1828,9 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 2, + total_amount_demanded_minor: 333_000_000_000_000 + 222_000_000_000_000, cw_service_fee_balance_minor: service_fee_balance_in_minor_units } ); @@ -1886,7 +1893,7 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1989,9 +1996,9 @@ mod tests { * agreed_transaction_fee_price, ); let blockchain_agent = BlockchainAgentMock::default() - .transaction_fee_balance_result(cw_transaction_fee_minor) - .service_fee_balance_result(service_fee_balances_setup.cw_balance_minor) - .estimated_transaction_fee_per_transaction_result( + .transaction_fee_balance_minor_result(cw_transaction_fee_minor) + .service_fee_balance_minor_result(service_fee_balances_setup.cw_balance_minor) + .estimated_transaction_fee_per_transaction_minor_result( estimated_transaction_fee_per_transaction_minor, ); diff --git a/node/src/accountant/payment_adjuster/verifier.rs b/node/src/accountant/payment_adjuster/possibility_verifier.rs similarity index 87% rename from node/src/accountant/payment_adjuster/verifier.rs rename to node/src/accountant/payment_adjuster/possibility_verifier.rs index 629a6a4af..3f01c15d8 100644 --- a/node/src/accountant/payment_adjuster/verifier.rs +++ b/node/src/accountant/payment_adjuster/possibility_verifier.rs @@ -1,7 +1,9 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + calculate_disqualification_edge, sum_as, +}; use crate::accountant::payment_adjuster::PaymentAdjusterError; use itertools::Itertools; @@ -24,16 +26,18 @@ impl MasqAdjustmentPossibilityVerifier { Ord::cmp(&account_a.balance_wei, &account_b.balance_wei) }) .collect::>(); - let smallest_account = sorted.first().expect("empty Vec of qualified payables "); + let smallest_account = sorted.first().expect("should be one at minimum"); if calculate_disqualification_edge(smallest_account.balance_wei) <= cw_service_fee_balance_minor { Ok(()) } else { + let total_amount_demanded_minor = sum_as(accounts, |account| account.balance_wei); Err( - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: accounts.len(), + total_amount_demanded_minor, cw_service_fee_balance_minor, }, ) @@ -45,7 +49,7 @@ impl MasqAdjustmentPossibilityVerifier { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::verifier::MasqAdjustmentPossibilityVerifier; + use crate::accountant::payment_adjuster::possibility_verifier::MasqAdjustmentPossibilityVerifier; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_payable_account; @@ -108,8 +112,9 @@ mod tests { assert_eq!( result, Err( - PaymentAdjusterError::RiskOfWastedAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, + total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, cw_service_fee_balance_minor: cw_masq_balance } ) diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 22e3e680a..2d55a78dd 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -12,18 +12,18 @@ pub struct BlockchainAgentNull { } impl BlockchainAgent for BlockchainAgentNull { - fn estimated_transaction_fee_per_transaction(&self) -> u128 { - self.log_function_call("estimated_transaction_fee_per_transaction()"); + fn estimated_transaction_fee_per_transaction_minor(&self) -> u128 { + self.log_function_call("estimated_transaction_fee_per_transaction_minor()"); 0 } - fn transaction_fee_balance(&self) -> U256 { - self.log_function_call("transaction_fee_balance()"); + fn transaction_fee_balance_minor(&self) -> U256 { + self.log_function_call("transaction_fee_balance_minor()"); U256::zero() } - fn service_fee_balance(&self) -> u128 { - self.log_function_call("service_fee_balance()"); + fn service_fee_balance_minor(&self) -> u128 { + self.log_function_call("service_fee_balance_minor()"); 0 } @@ -117,42 +117,42 @@ mod tests { } #[test] - fn null_agent_estimated_transaction_fee_per_transaction() { + fn null_agent_estimated_transaction_fee_per_transaction_minor() { init_test_logging(); - let test_name = "null_agent_estimated_transaction_fee_per_transaction"; + let test_name = "null_agent_estimated_transaction_fee_per_transaction_minor"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.estimated_transaction_fee_per_transaction(); + let result = subject.estimated_transaction_fee_per_transaction_minor(); assert_eq!(result, 0); - assert_error_log(test_name, "estimated_transaction_fee_per_transaction"); + assert_error_log(test_name, "estimated_transaction_fee_per_transaction_minor"); } #[test] - fn null_agent_consuming_transaction_fee_balance() { + fn null_agent_consuming_transaction_fee_balance_minor() { init_test_logging(); - let test_name = "null_agent_consuming_transaction_fee_balance"; + let test_name = "null_agent_consuming_transaction_fee_balance_minor"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.transaction_fee_balance(); + let result = subject.transaction_fee_balance_minor(); assert_eq!(result, U256::zero()); - assert_error_log(test_name, "transaction_fee_balance") + assert_error_log(test_name, "transaction_fee_balance_minor") } #[test] - fn null_agent_service_fee_balance() { + fn null_agent_service_fee_balance_minor() { init_test_logging(); - let test_name = "null_agent_service_fee_balance"; + let test_name = "null_agent_service_fee_balance_minor"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.service_fee_balance(); + let result = subject.service_fee_balance_minor(); assert_eq!(result, 0); - assert_error_log(test_name, "service_fee_balance") + assert_error_log(test_name, "service_fee_balance_minor") } #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 7b0cc20a1..8f1b890bf 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -5,6 +5,7 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockch use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; +use crate::accountant::gwei_to_wei; use web3::types::U256; #[derive(Debug, Clone)] @@ -18,18 +19,18 @@ pub struct BlockchainAgentWeb3 { } impl BlockchainAgent for BlockchainAgentWeb3 { - fn estimated_transaction_fee_per_transaction(&self) -> u128 { + fn estimated_transaction_fee_per_transaction_minor(&self) -> u128 { let gas_price = self.gas_price_gwei as u128; let max_gas_limit = (self.maximum_added_gas_margin + self.gas_limit_const_part) as u128; - gas_price * max_gas_limit + gwei_to_wei(gas_price * max_gas_limit) } - fn transaction_fee_balance(&self) -> U256 { + fn transaction_fee_balance_minor(&self) -> U256 { self.consuming_wallet_balances .transaction_fee_balance_in_minor_units } - fn service_fee_balance(&self) -> u128 { + fn service_fee_balance_minor(&self) -> u128 { self.consuming_wallet_balances .service_fee_balance_in_minor_units } @@ -80,6 +81,7 @@ mod tests { use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::test_utils::make_wallet; + use crate::accountant::gwei_to_wei; use web3::types::U256; #[test] @@ -109,11 +111,11 @@ mod tests { assert_eq!(subject.agreed_fee_per_computation_unit(), gas_price_gwei); assert_eq!(subject.consuming_wallet(), &consuming_wallet); assert_eq!( - subject.transaction_fee_balance(), + subject.transaction_fee_balance_minor(), consuming_wallet_balances.transaction_fee_balance_in_minor_units ); assert_eq!( - subject.service_fee_balance(), + subject.service_fee_balance_minor(), consuming_wallet_balances.service_fee_balance_in_minor_units ); assert_eq!(subject.pending_transaction_id(), pending_transaction_id) @@ -128,23 +130,24 @@ mod tests { }; let nonce = U256::from(55); let agent = BlockchainAgentWeb3::new( - 444, + 244, 77_777, consuming_wallet, consuming_wallet_balances, nonce, ); - let result = agent.estimated_transaction_fee_per_transaction(); + let result = agent.estimated_transaction_fee_per_transaction_minor(); assert_eq!(agent.gas_limit_const_part, 77_777); assert_eq!( agent.maximum_added_gas_margin, WEB3_MAXIMAL_GAS_LIMIT_MARGIN ); - assert_eq!( - result, - (77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) as u128 * 444 - ); + let expected_result: u128 = { + let gwei_amount = ((77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) as u128) * 244; + gwei_to_wei(gwei_amount) + }; + assert_eq!(result, expected_result); } } diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index e24abc8c1..fa0805da9 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -21,9 +21,9 @@ use web3::types::U256; //* defaulted limit pub trait BlockchainAgent: Send { - fn estimated_transaction_fee_per_transaction(&self) -> u128; - fn transaction_fee_balance(&self) -> U256; - fn service_fee_balance(&self) -> u128; + fn estimated_transaction_fee_per_transaction_minor(&self) -> u128; + fn transaction_fee_balance_minor(&self) -> U256; + fn service_fee_balance_minor(&self) -> u128; fn agreed_fee_per_computation_unit(&self) -> u64; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index 8e10a476d..3c19845f1 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -11,9 +11,9 @@ use std::cell::RefCell; #[derive(Default)] pub struct BlockchainAgentMock { - estimated_transaction_fee_per_transaction_results: RefCell>, - transaction_fee_balance_results: RefCell>, - service_fee_balance_results: RefCell>, + estimated_transaction_fee_per_transaction_minor_results: RefCell>, + transaction_fee_balance_minor_results: RefCell>, + service_fee_balance_minor_results: RefCell>, agreed_fee_per_computation_unit_results: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, @@ -21,18 +21,22 @@ pub struct BlockchainAgentMock { } impl BlockchainAgent for BlockchainAgentMock { - fn estimated_transaction_fee_per_transaction(&self) -> u128 { - self.estimated_transaction_fee_per_transaction_results + fn estimated_transaction_fee_per_transaction_minor(&self) -> u128 { + self.estimated_transaction_fee_per_transaction_minor_results .borrow_mut() .remove(0) } - fn transaction_fee_balance(&self) -> U256 { - self.transaction_fee_balance_results.borrow_mut().remove(0) + fn transaction_fee_balance_minor(&self) -> U256 { + self.transaction_fee_balance_minor_results + .borrow_mut() + .remove(0) } - fn service_fee_balance(&self) -> u128 { - self.service_fee_balance_results.borrow_mut().remove(0) + fn service_fee_balance_minor(&self) -> u128 { + self.service_fee_balance_minor_results + .borrow_mut() + .remove(0) } fn agreed_fee_per_computation_unit(&self) -> u64 { @@ -57,22 +61,24 @@ impl BlockchainAgent for BlockchainAgentMock { } impl BlockchainAgentMock { - pub fn estimated_transaction_fee_per_transaction_result(self, result: u128) -> Self { - self.estimated_transaction_fee_per_transaction_results + pub fn estimated_transaction_fee_per_transaction_minor_result(self, result: u128) -> Self { + self.estimated_transaction_fee_per_transaction_minor_results .borrow_mut() .push(result); self } - pub fn transaction_fee_balance_result(self, result: U256) -> Self { - self.transaction_fee_balance_results + pub fn transaction_fee_balance_minor_result(self, result: U256) -> Self { + self.transaction_fee_balance_minor_results .borrow_mut() .push(result); self } - pub fn service_fee_balance_result(self, result: u128) -> Self { - self.service_fee_balance_results.borrow_mut().push(result); + pub fn service_fee_balance_minor_result(self, result: u128) -> Self { + self.service_fee_balance_minor_results + .borrow_mut() + .push(result); self } diff --git a/node/src/accountant/scanners/payable_scan_setup_msgs.rs b/node/src/accountant/scanners/payable_scan_setup_msgs.rs deleted file mode 100644 index b006b922c..000000000 --- a/node/src/accountant/scanners/payable_scan_setup_msgs.rs +++ /dev/null @@ -1,37 +0,0 @@ -// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -// -// use crate::accountant::db_access_objects::payable_dao::PayableAccount; -// use crate::accountant::ResponseSkeleton; -// use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; -// use actix::Message; -// -// #[derive(Debug, Message, PartialEq, Eq, Clone)] -// pub struct PayablePaymentSetup { -// //this field should stay private for anybody outside Accountant -// pub(in crate::accountant) qualified_payables: Vec, -// pub this_stage_data_opt: Option, -// pub response_skeleton_opt: Option, -// } -// -// impl From<(PayablePaymentSetup, StageData)> for PayablePaymentSetup { -// fn from((previous_msg, this_stage_data): (PayablePaymentSetup, StageData)) -> Self { -// PayablePaymentSetup { -// qualified_payables: previous_msg.qualified_payables, -// this_stage_data_opt: Some(this_stage_data), -// response_skeleton_opt: previous_msg.response_skeleton_opt, -// } -// } -// } -// -// #[derive(Debug, PartialEq, Eq, Clone)] -// pub enum StageData { -// FinancialAndTechDetails(FinancialAndTechDetails), -// } -// -// #[derive(Debug, PartialEq, Eq, Clone)] -// pub struct FinancialAndTechDetails { -// pub consuming_wallet_balances: ConsumingWalletBalances, -// pub agreed_transaction_fee_per_computed_unit_major: u64, -// //rather technical stuff below -// pub estimated_gas_limit_per_transaction: u64, -// } diff --git a/node/src/accountant/scanners/scan_mid_procedures.rs b/node/src/accountant/scanners/scan_mid_procedures.rs deleted file mode 100644 index a5958753f..000000000 --- a/node/src/accountant/scanners/scan_mid_procedures.rs +++ /dev/null @@ -1,49 +0,0 @@ -// // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -// -// use crate::accountant::payment_adjuster::Adjustment; -// use crate::accountant::scanners::payable_scan_setup_msgs::PayablePaymentSetup; -// use crate::accountant::scanners::Scanner; -// use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; -// use actix::Message; -// use itertools::Either; -// use masq_lib::logger::Logger; -// -// pub trait PayableScannerWithMiddleProcedures: -// Scanner + PayableScannerMiddleProcedures -// where -// BeginMessage: Message, -// EndMessage: Message, -// { -// } -// -// pub trait PayableScannerMiddleProcedures { -// fn try_softly( -// &self, -// _msg: PayablePaymentSetup, -// _logger: &Logger, -// ) -> Result, String> { -// intentionally_blank!() -// } -// fn exacting_payments_instructions( -// &self, -// _setup: AwaitedAdjustment, -// _logger: &Logger, -// ) -> OutboundPaymentsInstructions { -// intentionally_blank!() -// } -// } -// -// #[derive(Debug, PartialEq, Eq)] -// pub struct AwaitedAdjustment { -// pub original_setup_msg: PayablePaymentSetup, -// pub adjustment: Adjustment, -// } -// -// impl AwaitedAdjustment { -// pub fn new(original_setup_msg: PayablePaymentSetup, adjustment: Adjustment) -> Self { -// Self { -// original_setup_msg, -// adjustment, -// } -// } -// } diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index a68718a36..0c7d139e7 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -29,7 +29,7 @@ pub mod payable_scanner_utils { RemotelyCausedErrors(Vec), } - //debugging purposes only + // Debug purposes only pub fn investigate_debt_extremes( timestamp: SystemTime, all_non_pending_payables: &[PayableAccount], 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 2d55fcd49..79e4bef85 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -1056,16 +1056,15 @@ mod tests { assert_eq!(*get_transaction_id_params, vec![wallet.clone()]); assert_eq!(result.consuming_wallet(), &wallet); assert_eq!(result.pending_transaction_id(), transaction_id); - assert_eq!(result.transaction_fee_balance(), transaction_fee_balance); - assert_eq!(result.service_fee_balance(), masq_balance.as_u128()); + assert_eq!( + result.transaction_fee_balance_minor(), + transaction_fee_balance + ); + assert_eq!(result.service_fee_balance_minor(), masq_balance.as_u128()); assert_eq!(result.agreed_fee_per_computation_unit(), 50); - let expected_fee_estimation = - ((BlockchainInterfaceWeb3::::web3_gas_limit_const_part(chain) - + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) - * 50) as u128; assert_eq!( - result.estimated_transaction_fee_per_transaction(), - expected_fee_estimation + result.estimated_transaction_fee_per_transaction_minor(), + 3666400000000000 ) } From de6eaec8af74eff07e29741b16aced735e4817a6 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:57:20 +0800 Subject: [PATCH 105/250] GH-711: logs neater now --- .../accountant/payment_adjuster/log_fns.rs | 18 +++---- node/src/accountant/payment_adjuster/mod.rs | 49 ++++++++++--------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index f1ed0cb57..8267d3dac 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -41,9 +41,10 @@ pub fn format_brief_adjustment_summary( account.wallet, original_account_balances_mapped .get(&account.wallet) - .expectv("initial balance"), + .expectv("initial balance") + .separate_with_commas(), BLANK_SPACE, - account.balance_wei, + account.balance_wei.separate_with_commas(), length = WALLET_ADDRESS_LENGTH ) }) @@ -52,7 +53,7 @@ pub fn format_brief_adjustment_summary( fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { let title = once(format!( "\n{:, adjusted_accounts: &[PayableAccount], @@ -101,15 +102,14 @@ pub fn before_and_after_debug_msg( format!( "\n\ {: Date: Sat, 2 Dec 2023 01:05:57 +0800 Subject: [PATCH 106/250] GH-711: log elegance --- node/src/accountant/mod.rs | 17 +++++++++-------- node/src/accountant/scanners/mod.rs | 13 +++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 594fd88ff..134473fb5 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1647,13 +1647,14 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Payment adjustment was \ - considered due to detected insolvency but the current balances are likely to suffice for \ - none of the recently qualified payables, not even by at least a half of any of them, which \ - would still lead to a launch of a payment. Please fund your consuming wallet in order to \ - avoid bans from your creditors. Failure reason: Found a smaller transaction fee balance \ - than it does for a single payment. Number of canceled payments: 1. Transaction fee by single \ - account: 3,300,000 wei. Consuming wallet balance: 123,000,000,000 wei." + "WARN: {test_name}: Insolvency detected, followed by considering a payment adjustment, \ + however, giving no satisfactory solution. Please note that your disponible means are \ + not able to cover even an reasonable portion out of any payable among those recently \ + qualified for an imminent payment. You are kindly requested to add funds into your \ + consuming wallet in order to stay free of delinquency bans that your creditors may \ + apply against you. Failure reason: Found a smaller transaction fee balance than it does \ + for a single payment. Number of canceled payments: 1. Transaction fee by single account: \ + 3,300,000 wei. Consuming wallet balance: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); @@ -1677,7 +1678,7 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( "WARN: {test_name}: Payment adjustment has not \ - produced any executable payments. Please fund your consuming wallet in order to avoid bans \ + produced any executable payments. Please add funds into your consuming wallet in order to avoid bans \ from your creditors. Failure reason: While chances were according to the preliminary analysis, \ the adjustment algorithm rejected each payable" )); diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 9ed6dc969..aa5244468 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -276,11 +276,12 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { Err(e) => { warning!( logger, - "Payment adjustment was considered due to detected insolvency but the current \ - balances are likely to suffice for none of the recently qualified payables, \ - not even by at least a half of any of them, which would still lead to a launch \ - of a payment. Please fund your consuming wallet in order to avoid bans from \ - your creditors. Failure reason: {}.", + "Insolvency detected, followed by considering a payment adjustment, however, \ + giving no satisfactory solution. Please note that your disponible means are not \ + able to cover even an reasonable portion out of any payable among those \ + recently qualified for an imminent payment. You are kindly requested to add \ + funds into your consuming wallet in order to stay free of delinquency bans \ + that your creditors may apply against you. Failure reason: {}.", e ); None @@ -304,7 +305,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { warning!( logger, "Payment adjustment has not produced any executable payments. Please \ - fund your consuming wallet in order to avoid bans from your creditors. \ + add funds into your consuming wallet in order to avoid bans from your creditors. \ Failure reason: {}", e ); From bfb73f521982c392a9b81c7529e358e14675f6ad Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 2 Dec 2023 02:08:43 +0800 Subject: [PATCH 107/250] GH-711: I think the multinode test works fine from now --- .../src/masq_real_node.rs | 2 +- .../tests/verify_bill_payment.rs | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index f7a528a6b..5170bc763 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -207,8 +207,8 @@ impl NodeStartupConfig { args.push(format!("\"{}\"", self.rate_pack)); args.push("--payment-thresholds".to_string()); args.push(format!("\"{}\"", self.payment_thresholds)); - args.push("--gas-price".to_string()); if let Some(price) = self.gas_price_opt { + args.push("--gas-price".to_string()); args.push(price.to_string()); } if let EarningWalletInfo::Address(ref address) = self.earning_wallet_info { diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 4737868d0..023855a92 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -172,9 +172,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { // Assuming all Nodes rely on the same set of payment thresholds let cons_node_initial_service_fee_balance_minor = (owed_to_serv_node_2_minor + owed_to_serv_node_3_minor) - - gwei_to_wei::(20_000_000); - let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor - - (owed_to_serv_node_1_minor + owed_to_serv_node_2_minor + owed_to_serv_node_3_minor); + - gwei_to_wei::(40_000_000); let test_global_config = TestInputsOutputsConfig { ui_ports_opt: Some(Ports { consuming_node: consuming_node_ui_port, @@ -184,7 +182,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }), // Should be enough only for two payments therefore the least significant one will // need to go away - cons_node_initial_transaction_fee_balance_minor_opt: Some(6_000_000), + cons_node_initial_transaction_fee_balance_minor_opt: Some(gwei_to_wei::<_, u64>(8_000_000)), cons_node_initial_service_fee_balance_minor, debts_config: Either::Right(FullySpecifiedSimulatedDebts { // This account will be the least significant and be eliminated for the reasons @@ -210,11 +208,14 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }), payment_thresholds_all_nodes: payment_thresholds, cons_node_transaction_fee_agreed_unit_price_opt: Some(60), - exp_final_cons_node_transaction_fee_balance_minor: 37_514_000_000_000, - exp_final_cons_node_service_fee_balance_minor, - exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, - exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor, - exp_final_service_fee_balance_serv_node_3_minor: owed_to_serv_node_3_minor, + exp_final_cons_node_transaction_fee_balance_minor: 2_601_680_000_000_000, + exp_final_cons_node_service_fee_balance_minor: 0, // Because the algorithm is designed to + // exhaust the wallet till the last drop + exp_final_service_fee_balance_serv_node_1_minor: 0, // This account had to be dropped so received no money + exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor, // This account was granted with + // the full size as the higher age had this effect + exp_final_service_fee_balance_serv_node_3_minor: owed_to_serv_node_3_minor + - gwei_to_wei::(40_000_000), }; let process_scan_request_to_node = From 873a189051f177acba745ea6510e0418751d9ee8 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:07:50 +0800 Subject: [PATCH 108/250] GH-711: ganache-cli build script for two platforms --- multinode_integration_tests/ci/all.sh | 2 +- .../docker/blockchain/amd64_linux/Dockerfile | 9 ++++++ .../blockchain/amd64_linux/entrypoint.sh | 17 +++++++++++ .../blockchain/{ => arm64_linux}/Dockerfile | 6 +--- .../blockchain/arm64_linux/entrypoint.sh | 18 ++++++++++++ .../docker/blockchain/build.sh | 14 +++++++++- .../docker/blockchain/entrypoint.sh | 28 ------------------- 7 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile create mode 100755 multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh rename multinode_integration_tests/docker/blockchain/{ => arm64_linux}/Dockerfile (55%) create mode 100755 multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh delete mode 100755 multinode_integration_tests/docker/blockchain/entrypoint.sh diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index cce56ab55..6ce38689c 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -46,5 +46,5 @@ popd pushd "$CI_DIR/.." export RUSTFLAGS="-D warnings -Anon-snake-case" ci/lint.sh -cargo test payments_were_adjusted_due_to_insufficient_balance --release -- --nocapture --test-threads=1 +cargo test --release -- --nocapture --test-threads=1 popd diff --git a/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile b/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile new file mode 100644 index 000000000..003557688 --- /dev/null +++ b/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile @@ -0,0 +1,9 @@ +# Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +FROM trufflesuite/ganache-cli:v6.7.0 + +ADD ./amd64_linux/entrypoint.sh /app/ + +EXPOSE 18545 + +ENTRYPOINT /app/entrypoint.sh diff --git a/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh new file mode 100755 index 000000000..d9f99800c --- /dev/null +++ b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# All ordinal wallets begin with zero balances. Except the contract owner wallet as the only one populated with usable +# means. The means are meant to be redistributed from here to any accounts that will need it. Notice the argument +# --account ',' that assigns a certain initial balance. +# +# This same principle of wallets fed from this centric wallet at the test setup is followed by both the gas currency +# and the MASQ tokens. With those, there is practically no other option for their strong dependency on the blockchain +# smart contract that defines the entire supply is deployed to the contract owner's wallet. + +node /app/ganache-core.docker.cli.js \ + -p 18545 \ + --networkId 2 \ + --verbose \ + --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ + --defaultBalanceEther 0 \ + --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' \ No newline at end of file diff --git a/multinode_integration_tests/docker/blockchain/Dockerfile b/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile similarity index 55% rename from multinode_integration_tests/docker/blockchain/Dockerfile rename to multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile index 5c2375576..ac6c4bc32 100644 --- a/multinode_integration_tests/docker/blockchain/Dockerfile +++ b/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile @@ -1,12 +1,8 @@ # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -# linux/amd64 -#FROM trufflesuite/ganache-cli:v6.7.0 - -# linux/arm64 (for MacOs VMs, in case the upper one did not work for you) FROM --platform=linux/arm64 nutrina/ganache-cli:0.3 -ADD ./entrypoint.sh /app/ +ADD ./arm64_linux/entrypoint.sh /app/ EXPOSE 18545 diff --git a/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh b/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh new file mode 100755 index 000000000..5d97a1121 --- /dev/null +++ b/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# All ordinal wallets begin with zero balances. Except the contract owner wallet as the only one populated with usable +# means. The means are meant to be redistributed from here to any accounts that will need it. Notice the argument +# --account ',' that assigns a certain initial balance. +# +# This same principle of wallets fed from this centric wallet at the test setup is followed by both the gas currency +# and the MASQ tokens. With those, there is practically no other option for their strong dependency on the blockchain +# smart contract that defines the entire supply is deployed to the contract owner's wallet. + +ganache-cli \ + -h 0.0.0.0 \ + -p 18545 \ + --networkId 2 \ + --verbose \ + -m "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ + --defaultBalanceEther 0 \ + --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' diff --git a/multinode_integration_tests/docker/blockchain/build.sh b/multinode_integration_tests/docker/blockchain/build.sh index 8ecfa5025..e8cec53d0 100755 --- a/multinode_integration_tests/docker/blockchain/build.sh +++ b/multinode_integration_tests/docker/blockchain/build.sh @@ -1,4 +1,16 @@ #!/bin/bash -evx # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -docker build -t ganache-cli . +arch=`dpkg --print-architecture` + +if [[ $arch == "amd64" ]]; then + + echo "Building ganache-cli image for linux/amd64 architecture" + docker build -t ganache-cli . -f amd64_linux/Dockerfile + +elif [[ $arch == "arm64" ]]; then + + echo "Building ganache-cli image for linux/arm64 architecture" + docker build -t ganache-cli . -f arm64_linux/Dockerfile + +fi diff --git a/multinode_integration_tests/docker/blockchain/entrypoint.sh b/multinode_integration_tests/docker/blockchain/entrypoint.sh deleted file mode 100755 index fbeeb2199..000000000 --- a/multinode_integration_tests/docker/blockchain/entrypoint.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -# All ordinal wallets begin with a zero balance, -# except the contract owner wallet whose initial gas balance is supposed to be redistributed from -# to accounts that will need it ( --account ',' ) -# which follows the principle how accounts are fed by service fee amounts from this unique wallet, -# required by the state resulted from the smart contract deployment. - -# linux/amd64 -###################################### -#node /app/ganache-core.docker.cli.js \ -# -p 18545 \ -# --networkId 2 \ -# --verbose \ -# --mnemonic "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ -# --defaultBalanceEther 0 \ -# --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' - -# linux/arm64 (for MacOs VMs, in case the upper one did not work for you) -###################################### -ganache-cli \ - -h 0.0.0.0 \ - -p 18545 \ - --networkId 2 \ - --verbose \ - -m "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold" \ - --defaultBalanceEther 0 \ - --account '0xd4670b314ecb5e6b44b7fbe625ed746522c906316e66df31be64194ee6189188,10000000000000000000000' From aae40821a3e2485eb0610b749778c0faf84d193d Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:07:50 +0800 Subject: [PATCH 109/250] GH-711: unified the ganache-cli version for different CPU archs --- .../docker/blockchain/amd64_linux/Dockerfile | 2 +- .../docker/blockchain/arm64_linux/Dockerfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile b/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile index 003557688..5413c9c8d 100644 --- a/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile +++ b/multinode_integration_tests/docker/blockchain/amd64_linux/Dockerfile @@ -1,6 +1,6 @@ # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -FROM trufflesuite/ganache-cli:v6.7.0 +FROM trufflesuite/ganache-cli:v6.12.2 ADD ./amd64_linux/entrypoint.sh /app/ diff --git a/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile b/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile index ac6c4bc32..355aa3958 100644 --- a/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile +++ b/multinode_integration_tests/docker/blockchain/arm64_linux/Dockerfile @@ -1,5 +1,6 @@ # Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +# This is version v6.12.2 according to the official ganache versioning FROM --platform=linux/arm64 nutrina/ganache-cli:0.3 ADD ./arm64_linux/entrypoint.sh /app/ From e0f14b943718b7296279472e27c1003445a66f6a Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:38:30 +0800 Subject: [PATCH 110/250] GH-711: enhancing debug messages --- .../tests/verify_bill_payment.rs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 023855a92..52c472694 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -46,15 +46,14 @@ use web3::Web3; #[test] fn verify_bill_payment() { - // Note: besides the main objectives of this test, it relies on (and so - // it proves) the premise that each Node, when reaching its full connectivity - // (able to make a route), activates its accountancy module whereas it also - // begins the first cycle of the scanners immediately. That's why it might - // not be wise to take out the passages with starting a bunch of Nodes just - // in order to meet the condition even though the test could be rewritten - // into a simpler one by using directly the `scans` command from a UI through - // which we would achieve the same with less PCU work an in a shorter time. - // See the second test in this file. + // Note: besides the main objectives of this test, it relies on (and so it proves) + // the premise each Node, when reaching its full connectivity, becoming able to make a route, + // activates its accountancy module whereas it also unleashes the first cycle of the scanners + // immediately. That's why a consideration has been made not to take out the passages with + // intensive startups of bunch of Nodes, with the only reason to fulfill the above discussed + // condition, even though the test could be rewritten simpler using directly the `scans` + // command from a UI, with less PCU work and in a shorter time. (Such approach is implemented + // for another test in this file.) let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_500_000, debt_threshold_gwei: 1_000_000_000, @@ -370,7 +369,10 @@ fn transfer_service_fee_amount_to_address( .send_raw_transaction(signed_tx.raw_transaction) .wait() { - Ok(tx_hash) => eprintln!("Transaction {} was sent", tx_hash), + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of MASQ was sent from wallet {:?} to {:?}", + tx_hash, amount_minor, from_wallet, to_wallet + ), Err(e) => panic!("Transaction for token transfer failed {:?}", e), } } @@ -419,25 +421,28 @@ fn transfer_transaction_fee_amount_to_address( Ok(was_successful) => { if was_successful { eprintln!( - "Account {} unlocked for a single transaction", + "Account {:?} unlocked for a single transaction", from_wallet.address() ) } else { panic!( - "Couldn't unlock account {} for the purpose of signing the next transaction", + "Couldn't unlock account {:?} for the purpose of signing the next transaction", from_wallet.address() ) } } Err(e) => panic!( - "Attempt to unlock account {} failed at {:?}", + "Attempt to unlock account {:?} failed at {:?}", from_wallet.address(), e ), } match web3.eth().send_transaction(tx).wait() { - Ok(tx_hash) => eprintln!("Transaction {} was sent", tx_hash), + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", + tx_hash, amount_minor, from_wallet, to_wallet + ), Err(e) => panic!("Transaction for token transfer failed {:?}", e), } } From d87108c13d38202bb29999035af0e00456d1778b Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:00:15 +0800 Subject: [PATCH 111/250] GH-711: improvements in verifier, diagnostics and a decent test added for an overflow case --- .../payment_adjuster/adjustment_runners.rs | 31 ++--- .../payment_adjuster/diagnostics.rs | 32 ++++-- .../miscellaneous/helper_functions.rs | 5 +- node/src/accountant/payment_adjuster/mod.rs | 106 +++++++++++++----- .../payment_adjuster/possibility_verifier.rs | 32 +++--- 5 files changed, 137 insertions(+), 69 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 8d27e6536..95c040c71 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -8,23 +8,24 @@ use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterR use itertools::Either; use std::vec; -// There are just two runners. Different by the required adjustment, -// either capable of adjusting by the transaction fee and also the service fee, -// or only by the transaction fee. The idea is that only in the initial iteration -// of the possible recursion the adjustment by the transaction fee could be performed. -// In any of those next iterations this feature becomes useless. -// The more featured one also allows to short-circuit for an adjustment with no need -// to adjust accounts by the service fee, therefore each runner can return slightly -// different data types, even though the resulting type of the service fee adjustment -// is always the same. +// There are just two runners. Different by the adjustment they can perform, either adjusting by +// both the transaction fee and service fee, or exclusively by the transaction fee. The idea is +// that the adjustment by the transaction fee may ever appear in the initial iteration of +// the recursion. In any of the next iterations, if it proceed that far, this feature would be +// staying around useless. Therefor the runner with more features is used only at the beginning. +// Its speciality is that it allows also to short-circuit the weights computation for accounts, +// because it can detect that after some dropped accounts due to the transaction fee scarcity +// another adjustment, by the service fee, is not needed and therefore there is no point in going +// through any extra assessment. Mostly for the things just described each runner provides +// a different result type. pub trait AdjustmentRunner { type ReturnType; - // This special method: - // a) helps with writing tests aimed at edge cases, - // b) avoids performing an unnecessary computation for an obvious result, - // c) makes the condition in the initial check for adjustment possibility achievable - // in its pureness + // This specialized method: + // a) helps with writing tests that target edge cases, + // b) allows to avoid performing unnecessary computation for an evident result, + // c) the initial check for adjustment possibility is built upon a condition that can be + // realized in its pureness by having this fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, @@ -64,7 +65,7 @@ impl AdjustmentRunner for TransactionAndServiceFeeRunner { ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { - return payment_adjuster.begin_adjustment_by_transaction_fee( + return payment_adjuster.begin_by_adjustment_by_transaction_fee( criteria_and_accounts_in_descending_order, limit, ) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index cf8e374ce..1c3ad50b2 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,5 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; @@ -8,18 +9,26 @@ pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; #[macro_export] macro_rules! diagnostics { - ($description: literal, $($arg: tt)*) => { - diagnostics(None::String>, $description, || format!($($arg)*)) + // Display a brief description and values from a collection + ($description: literal, $debuggable_collection: expr) => { + collection_diagnostics($description, $debuggable_collection) }; - ($wallet_ref: expr, $description: expr, $($arg: tt)*) => { + // Display a brief description and formatted literal with arguments + ($description: literal, $($formatted_values: tt)*) => { + diagnostics(None::String>, $description, || format!($($formatted_values)*)) + }; + // Display an account by wallet address, brief description and formatted literal with arguments + ($wallet_ref: expr, $description: expr, $($formatted_values: tt)*) => { diagnostics( Some(||$wallet_ref.to_string()), $description, - || format!($($arg)*) + || format!($($formatted_values)*) ) }; } +// Intended to be used through the overloaded macro diagnostics!() for better clearness +// and differentiation from the primary functionality pub fn diagnostics(subject_renderer_opt: Option, description: &str, value_renderer: F2) where F1: Fn() -> String, @@ -36,12 +45,14 @@ where subject, description, value_renderer(), - subject_column_length = 42, + subject_column_length = WALLET_ADDRESS_LENGTH, length = DIAGNOSTICS_MIDDLE_COLUMN_WIDTH ) } } +// Intended to be used through the overloaded macro diagnostics!() for better clearness +// and differentiation from the primary functionality pub fn collection_diagnostics(label: &str, accounts: &[D]) { if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { eprintln!("{}", label); @@ -166,15 +177,16 @@ pub mod formulas_progressive_characteristics { use std::sync::{Mutex, Once}; use thousands::Separable; - // Only for debugging; in order to see the characteristic values of distinct parameter + // For debugging and tuning up purposes. It lets you see the curve of calculated criterion + // in dependence to different values of a distinct parameter pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = false; - // Preserve the 'static' keyword + // You must preserve the 'static' keyword static SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER: Mutex> = Mutex::new(vec![]); - // Preserve the 'static' keyword + // You must preserve the 'static' keyword // - // The singleton ensures that we print the characteristics always only once, no matter how many - // tests are running + // The singleton ensures that the characteristics are always displayed only once, no matter + // how many tests requested static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); pub struct DiagnosticsAxisX { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index cfff55264..49047ceed 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -110,9 +110,8 @@ pub fn find_largest_nominated_account<'a>( ) { Ordering::Less => largest_so_far, Ordering::Greater => current, - Ordering::Equal => - // Greater value means younger - { + Ordering::Equal => { + // Greater value means younger if current.original_account.last_paid_timestamp > largest_so_far.original_account.last_paid_timestamp { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 7784f8066..bd0b04d11 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -32,7 +32,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_and_leave_naked_affordable_accounts, keep_only_transaction_fee_affordable_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; -use crate::accountant::payment_adjuster::possibility_verifier::MasqAdjustmentPossibilityVerifier; +use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -154,47 +154,65 @@ impl PaymentAdjusterReal { fn determine_transaction_count_limit_by_transaction_fee( agent: &dyn BlockchainAgent, - number_of_accounts: usize, + number_of_qualified_accounts: usize, logger: &Logger, ) -> Result, PaymentAdjusterError> { - let per_transaction_requirement_minor = - agent.estimated_transaction_fee_per_transaction_minor(); + fn max_possible_tx_count( + cw_transaction_fee_balance_minor: U256, + tx_fee_requirement_per_tx_minor: u128, + ) -> u128 { + let max_possible_tx_count_u256 = + cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor); + u128::try_from(max_possible_tx_count_u256).unwrap_or_else(|e| { + panic!( + "Transaction fee balance {} wei in the consuming wallet cases panic given \ + estimated transaction fee per tx {} wei and resulting ratio {}, that should fit in \ + u128, respectively: \"{}\"", + cw_transaction_fee_balance_minor, + tx_fee_requirement_per_tx_minor, + max_possible_tx_count_u256, + e + ) + }) + } let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); + let per_transaction_requirement_minor = + agent.estimated_transaction_fee_per_transaction_minor(); - let max_possible_tx_count = u128::try_from( - cw_transaction_fee_balance_minor / U256::from(per_transaction_requirement_minor), - ) - .expect("consuming wallet with too a big balance for the transaction fee"); + let max_possible_tx_count = max_possible_tx_count( + cw_transaction_fee_balance_minor, + per_transaction_requirement_minor, + ); - let (max_possible_tx_count_u16, required_tx_count_u16) = - Self::big_unsigned_integers_under_u16_ceiling( + let (max_tx_count_we_can_afford_u16, required_tx_count_u16) = + Self::u16_ceiling_for_accounts_to_be_processed_at_a_time( max_possible_tx_count, - number_of_accounts, + number_of_qualified_accounts, ); - if max_possible_tx_count_u16 == 0 { + if max_tx_count_we_can_afford_u16 == 0 { Err( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts, + number_of_accounts: number_of_qualified_accounts, per_transaction_requirement_minor, cw_transaction_fee_balance_minor, }, ) - } else if max_possible_tx_count_u16 >= required_tx_count_u16 { + } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { Ok(None) } else { log_insufficient_transaction_fee_balance( logger, required_tx_count_u16, cw_transaction_fee_balance_minor, - max_possible_tx_count_u16, + max_tx_count_we_can_afford_u16, ); - Ok(Some(max_possible_tx_count_u16)) + Ok(Some(max_tx_count_we_can_afford_u16)) } } - fn big_unsigned_integers_under_u16_ceiling( + fn u16_ceiling_for_accounts_to_be_processed_at_a_time( max_possible_tx_count: u128, number_of_accounts: usize, ) -> (u16, u16) { @@ -225,8 +243,11 @@ impl PaymentAdjusterReal { if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { - MasqAdjustmentPossibilityVerifier {} - .verify_adjustment_possibility(&qualified_payables, cw_service_fee_balance_minor)?; + TransactionFeeAdjustmentPossibilityVerifier {} + .verify_lowest_detectable_adjustment_possibility( + &qualified_payables, + cw_service_fee_balance_minor, + )?; log_adjustment_by_service_fee_is_required( logger, @@ -267,6 +288,7 @@ impl PaymentAdjusterReal { qualified_accounts, TransactionAndServiceFeeRunner {}, )?; + match accounts { Either::Left(non_exhausted_accounts) => { let affordable_accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( @@ -287,16 +309,15 @@ impl PaymentAdjusterReal { where A: AdjustmentRunner, { - collection_diagnostics( + diagnostics!( "\nUNRESOLVED QUALIFIED ACCOUNTS:", - &unresolved_qualified_accounts, + &unresolved_qualified_accounts ); if unresolved_qualified_accounts.len() == 1 { return adjustment_runner .adjust_last_one(self, unresolved_qualified_accounts.remove(0)); } - let accounts_with_individual_criteria_sorted = self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); @@ -306,7 +327,7 @@ impl PaymentAdjusterReal { adjustment_runner.adjust_multiple(self, accounts_with_individual_criteria_sorted) } - fn begin_adjustment_by_transaction_fee( + fn begin_by_adjustment_by_transaction_fee( &mut self, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, already_known_affordable_transaction_count: u16, @@ -373,7 +394,7 @@ impl PaymentAdjusterReal { let merged: Vec = here_decided_iter.chain(downstream_decided_iter).collect(); - collection_diagnostics("\nFINAL ADJUSTED ACCOUNTS:", &merged); + diagnostics!("\nFINAL ADJUSTED ACCOUNTS:", &merged); merged } @@ -695,6 +716,7 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; + use std::panic::{AssertUnwindSafe, catch_unwind}; use thousands::Separable; use web3::types::U256; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -946,6 +968,38 @@ mod tests { } } + #[test] + fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() + { + let deadly_ratio = U256::from(u128::MAX) + 1; + let estimated_transaction_fee_per_one_tx_minor = 123_456_789; + let critical_wallet_balance = + deadly_ratio * U256::from(estimated_transaction_fee_per_one_tx_minor); + let blockchain_agent = BlockchainAgentMock::default() + .estimated_transaction_fee_per_transaction_minor_result( + estimated_transaction_fee_per_one_tx_minor, + ) + .transaction_fee_balance_minor_result(critical_wallet_balance); + + let panic_err = catch_unwind(AssertUnwindSafe(|| { + PaymentAdjusterReal::determine_transaction_count_limit_by_transaction_fee( + &blockchain_agent, + 123, + &Logger::new("test"), + ) + })) + .unwrap_err(); + + let err_msg = panic_err.downcast_ref::().unwrap(); + let expected_panic = format!( + "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ + transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ + respectively: \"integer overflow when casting to u128\"", + critical_wallet_balance, estimated_transaction_fee_per_one_tx_minor, deadly_ratio + ); + assert_eq!(err_msg, &expected_panic) + } + #[test] fn there_is_u16_ceiling_for_possible_tx_count() { let result = [-3_i8, -1, 0, 1, 10] @@ -953,7 +1007,7 @@ mod tests { .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) .map(|max_possible_tx_count| { let (doable_txs_count, _) = - PaymentAdjusterReal::big_unsigned_integers_under_u16_ceiling( + PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( max_possible_tx_count, 123, ); @@ -974,7 +1028,7 @@ mod tests { .map(|correction| plus_minus_correction_of_u16_max(correction)) .map(|required_tx_count_usize| { let (_, required_tx_count) = - PaymentAdjusterReal::big_unsigned_integers_under_u16_ceiling( + PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( 123, required_tx_count_usize, ); diff --git a/node/src/accountant/payment_adjuster/possibility_verifier.rs b/node/src/accountant/payment_adjuster/possibility_verifier.rs index 3f01c15d8..65c3c586e 100644 --- a/node/src/accountant/payment_adjuster/possibility_verifier.rs +++ b/node/src/accountant/payment_adjuster/possibility_verifier.rs @@ -7,19 +7,17 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ use crate::accountant::payment_adjuster::PaymentAdjusterError; use itertools::Itertools; -pub struct MasqAdjustmentPossibilityVerifier {} +pub struct TransactionFeeAdjustmentPossibilityVerifier {} -impl MasqAdjustmentPossibilityVerifier { - pub fn verify_adjustment_possibility( +impl TransactionFeeAdjustmentPossibilityVerifier { + // We cannot do much in this area, only step in if the balance can be zero or nearly zero by + // assumption we make about the smallest debt in the set and the disqualification limit applied + // on it. If so, we don't want to bother payment adjuster and so we will abort instead. + pub fn verify_lowest_detectable_adjustment_possibility( &self, accounts: &[&PayableAccount], cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - // The idea stands as the adjustment algorithm will have to proceed in each iteration by - // eliminating the biggest account there as it always finds an account to disqualify, - // reaching out the smallest one eventually; if the smallest one reduced by - // the disqualification margin turns out to be still possibly paid out we can be sure - // that this Node is going to realize at least one blockchain transaction let sorted = accounts .iter() .sorted_by(|account_a, account_b| { @@ -49,7 +47,7 @@ impl MasqAdjustmentPossibilityVerifier { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::possibility_verifier::MasqAdjustmentPossibilityVerifier; + use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_payable_account; @@ -59,10 +57,12 @@ mod tests { ) { let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = MasqAdjustmentPossibilityVerifier {}; + let subject = TransactionFeeAdjustmentPossibilityVerifier {}; - let result = - subject.verify_adjustment_possibility(&accounts_in_expected_format, cw_masq_balance); + let result = subject.verify_lowest_detectable_adjustment_possibility( + &accounts_in_expected_format, + cw_masq_balance, + ); assert_eq!(result, Ok(())) } @@ -104,10 +104,12 @@ mod tests { let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = MasqAdjustmentPossibilityVerifier {}; + let subject = TransactionFeeAdjustmentPossibilityVerifier {}; - let result = - subject.verify_adjustment_possibility(&accounts_in_expected_format, cw_masq_balance); + let result = subject.verify_lowest_detectable_adjustment_possibility( + &accounts_in_expected_format, + cw_masq_balance, + ); assert_eq!( result, From 2a9bb72349b252691de63747e87c600ee4797861 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 7 Jan 2024 13:23:34 +0800 Subject: [PATCH 112/250] GH-711: little refactorings --- node/src/accountant/mod.rs | 9 +- .../payment_adjuster/adjustment_runners.rs | 35 ++- .../criteria_calculators/mod.rs | 22 +- .../payment_adjuster/diagnostics.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 2 +- .../miscellaneous/data_structures.rs | 5 + .../miscellaneous/helper_functions.rs | 182 +++++++------- node/src/accountant/payment_adjuster/mod.rs | 226 +++++++++--------- 8 files changed, 244 insertions(+), 239 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 134473fb5..4a68b742c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1518,7 +1518,7 @@ mod tests { response_skeleton_opt: Some(response_skeleton), }; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) + .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(Ok(payments_instructions)); let payable_scanner = PayableScannerBuilder::new() @@ -1537,7 +1537,10 @@ mod tests { let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); let (actual_prepared_adjustment, captured_now) = adjust_payments_params.remove(0); - assert_eq!(actual_prepared_adjustment.adjustment, Adjustment::MasqToken); + assert_eq!( + actual_prepared_adjustment.adjustment, + Adjustment::ByServiceFee + ); assert_eq!( actual_prepared_adjustment.qualified_payables, unadjusted_accounts @@ -1670,7 +1673,7 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::MasqToken))) + .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsUnexpectedlyEliminated)); test_handling_payment_adjuster_error(test_name, payment_adjuster); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 95c040c71..19d1708a6 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -35,7 +35,7 @@ pub trait AdjustmentRunner { fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - accounts_with_individual_criteria_sorted: Vec<(u128, PayableAccount)>, + weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType; } @@ -52,21 +52,19 @@ impl AdjustmentRunner for TransactionAndServiceFeeRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - Ok(Either::Left(empty_or_single_element(adjust_last_one( - payment_adjuster, - last_account, - )))) + let element_opt = adjust_last_one(payment_adjuster, last_account); + Ok(Either::Left(empty_or_single_element_vector(element_opt))) } fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { - return payment_adjuster.begin_by_adjustment_by_transaction_fee( - criteria_and_accounts_in_descending_order, + return payment_adjuster.begin_with_adjustment_by_transaction_fee( + weights_and_accounts_in_descending_order, limit, ) } @@ -75,7 +73,7 @@ impl AdjustmentRunner for TransactionAndServiceFeeRunner { Ok(Either::Left( payment_adjuster - .propose_possible_adjustment_recursively(criteria_and_accounts_in_descending_order), + .propose_possible_adjustment_recursively(weights_and_accounts_in_descending_order), )) } } @@ -90,16 +88,17 @@ impl AdjustmentRunner for ServiceFeeOnlyRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - empty_or_single_element(adjust_last_one(payment_adjuster, last_account)) + let element_opt = adjust_last_one(payment_adjuster, last_account); + empty_or_single_element_vector(element_opt) } fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, ) -> Self::ReturnType { payment_adjuster - .propose_possible_adjustment_recursively(criteria_and_accounts_in_descending_order) + .propose_possible_adjustment_recursively(weights_and_accounts_in_descending_order) } } @@ -134,7 +133,7 @@ fn adjust_last_one( } } -fn empty_or_single_element( +fn empty_or_single_element_vector( adjusted_account_opt: Option, ) -> Vec { match adjusted_account_opt { @@ -147,7 +146,7 @@ fn empty_or_single_element( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one, empty_or_single_element, AdjustmentRunner, ServiceFeeOnlyRunner, + adjust_last_one, empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyRunner, TransactionAndServiceFeeRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; @@ -161,7 +160,7 @@ mod tests { use std::time::{Duration, SystemTime}; fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { - let adjustment = Adjustment::MasqToken; + let adjustment = Adjustment::ByServiceFee; let mut payment_adjuster = PaymentAdjusterReal::new(); payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); payment_adjuster @@ -285,7 +284,7 @@ mod tests { let cw_balance = 9_000_000; payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = ServiceFeeOnlyRunner {}; - let criteria_and_accounts = payment_adjuster.calculate_criteria_sums_for_accounts(accounts); + let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(accounts); let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); @@ -300,7 +299,7 @@ mod tests { #[test] fn empty_or_single_element_for_none() { - let result = empty_or_single_element(None); + let result = empty_or_single_element_vector(None); assert_eq!(result, vec![]) } @@ -311,7 +310,7 @@ mod tests { original_account: make_payable_account(123), proposed_adjusted_balance: 123_456_789, }; - let result = empty_or_single_element(Some(account_info.clone())); + let result = empty_or_single_element_vector(Some(account_info.clone())); assert_eq!(result, vec![account_info]) } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index cdd218984..e6e218deb 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -32,22 +32,22 @@ pub trait CriterionCalculator: // implementations), and expose it to outside fn formula(&self) -> &dyn Fn(Self::Input) -> u128; - fn calculate_and_add_to_criteria_sum( + fn calculate_and_add_to_weight( &self, - (criteria_sum, account): (u128, PayableAccount), + (weight, account): (u128, PayableAccount), ) -> (u128, PayableAccount) where ::Input: Debug, { #[cfg(test)] - self.formula_characteristics_diagnostics(); + self.diagnostics_of_formula_characteristics(); let criterion: u128 = self.formula()((&account).into()); - let new_sum = criteria_sum + criterion; + let new_weight = weight + criterion; - calculator_local_diagnostics(&account.wallet, self, criterion, new_sum); + calculator_local_diagnostics(&account.wallet, self, criterion, new_weight); - (criteria_sum + criterion, account) + (new_weight, account) } #[cfg(test)] @@ -60,7 +60,7 @@ pub trait CriterionCalculator: .take() } #[cfg(test)] - fn formula_characteristics_diagnostics(&self) + fn diagnostics_of_formula_characteristics(&self) where ::Input: Debug, { @@ -74,7 +74,7 @@ pub trait CriterionCalculator: } } -pub trait CriteriaCalculators { +pub trait CriteriaCalculatorIterators { fn calculate_age_criteria( self, payment_adjuster: &PaymentAdjusterReal, @@ -87,7 +87,7 @@ pub trait CriteriaCalculators { Self: Iterator + Sized; } -impl CriteriaCalculators for I +impl CriteriaCalculatorIterators for I where I: Iterator, { @@ -117,8 +117,8 @@ macro_rules! standard_impls_for_calculator { type Item = (u128, PayableAccount); fn next(&mut self) -> Option { - self.iter.next().map(|criteria_sum_and_account| { - self.calculate_and_add_to_criteria_sum(criteria_sum_and_account.into()) + self.iter.next().map(|weight_and_account| { + self.calculate_and_add_to_weight(weight_and_account.into()) }) } } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 1c3ad50b2..2e0a7a614 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -126,7 +126,7 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn non_finalized_adjusted_accounts_diagnostics( + pub fn proposed_adjusted_balance_diagnostics( account: &PayableAccount, proposed_adjusted_balance: u128, ) { diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 8267d3dac..f534d43d9 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -95,7 +95,7 @@ pub fn format_brief_adjustment_summary( .join("\n") } -pub fn before_and_after_debug_msg( +pub fn accounts_before_and_after_debug( original_account_balances_mapped: HashMap, adjusted_accounts: &[PayableAccount], ) -> String { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index d1c080c1d..cea5767a2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -47,3 +47,8 @@ pub struct PercentageAccountInsignificance { pub multiplier: u128, pub divisor: u128, } + +pub struct TransactionCountsWithin16bits { + pub affordable: u16, + pub required: u16, +} diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 49047ceed..1be3bbb92 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -18,6 +18,7 @@ use masq_lib::logger::Logger; use std::cmp::Ordering; use std::iter::successors; use std::ops::Not; +use web3::types::U256; const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; @@ -36,37 +37,40 @@ where collection.iter().map(arranger).sum::().into() } -pub fn criteria_total(accounts_with_individual_criteria: &[(u128, PayableAccount)]) -> u128 { - sum_as(accounts_with_individual_criteria, |(criteria, _)| *criteria) +pub fn weights_total(weights_and_accounts: &[(u128, PayableAccount)]) -> u128 { + sum_as(weights_and_accounts, |(weight, _)| *weight) } -pub fn keep_only_transaction_fee_affordable_accounts_and_drop_the_rest( - criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, +pub fn drop_accounts_that_cannot_be_afforded_due_to_service_fee( + weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, affordable_transaction_count: u16, ) -> Vec<(u128, PayableAccount)> { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", affordable_transaction_count, - criteria_and_accounts_in_descending_order.len() + weights_and_accounts_in_descending_order.len() ); - criteria_and_accounts_in_descending_order + weights_and_accounts_in_descending_order .into_iter() .take(affordable_transaction_count as usize) .collect() } -pub fn compute_fractional_numbers_preventing_mul_coefficient( +// TODO U256 possible + test for extreme input fed?? +pub fn compute_mul_coefficient_preventing_fractional_numbers( cw_masq_balance_minor: u128, - account_criteria_sum: u128, -) -> u128 { - let criteria_sum_digits_count = log_10(account_criteria_sum); + account_weight: u128, +) -> U256 { + let weight_digits_count = log_10(account_weight); let cw_balance_digits_count = log_10(cw_masq_balance_minor); - let positive_difference = criteria_sum_digits_count.saturating_sub(cw_balance_digits_count); + let positive_difference = weight_digits_count.saturating_sub(cw_balance_digits_count); let safe_mul_coefficient = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; - 10_u128 - .checked_pow(safe_mul_coefficient as u32) - .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) + U256::from(10) + .checked_pow(safe_mul_coefficient.into()) + // .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) + // TODO fix me in the test + .unwrap_or_else(|| U256::MAX) } pub fn possibly_outweighed_accounts_fold_guts( @@ -278,20 +282,18 @@ impl CwExhaustingStatus { } } -pub fn sort_in_descendant_order_by_criteria_sums( +pub fn sort_in_descendant_order_by_weights( unsorted: impl Iterator, ) -> Vec<(u128, PayableAccount)> { unsorted - .sorted_by(|(criteria_sum_a, _), (criteria_sum_b, _)| { - Ord::cmp(criteria_sum_b, criteria_sum_a) - }) + .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) .collect() } -pub fn drop_criteria_and_leave_naked_affordable_accounts( - criteria_and_accounts: Vec<(u128, PayableAccount)>, +pub fn isolate_accounts_from_weights( + weights_and_accounts: Vec<(u128, PayableAccount)>, ) -> Vec { - criteria_and_accounts + weights_and_accounts .into_iter() .map(|(_, account)| account) .collect() @@ -378,11 +380,11 @@ mod tests { AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - calculate_disqualification_edge, compute_fractional_numbers_preventing_mul_coefficient, - criteria_total, exhaust_cw_till_the_last_drop, find_largest_nominated_account, + calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, + exhaust_cw_till_the_last_drop, find_largest_nominated_account, list_accounts_nominated_for_disqualification, log_10, log_2, possibly_outweighed_accounts_fold_guts, - try_finding_an_account_to_disqualify_in_this_iteration, CwExhaustingStatus, + try_finding_an_account_to_disqualify_in_this_iteration, weights_total, CwExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_IN_U128, }; @@ -395,6 +397,7 @@ mod tests { use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use std::time::{Duration, SystemTime}; + use web3::types::U256; #[test] fn constants_are_correct() { @@ -447,7 +450,7 @@ mod tests { #[test] fn multiplication_coefficient_can_give_numbers_preventing_fractional_numbers() { - let final_criteria_sum = 5_000_000_000_000_u128; + let final_weight = 5_000_000_000_000_u128; let cw_balances = vec![ 222_222_222_222_u128, 100_000, @@ -461,29 +464,27 @@ mod tests { .clone() .into_iter() .map(|cw_balance| { - compute_fractional_numbers_preventing_mul_coefficient( - cw_balance, - final_criteria_sum, - ) + compute_mul_coefficient_preventing_fractional_numbers(cw_balance, final_weight) }) - .collect::>(); - - assert_eq!( - result, - vec![ - 1_000_000_000, - 1_000_000_000_000_000, - 1_000_000_000_000, - 100_000_000, - 100_000_000, - 100_000_000 - ] - ) + .collect::>(); + + let expected_result = vec![ + 1_000_000_000_u128, + 1_000_000_000_000_000, + 1_000_000_000_000, + 100_000_000, + 100_000_000, + 100_000_000, + ] + .into_iter() + .map(U256::from) + .collect::>(); + assert_eq!(result, expected_result) } #[test] fn multiplication_coefficient_extreme_feeding_and_safety_ceiling() { - // We cannot say by heart which of the evaluated criteria sums from + // We cannot say by heart which of the evaluated weights from // these parameters below will be bigger than another and therefore // we cannot line them up in an order let accounts_as_months_and_balances = vec![ @@ -495,81 +496,80 @@ mod tests { (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), ]; - let ( - accounts_with_their_criteria_sums, - reserved_initial_accounts_order_according_to_wallets, - ) = get_extreme_criteria_and_initial_accounts_order(accounts_as_months_and_balances); + let (accounts_with_their_weights, reserved_initial_accounts_order_according_to_wallets) = + get_extreme_weights_and_initial_accounts_order(accounts_as_months_and_balances); let cw_balance_in_minor = 1; - let results = accounts_with_their_criteria_sums + let results = accounts_with_their_weights .into_iter() - .map(|(criteria_sum, account)| { + .map(|(weight, account)| { // Scenario simplification: we assume there is always just one account to process in a time - let computed_coefficient = compute_fractional_numbers_preventing_mul_coefficient( + let computed_coefficient = compute_mul_coefficient_preventing_fractional_numbers( cw_balance_in_minor, - criteria_sum, + weight, ); - (computed_coefficient, account.wallet, criteria_sum) + (computed_coefficient, account.wallet, weight) }) - .collect::>(); + .collect::>(); let reserved_initial_accounts_order_according_to_wallets_iter = reserved_initial_accounts_order_according_to_wallets .iter() .enumerate(); - let mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs = results + let mul_coefficients_and_weights_in_the_same_order_as_original_inputs = results .into_iter() - .map( - |(computed_coefficient, account_wallet, account_criteria_sum)| { - let (idx, _) = reserved_initial_accounts_order_according_to_wallets_iter - .clone() - .find(|(_, wallet_ordered)| wallet_ordered == &&account_wallet) - .unwrap(); - (idx, computed_coefficient, account_criteria_sum) - }, - ) + .map(|(computed_coefficient, account_wallet, account_weight)| { + let (idx, _) = reserved_initial_accounts_order_according_to_wallets_iter + .clone() + .find(|(_, wallet_ordered)| wallet_ordered == &&account_wallet) + .unwrap(); + (idx, computed_coefficient, account_weight) + }) .sorted_by(|(idx_a, _, _), (idx_b, _, _)| Ord::cmp(&idx_b, &idx_a)) - .map(|(_, coefficient, criteria_sum)| (coefficient, criteria_sum)) - .collect::>(); + .map(|(_, coefficient, weight)| (coefficient, weight)) + .collect::>(); let templates_for_coefficients = vec![ - 100000000000000000000000000000000000000, + 100000000000000000000000000000000000000_u128, 100000000000000000000000000000000000, 100000000000000000000000000000000000, 100000000000000000000000000000000, 10000000000000000000000000000000, 10000000000000000000000000000000, 100000000000000000000000000000000000, - ]; + ] + .into_iter() + .map(U256::from) + .collect::>(); // I was trying to write these assertions so that it wouldn't require us to rewrite // the expected values everytime someone pokes into the formulas. - check_relation_to_computed_criteria_sum_fairly_but_with_enough_benevolence( - &mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs, + check_relation_to_computed_weight_fairly_but_with_enough_benevolence( + &mul_coefficients_and_weights_in_the_same_order_as_original_inputs, ); compare_coefficients_to_templates( - &mul_coefficients_and_criteria_sums_in_the_same_order_as_original_inputs, + &mul_coefficients_and_weights_in_the_same_order_as_original_inputs, &templates_for_coefficients, ); } - fn check_relation_to_computed_criteria_sum_fairly_but_with_enough_benevolence( - output: &[(u128, u128)], + fn check_relation_to_computed_weight_fairly_but_with_enough_benevolence( + output: &[(U256, u128)], ) { - output.iter().for_each(|(coefficient, corresponding_criteria_sum)| { - let coefficient_num_decimal_length = log_10(*coefficient); - let criteria_sum_decimal_length = log_10(*corresponding_criteria_sum); - assert_eq!(coefficient_num_decimal_length, criteria_sum_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, + output.iter().for_each(|(coefficient, corresponding_weight)| { + let coefficient_num_decimal_length = log_10(coefficient.as_u128()); + let weight_decimal_length = log_10(*corresponding_weight); + assert_eq!(coefficient_num_decimal_length, weight_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, "coefficient with bad safety margin; should be {} but was {}, as one of this set {:?}", coefficient_num_decimal_length, - criteria_sum_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, + weight_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, output ); let expected_division_by_10_if_wrong = 10_u128.pow(coefficient_num_decimal_length as u32 - 1); - let experiment_result = corresponding_criteria_sum / 10; + let experiment_result = corresponding_weight / 10; match experiment_result == expected_division_by_10_if_wrong { false => (), - true => match corresponding_criteria_sum % 10 { - 0 => panic!("the criteria sum is a pure power of ten, too a suspicious result, \ + true => match corresponding_weight % 10 { + 0 => panic!("the weight is a pure power of ten, such a suspicious result, \ check it in {:?}", output), _ => () } @@ -577,7 +577,7 @@ mod tests { }) } - fn compare_coefficients_to_templates(outputs: &[(u128, u128)], templates: &[u128]) { + fn compare_coefficients_to_templates(outputs: &[(U256, u128)], templates: &[U256]) { assert_eq!( outputs.len(), templates.len(), @@ -758,13 +758,11 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(7_000)).unwrap(), pending_payable_opt: None, }; - let accounts_with_individual_criteria = subject - .calculate_criteria_sums_for_accounts(vec![account_1, account_2, account_3, account_4]); - let criteria_total = criteria_total(&accounts_with_individual_criteria); - let non_finalized_adjusted_accounts = subject.compute_adjusted_but_non_finalized_accounts( - accounts_with_individual_criteria, - criteria_total, - ); + let weights_and_accounts = subject + .calculate_weights_for_accounts(vec![account_1, account_2, account_3, account_4]); + let weights_total = weights_total(&weights_and_accounts); + let non_finalized_adjusted_accounts = subject + .compute_adjusted_but_non_finalized_accounts(weights_and_accounts, weights_total); let result = try_finding_an_account_to_disqualify_in_this_iteration( &non_finalized_adjusted_accounts, @@ -962,7 +960,7 @@ mod tests { assert_eq!(result, expected_disqualified_accounts) } - fn get_extreme_criteria_and_initial_accounts_order( + fn get_extreme_weights_and_initial_accounts_order( months_of_debt_and_balances: Vec<(usize, u128)>, ) -> (Vec<(u128, PayableAccount)>, Vec) { let now = SystemTime::now(); @@ -972,9 +970,9 @@ mod tests { .map(|account| account.wallet.clone()) .collect(); let subject = make_initialized_subject(now, None, None); - // The initial order is remembered because when the criteria are applied the collection the collection + // The initial order is remembered because when the weight are applied the collection the collection // also gets sorted and will not necessarily have to match the initial order - let criteria_and_accounts = subject.calculate_criteria_sums_for_accounts(accounts); - (criteria_and_accounts, wallets_in_order) + let weights_and_accounts = subject.calculate_weights_for_accounts(accounts); + (weights_and_accounts, wallets_in_order) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bd0b04d11..227443417 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -14,24 +14,21 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeRunner, ServiceFeeOnlyRunner, }; -use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculators}; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::non_finalized_adjusted_accounts_diagnostics; +use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculatorIterators}; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - before_and_after_debug_msg, log_adjustment_by_service_fee_is_required, + accounts_before_and_after_debug, log_adjustment_by_service_fee_is_required, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_fractional_numbers_preventing_mul_coefficient, criteria_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, drop_criteria_and_leave_naked_affordable_accounts, keep_only_transaction_fee_affordable_accounts_and_drop_the_rest, sort_in_descendant_order_by_criteria_sums, sum_as}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, TransactionCountsWithin16bits, ProposedAdjustmentResolution}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; use crate::diagnostics; use crate::masq_lib::utils::ExpectValue; @@ -47,6 +44,7 @@ use thousands::Separable; use web3::types::U256; #[cfg(test)] use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::render_formulas_characteristics_for_diagnostics_if_enabled; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::proposed_adjusted_balance_diagnostics; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -100,7 +98,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { service_fee_balance_minor, ) { Ok(false) => Ok(None), - Ok(true) => Ok(Some(Adjustment::MasqToken)), + Ok(true) => Ok(Some(Adjustment::ByServiceFee)), Err(e) => Err(e), } } @@ -118,14 +116,17 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.initialize_inner(initial_service_fee_balance_minor, required_adjustment, now); - let debug_info_opt = self.debug_info_opt(&qualified_payables); + let sketched_debug_info_opt = self.sketch_debug_info_opt(&qualified_payables); let affordable_accounts = self.run_adjustment(qualified_payables)?; debug!( self.logger, "{}", - before_and_after_debug_msg(debug_info_opt.expectv("debug info"), &affordable_accounts) + accounts_before_and_after_debug( + sketched_debug_info_opt.expectv("debug info"), + &affordable_accounts + ) ); Ok(OutboundPaymentsInstructions { @@ -185,11 +186,13 @@ impl PaymentAdjusterReal { per_transaction_requirement_minor, ); - let (max_tx_count_we_can_afford_u16, required_tx_count_u16) = - Self::u16_ceiling_for_accounts_to_be_processed_at_a_time( - max_possible_tx_count, - number_of_qualified_accounts, - ); + let detected_tx_counts = Self::u16_ceiling_for_accounts_to_be_processed_at_a_time( + max_possible_tx_count, + number_of_qualified_accounts, + ); + + let max_tx_count_we_can_afford_u16 = detected_tx_counts.affordable; + let required_tx_count_u16 = detected_tx_counts.required; if max_tx_count_we_can_afford_u16 == 0 { Err( @@ -215,11 +218,11 @@ impl PaymentAdjusterReal { fn u16_ceiling_for_accounts_to_be_processed_at_a_time( max_possible_tx_count: u128, number_of_accounts: usize, - ) -> (u16, u16) { - ( - u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), - u16::try_from(number_of_accounts).unwrap_or(u16::MAX), - ) + ) -> TransactionCountsWithin16bits { + TransactionCountsWithin16bits { + affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), + required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), + } } fn check_need_of_adjustment_by_service_fee( @@ -229,7 +232,7 @@ impl PaymentAdjusterReal { ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { Either::Left(accounts) => accounts.iter().collect(), - Either::Right(criteria_and_accounts) => criteria_and_accounts + Either::Right(weights_and_accounts) => weights_and_accounts .iter() .map(|(_, account)| account) .collect(), @@ -268,7 +271,7 @@ impl PaymentAdjusterReal { Adjustment::TransactionFeeInPriority { affordable_transaction_count, } => Some(affordable_transaction_count), - Adjustment::MasqToken => None, + Adjustment::ByServiceFee => None, }; let inner = PaymentAdjusterInnerReal::new( @@ -301,13 +304,13 @@ impl PaymentAdjusterReal { } } - fn calculate_criteria_and_propose_adjustments_recursively( + fn calculate_criteria_and_propose_adjustments_recursively( &mut self, mut unresolved_qualified_accounts: Vec, - adjustment_runner: A, - ) -> R + adjustment_runner: AR, + ) -> RT where - A: AdjustmentRunner, + AR: AdjustmentRunner, { diagnostics!( "\nUNRESOLVED QUALIFIED ACCOUNTS:", @@ -318,16 +321,15 @@ impl PaymentAdjusterReal { return adjustment_runner .adjust_last_one(self, unresolved_qualified_accounts.remove(0)); } - let accounts_with_individual_criteria_sorted = - self.calculate_criteria_sums_for_accounts(unresolved_qualified_accounts); + let weights_and_accounts_sorted = + self.calculate_weights_for_accounts(unresolved_qualified_accounts); #[cfg(test)] render_formulas_characteristics_for_diagnostics_if_enabled(); - - adjustment_runner.adjust_multiple(self, accounts_with_individual_criteria_sorted) + adjustment_runner.adjust_multiple(self, weights_and_accounts_sorted) } - fn begin_by_adjustment_by_transaction_fee( + fn begin_with_adjustment_by_transaction_fee( &mut self, criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, already_known_affordable_transaction_count: u16, @@ -336,7 +338,7 @@ impl PaymentAdjusterReal { PaymentAdjusterError, > { let accounts_with_criteria_affordable_by_transaction_fee = - keep_only_transaction_fee_affordable_accounts_and_drop_the_rest( + drop_accounts_that_cannot_be_afforded_due_to_service_fee( criteria_and_accounts_in_descending_order, already_known_affordable_transaction_count, ); @@ -363,7 +365,7 @@ impl PaymentAdjusterReal { Ok(Either::Left(adjustment_result_before_verification)) } false => { - let finalized_accounts = drop_criteria_and_leave_naked_affordable_accounts( + let finalized_accounts = isolate_accounts_from_weights( accounts_with_criteria_affordable_by_transaction_fee, ); Ok(Either::Right(finalized_accounts)) @@ -371,20 +373,20 @@ impl PaymentAdjusterReal { } } - fn calculate_criteria_sums_for_accounts( + fn calculate_weights_for_accounts( &self, accounts: Vec, ) -> Vec<(u128, PayableAccount)> { - let accounts_with_zero_criteria = Self::initialize_zero_criteria(accounts); - self.apply_criteria(accounts_with_zero_criteria) + let zero_weights_accounts = Self::initialize_zero_weights(accounts); + self.apply_criteria(zero_weights_accounts) } fn propose_possible_adjustment_recursively( &mut self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + weights_and_accounts: Vec<(u128, PayableAccount)>, ) -> Vec { let adjustment_iteration_result = - self.perform_masq_adjustment(accounts_with_individual_criteria); + self.perform_adjustment_by_service_fee(weights_and_accounts); let (here_decided_accounts, downstream_decided_accounts) = self.resolve_iteration_result(adjustment_iteration_result); @@ -441,40 +443,38 @@ impl PaymentAdjusterReal { } } - fn initialize_zero_criteria( + fn initialize_zero_weights( qualified_payables: Vec, ) -> impl Iterator { - fn only_zero_criteria_iterator(accounts_count: usize) -> impl Iterator { + fn zero_weights_iterator(accounts_count: usize) -> impl Iterator { let one_element = once(0_u128); let endlessly_repeated = one_element.into_iter().cycle(); endlessly_repeated.take(accounts_count) } let accounts_count = qualified_payables.len(); - let criteria_iterator = only_zero_criteria_iterator(accounts_count); - criteria_iterator.zip(qualified_payables.into_iter()) + let weights_iterator = zero_weights_iterator(accounts_count); + weights_iterator.zip(qualified_payables.into_iter()) } fn apply_criteria( &self, - accounts_with_zero_criteria: impl Iterator, + zero_weights_accounts: impl Iterator, ) -> Vec<(u128, PayableAccount)> { - let criteria_and_accounts = accounts_with_zero_criteria + let weights_and_accounts = zero_weights_accounts .calculate_age_criteria(self) .calculate_balance_criteria(); - sort_in_descendant_order_by_criteria_sums(criteria_and_accounts) + sort_in_descendant_order_by_weights(weights_and_accounts) } - fn perform_masq_adjustment( + fn perform_adjustment_by_service_fee( &self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, + weights_and_accounts: Vec<(u128, PayableAccount)>, ) -> AdjustmentIterationResult { - let criteria_total = criteria_total(&accounts_with_individual_criteria); - let non_finalized_adjusted_accounts = self.compute_adjusted_but_non_finalized_accounts( - accounts_with_individual_criteria, - criteria_total, - ); + let weights_total = weights_total(&weights_and_accounts); + let non_finalized_adjusted_accounts = + self.compute_adjusted_but_non_finalized_accounts(weights_and_accounts, weights_total); let still_unchecked_for_disqualified = match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { @@ -495,55 +495,56 @@ impl PaymentAdjusterReal { fn compute_adjusted_but_non_finalized_accounts( &self, - accounts_with_individual_criteria: Vec<(u128, PayableAccount)>, - criteria_total: u128, + weights_with_accounts: Vec<(u128, PayableAccount)>, + weights_total: u128, ) -> Vec { - let cw_masq_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let cpm_coeff = - compute_fractional_numbers_preventing_mul_coefficient(cw_masq_balance, criteria_total); - let multiplication_coeff_u256 = U256::from(cpm_coeff); - - let proportional_fragment_of_cw_balance = Self::compute_proportional_fragment( - cw_masq_balance, - criteria_total, - multiplication_coeff_u256, - ); + let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let turn_account_into_adjusted_account_before_finalization = - |(criteria_sum, account): (u128, PayableAccount)| { - let proposed_adjusted_balance = (U256::from(criteria_sum) - * proportional_fragment_of_cw_balance - / multiplication_coeff_u256) - .as_u128(); + let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( + cw_service_fee_balance, + weights_total, + ); - non_finalized_adjusted_accounts_diagnostics(&account, proposed_adjusted_balance); + let balanced_fragment_of_cw_balance = Self::compute_balanced_cw_fragment( + cw_service_fee_balance, + weights_total, + multiplication_coefficient, + ); - AdjustedAccountBeforeFinalization::new(account, proposed_adjusted_balance) - }; + let compute_proposed_adjusted_balance = |weight: u128| { + u128::try_from( + U256::from(weight) * balanced_fragment_of_cw_balance / multiplication_coefficient, + ) + .expect("mul coefficient computation worked, this must too") + }; - accounts_with_individual_criteria + weights_with_accounts .into_iter() - .map(turn_account_into_adjusted_account_before_finalization) + .map(|(weight, account)| (account, compute_proposed_adjusted_balance(weight))) + .map(|(account, proposed_adjusted_balance)| { + proposed_adjusted_balance_diagnostics(&account, proposed_adjusted_balance); + AdjustedAccountBeforeFinalization::new(account, proposed_adjusted_balance) + }) .collect() } - fn compute_proportional_fragment( - cw_masq_balance: u128, - criteria_total: u128, - multiplication_coeff: U256, + fn compute_balanced_cw_fragment( + cw_service_fee_balance: u128, + weights_total: u128, + multiplication_coefficient: U256, ) -> U256 { - let cw_masq_balance_u256 = U256::from(cw_masq_balance); - let criteria_total_u256 = U256::from(criteria_total); + let cw_service_fee_balance = U256::from(cw_service_fee_balance); + let weights_total = U256::from(weights_total); - cw_masq_balance_u256 - .checked_mul(multiplication_coeff) + cw_service_fee_balance + .checked_mul(multiplication_coefficient) .unwrap_or_else(|| { panic!( "mul overflow from {} * {}", - criteria_total_u256, multiplication_coeff + weights_total, multiplication_coefficient ) }) - .checked_div(criteria_total_u256) + .checked_div(weights_total) .expect("div overflow") } @@ -587,6 +588,7 @@ impl PaymentAdjusterReal { non_finalized_adjusted_accounts: Vec, ) -> Either, AdjustmentIterationResult> { let init = (vec![], vec![]); + let (outweighed, passing_through) = non_finalized_adjusted_accounts .into_iter() .fold(init, possibly_outweighed_accounts_fold_guts); @@ -621,7 +623,7 @@ impl PaymentAdjusterReal { ) } - fn debug_info_opt( + fn sketch_debug_info_opt( &self, qualified_payables: &[PayableAccount], ) -> Option> { @@ -636,7 +638,7 @@ impl PaymentAdjusterReal { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Adjustment { - MasqToken, + ByServiceFee, TransactionFeeInPriority { affordable_transaction_count: u16 }, } @@ -700,7 +702,7 @@ mod tests { use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::criteria_total; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, }; @@ -853,7 +855,7 @@ mod tests { let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); - assert_eq!(result, Ok(Some(Adjustment::MasqToken))); + assert_eq!(result, Ok(Some(Adjustment::ByServiceFee))); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,000,001 \ wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ \ @@ -1006,12 +1008,12 @@ mod tests { .into_iter() .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) .map(|max_possible_tx_count| { - let (doable_txs_count, _) = + let detected_tx_counts = PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( max_possible_tx_count, 123, ); - doable_txs_count + detected_tx_counts.affordable }) .collect::>(); @@ -1027,12 +1029,12 @@ mod tests { .into_iter() .map(|correction| plus_minus_correction_of_u16_max(correction)) .map(|required_tx_count_usize| { - let (_, required_tx_count) = + let detected_tx_counts = PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( 123, required_tx_count_usize, ); - required_tx_count + detected_tx_counts.required }) .collect::>(); @@ -1066,20 +1068,19 @@ mod tests { }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; - let criteria_and_accounts = - subject.calculate_criteria_sums_for_accounts(qualified_payables); + let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let mut previous_criteria_sum = u128::MAX; + let mut previous_weight = u128::MAX; let accounts_alone = criteria_and_accounts .into_iter() - .map(|(criteria_sum, account)| { + .map(|(weight, account)| { assert!( - previous_criteria_sum > criteria_sum, + previous_weight > weight, "Previous criteria {} wasn't larger than {} but should've been", - previous_criteria_sum, - criteria_sum + previous_weight, + weight ); - previous_criteria_sum = criteria_sum; + previous_weight = weight; account }) .collect::>(); @@ -1118,11 +1119,10 @@ mod tests { .unwrap(); // First, let's have an example of why this test is important - let criteria_and_accounts = - subject.calculate_criteria_sums_for_accounts(qualified_payables); - let criteria_total = criteria_total(&criteria_and_accounts); + let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + let weights_total = weights_total(&criteria_and_accounts); let proposed_adjustments = subject - .compute_adjusted_but_non_finalized_accounts(criteria_and_accounts, criteria_total); + .compute_adjusted_but_non_finalized_accounts(criteria_and_accounts, weights_total); let proposed_adjusted_balance_2 = proposed_adjustments[1].proposed_adjusted_balance; // The criteria sum of the second account grew very progressively due to the effect of the greater age; // consequences would've been that redistributing the new balances according to the computed criteria would've @@ -1197,10 +1197,10 @@ mod tests { Some(consuming_wallet_balance), Some(Logger::new(test_name)), ); - let accounts_with_individual_criteria = - subject.calculate_criteria_sums_for_accounts(accounts); + let accounts_with_individual_criteria = subject.calculate_weights_for_accounts(accounts); - let result = subject.perform_masq_adjustment(accounts_with_individual_criteria.clone()); + let result = + subject.perform_adjustment_by_service_fee(accounts_with_individual_criteria.clone()); let remaining = match result { AdjustmentIterationResult::SpecialTreatmentNeeded { @@ -1264,7 +1264,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::MasqToken, + adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, }; @@ -1338,7 +1338,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::MasqToken, + adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, }; @@ -1572,7 +1572,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::MasqToken, + adjustment: Adjustment::ByServiceFee, response_skeleton_opt, }; @@ -1658,7 +1658,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables, agent, - adjustment: Adjustment::MasqToken, + adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, }; @@ -1956,7 +1956,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { qualified_payables: qualified_payables.clone(), agent, - adjustment: Adjustment::MasqToken, + adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, }; From ebfef1d1f389890fba3b4115ad75de8f2c6b3791 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 7 Jan 2024 14:40:50 +0800 Subject: [PATCH 113/250] GH-711: another little chunk of refactoring --- .../payment_adjuster/adjustment_runners.rs | 46 ++++++++-------- node/src/accountant/payment_adjuster/inner.rs | 20 ++++--- .../accountant/payment_adjuster/log_fns.rs | 4 +- .../miscellaneous/helper_functions.rs | 10 ++-- node/src/accountant/payment_adjuster/mod.rs | 53 +++++++++++-------- .../payment_adjuster/possibility_verifier.rs | 24 +++++---- .../accountant/payment_adjuster/test_utils.rs | 2 +- 7 files changed, 91 insertions(+), 68 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 19d1708a6..5b7a8a20d 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -39,9 +39,9 @@ pub trait AdjustmentRunner { ) -> Self::ReturnType; } -pub struct TransactionAndServiceFeeRunner {} +pub struct TransactionAndServiceFeeAdjustmentRunner {} -impl AdjustmentRunner for TransactionAndServiceFeeRunner { +impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { type ReturnType = Result< Either, Vec>, PaymentAdjusterError, @@ -78,9 +78,9 @@ impl AdjustmentRunner for TransactionAndServiceFeeRunner { } } -pub struct ServiceFeeOnlyRunner {} +pub struct ServiceFeeOnlyAdjustmentRunner {} -impl AdjustmentRunner for ServiceFeeOnlyRunner { +impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { type ReturnType = Vec; fn adjust_last_one( @@ -146,8 +146,8 @@ fn empty_or_single_element_vector( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one, empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyRunner, - TransactionAndServiceFeeRunner, + adjust_last_one, empty_or_single_element_vector, AdjustmentRunner, + ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; @@ -166,12 +166,12 @@ mod tests { payment_adjuster } - fn test_adjust_last_one( + fn test_adjust_last_one( subject: AR, - expected_return_type_finalizer: fn(Vec) -> R, + expected_return_type_finalizer: fn(Vec) -> RT, ) where - AR: AdjustmentRunner, - R: Debug + PartialEq, + AR: AdjustmentRunner, + RT: Debug + PartialEq, { let now = SystemTime::now(); let wallet_1 = make_wallet("abc"); @@ -196,15 +196,18 @@ mod tests { } #[test] - fn masq_and_transaction_fee_adjust_single_works() { - test_adjust_last_one(TransactionAndServiceFeeRunner {}, |expected_vec| { - Ok(Either::Left(expected_vec)) - }) + fn transaction_and_service_fee_adjust_last_one_works() { + test_adjust_last_one( + TransactionAndServiceFeeAdjustmentRunner {}, + |expected_vec| Ok(Either::Left(expected_vec)), + ) } #[test] - fn masq_only_adjust_single_works() { - test_adjust_last_one(ServiceFeeOnlyRunner {}, |expected_vec| expected_vec) + fn service_fee_only_adjust_last_one_works() { + test_adjust_last_one(ServiceFeeOnlyAdjustmentRunner {}, |expected_vec| { + expected_vec + }) } #[test] @@ -247,7 +250,8 @@ mod tests { } #[test] - fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_on_edge() { + fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_evens_the_edge() + { let account_balance = 4_000_444; let cw_balance = calculate_disqualification_edge(account_balance); @@ -264,7 +268,7 @@ mod tests { } #[test] - fn adjust_multiple_for_the_masq_only_runner_is_not_supposed_to_care_about_transaction_fee() { + fn adjust_multiple_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { @@ -283,7 +287,7 @@ mod tests { let mut payment_adjuster = PaymentAdjusterReal::new(); let cw_balance = 9_000_000; payment_adjuster.initialize_inner(cw_balance, adjustment, now); - let subject = ServiceFeeOnlyRunner {}; + let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(accounts); let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); @@ -298,14 +302,14 @@ mod tests { } #[test] - fn empty_or_single_element_for_none() { + fn empty_or_single_element_vector_for_none() { let result = empty_or_single_element_vector(None); assert_eq!(result, vec![]) } #[test] - fn empty_or_single_element_for_some() { + fn empty_or_single_element_vector_for_some() { let account_info = AdjustedAccountBeforeFinalization { original_account: make_payable_account(123), proposed_adjusted_balance: 123_456_789, diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 339d95743..4ff3afac2 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,5 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::payment_adjuster::adjustment_runners::AdjustmentRunner; use std::time::SystemTime; pub trait PaymentAdjusterInner { @@ -21,13 +22,13 @@ impl PaymentAdjusterInnerReal { pub fn new( now: SystemTime, transaction_fee_count_limit_opt: Option, - cw_masq_balance_minor: u128, + cw_service_fee_balance_minor: u128, ) -> Self { Self { now, transaction_fee_count_limit_opt, - original_cw_service_fee_balance_minor: cw_masq_balance_minor, - unallocated_cw_service_fee_balance_minor: cw_masq_balance_minor, + original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, + unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } } } @@ -96,9 +97,12 @@ mod tests { fn inner_real_is_constructed_correctly() { let now = SystemTime::now(); let transaction_fee_count_limit_opt = Some(3); - let cw_masq_balance = 123_456_789; - let result = - PaymentAdjusterInnerReal::new(now, transaction_fee_count_limit_opt, cw_masq_balance); + let cw_service_fee_balance = 123_456_789; + let result = PaymentAdjusterInnerReal::new( + now, + transaction_fee_count_limit_opt, + cw_service_fee_balance, + ); assert_eq!(result.now, now); assert_eq!( @@ -107,11 +111,11 @@ mod tests { ); assert_eq!( result.original_cw_service_fee_balance_minor, - cw_masq_balance + cw_service_fee_balance ); assert_eq!( result.unallocated_cw_service_fee_balance_minor, - cw_masq_balance + cw_service_fee_balance ) } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index f534d43d9..60119819b 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -136,14 +136,14 @@ pub fn info_log_for_disqualified_account( pub fn log_adjustment_by_service_fee_is_required( logger: &Logger, payables_sum: u128, - cw_masq_balance: u128, + cw_service_fee_balance: u128, ) { warning!( logger, "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ the MASQ token. Adjustment in their count or the amounts is required.", payables_sum.separate_with_commas(), - cw_masq_balance.separate_with_commas() + cw_service_fee_balance.separate_with_commas() ); info!(logger, "{}", REFILL_RECOMMENDATION) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 1be3bbb92..983fe8896 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -389,7 +389,7 @@ mod tests { MAX_EXPONENT_FOR_10_IN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, }; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; @@ -488,13 +488,13 @@ mod tests { // these parameters below will be bigger than another and therefore // we cannot line them up in an order let accounts_as_months_and_balances = vec![ - (1, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), + (1, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), (5, 10_u128.pow(18)), (12, 10_u128.pow(18)), (120, 10_u128.pow(20)), - (600, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR * 1000), + (600, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), + (1200, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR * 1000), ]; let (accounts_with_their_weights, reserved_initial_accounts_order_according_to_wallets) = get_extreme_weights_and_initial_accounts_order(accounts_as_months_and_balances); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 227443417..86210c826 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -12,7 +12,7 @@ mod test_utils; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, TransactionAndServiceFeeRunner, ServiceFeeOnlyRunner, + AdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, }; use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculatorIterators}; use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; @@ -289,7 +289,7 @@ impl PaymentAdjusterReal { ) -> Result, PaymentAdjusterError> { let accounts = self.calculate_criteria_and_propose_adjustments_recursively( qualified_accounts, - TransactionAndServiceFeeRunner {}, + TransactionAndServiceFeeAdjustmentRunner {}, )?; match accounts { @@ -306,7 +306,7 @@ impl PaymentAdjusterReal { fn calculate_criteria_and_propose_adjustments_recursively( &mut self, - mut unresolved_qualified_accounts: Vec, + unresolved_qualified_accounts: Vec, adjustment_runner: AR, ) -> RT where @@ -318,14 +318,19 @@ impl PaymentAdjusterReal { ); if unresolved_qualified_accounts.len() == 1 { - return adjustment_runner - .adjust_last_one(self, unresolved_qualified_accounts.remove(0)); + let last_one = unresolved_qualified_accounts + .into_iter() + .next() + .expect("previous if must be wrong"); + return adjustment_runner.adjust_last_one(self, last_one); } let weights_and_accounts_sorted = self.calculate_weights_for_accounts(unresolved_qualified_accounts); + #[cfg(test)] render_formulas_characteristics_for_diagnostics_if_enabled(); + adjustment_runner.adjust_multiple(self, weights_and_accounts_sorted) } @@ -435,7 +440,7 @@ impl PaymentAdjusterReal { let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( remaining, - ServiceFeeOnlyRunner {}, + ServiceFeeOnlyAdjustmentRunner {}, ); (here_decided_accounts, down_stream_decided_accounts) @@ -676,7 +681,7 @@ impl Display for PaymentAdjusterError { PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts, total_amount_demanded_minor, - cw_service_fee_balance_minor: cw_masq_balance_minor, + cw_service_fee_balance_minor: cw_service_fee_balance_minor, } => write!( f, "Analysis projected a possibility for an adjustment leaving each of the transactions \ @@ -685,7 +690,7 @@ impl Display for PaymentAdjusterError { Total amount demanded: {} wei. Consuming wallet balance: {} wei", number_of_accounts.separate_with_commas(), total_amount_demanded_minor.separate_with_commas(), - cw_masq_balance_minor.separate_with_commas() + cw_service_fee_balance_minor.separate_with_commas() ), PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( f, @@ -699,12 +704,12 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeRunner; + use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR, + make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, }; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -741,7 +746,7 @@ mod tests { let test_name = "search_for_indispensable_adjustment_gives_negative_answer"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - // MASQ balance > payments + // service fee balance > payments let input_1 = make_test_input_for_initial_check( Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Right(vec![ @@ -752,7 +757,7 @@ mod tests { }), None, ); - // MASQ balance == payments + // service fee balance == payments let input_2 = make_test_input_for_initial_check( Some(TestConfigForServiceFeeBalances { balances_of_accounts: Either::Left(vec![85, 15]), @@ -803,9 +808,9 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let number_of_accounts = 3; - let masq_balances_config_opt = None; + let service_fee_balances_config_opt = None; let (qualified_payables, agent) = make_test_input_for_initial_check( - masq_balances_config_opt, + service_fee_balances_config_opt, Some(TestConfigForTransactionFee { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, @@ -1112,7 +1117,7 @@ mod tests { let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( qualified_payables.clone(), - TransactionAndServiceFeeRunner {}, + TransactionAndServiceFeeAdjustmentRunner {}, ) .unwrap() .left() @@ -1159,8 +1164,9 @@ mod tests { ) { // NOTE that the same is true for more outweighed accounts that would require more than // the whole cw balance together, therefore there is no such a test either. - // This test answers the question what is happening when the cw MASQ balance cannot cover - // the outweighed accounts, which is just a hypothesis we can never reach in the reality. + // This test answers the question what is happening when the cw service fee balance cannot + // cover the outweighed accounts, which is just a hypothesis we can never reach in + // the reality. // If there are outweighed accounts some other accounts must be also around of which some // are under the disqualification limit pointing to one that would definitely head to its // disqualification. @@ -1248,7 +1254,10 @@ mod tests { let qualified_payables = { let debt_age_in_months = vec![120, 120, 120]; make_extreme_accounts( - Either::Left((debt_age_in_months, *MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR)), + Either::Left(( + debt_age_in_months, + *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, + )), now, ) }; @@ -1275,11 +1284,11 @@ mod tests { assert_eq!(result.affordable_accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( - "INFO: {test_name}: Shortage of MASQ in your consuming wallet impacts on payable \ + "INFO: {test_name}: Shortage of in your consuming wallet impacts on payable \ {wallet}, ruled out from this round of payments. The proposed adjustment {} wei \ was less than half of the recorded debt, {} wei", proposed_adjusted_balance_in_this_iteration.separate_with_commas(), - (*MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR).separate_with_commas() + (*MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR).separate_with_commas() ) }; let log_handler = TestLogHandler::new(); @@ -1687,10 +1696,10 @@ mod tests { } #[test] - fn not_enough_masq_to_pay_for_both_accounts_at_least_by_their_half_so_one_wins() { + fn not_enough_service_fee_for_both_accounts_at_least_by_their_half_so_only_one_wins() { fn merge_test_name_with_test_scenario(description: &str) -> String { format!( - "not_enough_masq_to_pay_for_both_accounts_at_least_by_their_half_so_one_wins{}", + "not_enough_service_fee_for_both_accounts_at_least_by_their_half_so_only_one_wins{}", description ) } diff --git a/node/src/accountant/payment_adjuster/possibility_verifier.rs b/node/src/accountant/payment_adjuster/possibility_verifier.rs index 65c3c586e..d47f77525 100644 --- a/node/src/accountant/payment_adjuster/possibility_verifier.rs +++ b/node/src/accountant/payment_adjuster/possibility_verifier.rs @@ -53,7 +53,7 @@ mod tests { fn test_body_for_adjustment_possibility_nearly_rejected( original_accounts: Vec, - cw_masq_balance: u128, + cw_service_fee_balance: u128, ) { let accounts_in_expected_format = original_accounts.iter().collect::>(); @@ -61,7 +61,7 @@ mod tests { let result = subject.verify_lowest_detectable_adjustment_possibility( &accounts_in_expected_format, - cw_masq_balance, + cw_service_fee_balance, ); assert_eq!(result, Ok(())) @@ -73,10 +73,13 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; + let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; let original_accounts = vec![account_1, account_2]; - test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) + test_body_for_adjustment_possibility_nearly_rejected( + original_accounts, + cw_service_fee_balance, + ) } #[test] @@ -85,10 +88,13 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_masq_balance = calculate_disqualification_edge(account_2.balance_wei); + let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei); let original_accounts = vec![account_1, account_2]; - test_body_for_adjustment_possibility_nearly_rejected(original_accounts, cw_masq_balance) + test_body_for_adjustment_possibility_nearly_rejected( + original_accounts, + cw_service_fee_balance, + ) } #[test] @@ -100,7 +106,7 @@ mod tests { account_2.balance_wei = 2_000_000_002; let mut account_3 = make_payable_account(333); account_3.balance_wei = 1_000_000_002; - let cw_masq_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; + let cw_service_fee_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts.iter().collect::>(); @@ -108,7 +114,7 @@ mod tests { let result = subject.verify_lowest_detectable_adjustment_possibility( &accounts_in_expected_format, - cw_masq_balance, + cw_service_fee_balance, ); assert_eq!( @@ -117,7 +123,7 @@ mod tests { PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts: 3, total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, - cw_service_fee_balance_minor: cw_masq_balance + cw_service_fee_balance_minor: cw_service_fee_balance } ) ) diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index bca3525a0..a483f88e5 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -14,7 +14,7 @@ use std::iter::Empty; use std::time::{Duration, SystemTime}; lazy_static! { - pub static ref MAX_POSSIBLE_MASQ_BALANCE_IN_MINOR: u128 = + pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18); pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; } From 9302444ad7fd76a22e2aa01c32b0a9d64a347cc2 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 7 Jan 2024 23:15:08 +0800 Subject: [PATCH 114/250] GH-711: save point before trying to disolve the huge mass of code for diagnostics --- .../age_criterion_calculator.rs | 24 +++-- .../balance_criterion_calculator.rs | 24 +++-- .../criteria_calculators/mod.rs | 74 +++++++------- .../payment_adjuster/diagnostics.rs | 70 ++++++++++--- node/src/accountant/payment_adjuster/inner.rs | 1 - .../miscellaneous/data_structures.rs | 58 +++++++++++ node/src/accountant/payment_adjuster/mod.rs | 98 ++++--------------- .../accountant/payment_adjuster/test_utils.rs | 15 +-- 8 files changed, 201 insertions(+), 163 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index ff55ca236..a1aca6770 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -1,12 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, ParameterCriterionCalculator, -}; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use crate::standard_impls_for_calculator; +use crate::all_standard_impls_for_criterion_calculator; use std::time::SystemTime; test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; @@ -29,7 +28,7 @@ where formula: Box u128>, } -standard_impls_for_calculator!( +all_standard_impls_for_criterion_calculator!( AgeCriterionCalculator, AgeInput, "AGE", @@ -124,8 +123,8 @@ impl From<&PayableAccount> for AgeInput { #[cfg(test)] pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; use std::time::Duration; @@ -134,7 +133,7 @@ pub mod characteristics_config { lazy_static! { pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { let now = SystemTime::now(); - let literal_values = [ + let literal_values = vec![ 1, 5, 9, @@ -156,9 +155,9 @@ pub mod characteristics_config { 78_000_000_000, 444_333_444_444, ]; - let decadic_exponents = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let decadic_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let horisontal_axis_data_suply = - reinterpret_vec_of_values_on_x_axis(literal_values, decadic_exponents); + serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); Mutex::new(Some(DiagnosticsAxisX { non_remarkable_values_supply: horisontal_axis_data_suply, remarkable_values_opt: Some(vec![ @@ -188,9 +187,8 @@ mod tests { AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_MAIN_EXPONENT, }; - use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, ParameterCriterionCalculator, - }; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::test_utils::{make_initialized_subject, Sentinel}; use std::iter::empty; use std::time::{Duration, SystemTime}; @@ -271,7 +269,7 @@ mod tests { let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); let subject = AgeCriterionCalculator::new(empty(), &payment_adjuster); - let result = subject.parameter_name(); + let result = subject.input_parameter_name(); assert_eq!(result, "AGE") } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 628b26134..e82c280b5 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -1,11 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, ParameterCriterionCalculator, -}; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; -use crate::standard_impls_for_calculator; +use crate::all_standard_impls_for_criterion_calculator; test_only_use!( use std::sync::Mutex; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; @@ -31,7 +30,7 @@ where formula: Box u128>, } -standard_impls_for_calculator!( +all_standard_impls_for_criterion_calculator!( BalanceCriterionCalculator, BalanceInput, "BALANCE", @@ -81,25 +80,25 @@ impl From<&PayableAccount> for BalanceInput { #[cfg(test)] pub mod characteristics_config { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; + use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use crate::accountant::payment_adjuster::test_utils::reinterpret_vec_of_values_on_x_axis; use lazy_static::lazy_static; use std::sync::Mutex; lazy_static! { pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let literal_values = [ + let literal_values = vec![ 123_456, 7_777_777, 1_888_999_999_888, 543_210_000_000_000_000_000, ]; - let decadic_exponents = [ + let decadic_exponents = vec![ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ]; let horisontal_axis_decimal_exponents = - reinterpret_vec_of_values_on_x_axis(literal_values, decadic_exponents); + serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); Mutex::new(Some(DiagnosticsAxisX { non_remarkable_values_supply: horisontal_axis_decimal_exponents, remarkable_values_opt: Some(vec![ @@ -120,9 +119,8 @@ mod tests { BalanceCriterionCalculator, BalanceInput, BALANCE_FINAL_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, }; - use crate::accountant::payment_adjuster::criteria_calculators::{ - CriterionCalculator, ParameterCriterionCalculator, - }; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::test_utils::Sentinel; use std::iter; @@ -173,7 +171,7 @@ mod tests { fn calculator_returns_the_right_main_param_name() { let subject = BalanceCriterionCalculator::new(iter::empty()); - let result = subject.parameter_name(); + let result = subject.input_parameter_name(); assert_eq!(result, "BALANCE") } diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index e6e218deb..9385f226a 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -6,7 +6,7 @@ pub mod balance_criterion_calculator; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::calculator_local_diagnostics; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::inside_calculator_local_diagnostics; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::fmt::Debug; test_only_use!( @@ -18,21 +18,21 @@ test_only_use!( // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator: - ParameterCriterionCalculator + Iterator + Iterator + CriterionCalculatorDiagnostics { // The additional trait constrain comes from efforts to write the API more Rust-like. - // This implementation has its own pros and cons; the little cons are we must learn to - // understand the need to have a special wrapper, for the input of any additional calculator. - // Don't be fooled to try writing a From implementation for third-part data types to satisfy - // the requirements. Because it is disallowed, this specific design has arisen. + // This implementation has its own pros and cons; the little cons are we have to learn to + // understand the need for a special wrapper of the input parameter. + // Don't be fooled trying writing a From implementation for third-party data types, hoping to + // satisfy the requirements. Because Rust disallow such things, this design has arisen. type Input: for<'a> From<&'a PayableAccount>; - // This is the only function you are supposed to implement for your calculator. - // All it does is to link the formula from inside of your calculator (see the existing - // implementations), and expose it to outside + // Good news, this is the only function you are supposed to implement for your calculator! + // All it does is to refer to the formula from inside of your calculator (see the existing + // implementations) and provide it for the outside fn formula(&self) -> &dyn Fn(Self::Input) -> u128; - fn calculate_and_add_to_weight( + fn calculate_criterion_and_add_in_total_weight( &self, (weight, account): (u128, PayableAccount), ) -> (u128, PayableAccount) @@ -40,33 +40,43 @@ pub trait CriterionCalculator: ::Input: Debug, { #[cfg(test)] - self.diagnostics_of_formula_characteristics(); + self.compute_formula_characteristics_for_diagnostics(); - let criterion: u128 = self.formula()((&account).into()); - let new_weight = weight + criterion; + let input_wrapper = Self::Input::from(&account); + let criterion: u128 = self.formula()(input_wrapper); + let updated_weight = weight + criterion; - calculator_local_diagnostics(&account.wallet, self, criterion, new_weight); + inside_calculator_local_diagnostics(&account.wallet, self, criterion, updated_weight); - (new_weight, account) + (updated_weight, account) } +} +pub trait CriterionCalculatorDiagnostics { + fn input_parameter_name(&self) -> &'static str; #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>>; + fn diagnostics_config_location(&self) -> &Mutex>> + where + Self: CriterionCalculator; #[cfg(test)] - fn diagnostics_config_opt(&self) -> Option> { + fn diagnostics_config_opt(&self) -> Option> + where + Self: CriterionCalculator, + { self.diagnostics_config_location() .lock() .expect("diagnostics poisoned") .take() } #[cfg(test)] - fn diagnostics_of_formula_characteristics(&self) + fn compute_formula_characteristics_for_diagnostics(&self) where - ::Input: Debug, + Self::Input: Debug, + Self: CriterionCalculator, { if COMPUTE_FORMULAS_CHARACTERISTICS { compute_progressive_characteristics( - self.parameter_name(), + self.input_parameter_name(), self.diagnostics_config_opt(), self.formula(), ) @@ -103,12 +113,8 @@ where } } -pub trait ParameterCriterionCalculator { - fn parameter_name(&self) -> &'static str; -} - #[macro_export] -macro_rules! standard_impls_for_calculator { +macro_rules! all_standard_impls_for_criterion_calculator { ($calculator: tt, $input_type: tt, $param_name: literal, $diagnostics_config_opt: expr) => { impl Iterator for $calculator where @@ -118,7 +124,7 @@ macro_rules! standard_impls_for_calculator { fn next(&mut self) -> Option { self.iter.next().map(|weight_and_account| { - self.calculate_and_add_to_weight(weight_and_account.into()) + self.calculate_criterion_and_add_in_total_weight(weight_and_account.into()) }) } } @@ -132,20 +138,22 @@ macro_rules! standard_impls_for_calculator { fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { self.formula.as_ref() } - - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> { - &$diagnostics_config_opt - } } - impl ParameterCriterionCalculator for $calculator + impl CriterionCalculatorDiagnostics for $calculator where I: Iterator, { - fn parameter_name(&self) -> &'static str { + fn input_parameter_name(&self) -> &'static str { $param_name } + + #[cfg(test)] + fn diagnostics_config_location( + &self, + ) -> &Mutex::Input>>> { + &$diagnostics_config_opt + } } }; } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 2e0a7a614..57ccd5b25 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -64,7 +64,7 @@ pub fn collection_diagnostics(label: &str, accounts: &[D]) { pub mod separately_defined_diagnostic_functions { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::ParameterCriterionCalculator; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::sub_lib::wallet::Wallet; @@ -150,9 +150,9 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn calculator_local_diagnostics( + pub fn inside_calculator_local_diagnostics( wallet_ref: &Wallet, - calculator: &N, + calculator: &D, criterion: u128, added_in_the_sum: u128, ) { @@ -161,7 +161,7 @@ pub mod separately_defined_diagnostic_functions { wallet_ref, "PARTIAL CRITERION CALCULATED", "{:>, + remarkable_vals: Option<&[(u128, &'static str)]>, ) -> String { match should_mark_be_used(coordinate_value, remarkable_vals) { Some(mark) => format!("{} {}", coordinate_value.separate_with_commas(), mark), @@ -238,7 +241,7 @@ pub mod formulas_progressive_characteristics { } fn should_mark_be_used( coordinate_value: u128, - remarkable_vals: Option<&Vec<(u128, &'static str)>>, + remarkable_vals: Option<&[(u128, &'static str)]>, ) -> Option<&'static str> { match remarkable_vals { Some(vals) => vals @@ -249,12 +252,12 @@ pub mod formulas_progressive_characteristics { } } - pub fn compute_progressive_characteristics( + pub fn compute_progressive_characteristics( main_param_name: &'static str, - config_opt: Option>, - formula: &dyn Fn(A) -> u128, + config_opt: Option>, + formula: &dyn Fn(CriterionCalculatorInput) -> u128, ) where - A: Debug, + CriterionCalculatorInput: Debug, { config_opt.map(|mut config| { let input_values = config.finalize_input_with_remarkable_values(); @@ -262,7 +265,8 @@ pub mod formulas_progressive_characteristics { let config_x_axis_type_formatter = config.convertor_to_expected_formula_input_type; let characteristics = input_values.into_iter().map(|single_coordinate| { let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); - let input_with_commas = render_notation(single_coordinate, remarkable.as_ref()); + let input_with_commas = + render_notation(single_coordinate, todo!("supply remarkable")); let computed_value_with_commas = formula(correctly_formatted_input).separate_with_commas(); format!( @@ -283,6 +287,50 @@ pub mod formulas_progressive_characteristics { .push(full_text); }); } + + fn read_diagnostics_inputs_from_file(path: &Path) -> Vec { + let mut file = File::open(path).expect("inputs badly prepared"); + let mut buffer = String::new(); + file.read_to_string(&mut buffer).unwrap(); + let mut first_two_lines = buffer.lines().take(2); + let first = first_two_lines.next().expect("first line missing"); + let second = first_two_lines.next().expect("second line missing"); + let first_line_starter = extract_line_starter(first); + if extract_line_starter(first) != "literals:" + || extract_line_starter(second) != "decimal_exponents" + { + panic!("Inputs in the file in {:?} should have the following format. First line starting \ + \"literals:\", second line with \"decimal_exponents:\", both immediately followed by comma \ + separated integers or left blank", path) + } + serialize_values_on_x_axis_from_vecs( + parse_numbers_from_line(first), + parse_numbers_from_line(second), + ) + } + + fn extract_line_starter(line: &str) -> String { + line.chars().take_while(|char| !char.is_numeric()).collect() + } + + fn parse_numbers_from_line(line: &str) -> Vec { + todo!("implement me"); + // line.chars().take() + } + + pub fn serialize_values_on_x_axis_from_vecs( + nums_declared_as_literals: Vec, + nums_declared_as_decimal_exponents: Vec, + ) -> Vec { + let exponent_based_numbers = nums_declared_as_decimal_exponents + .into_iter() + .map(|exponent| 10_u128.pow(exponent)); + nums_declared_as_literals + .into_iter() + .chain(exponent_based_numbers) + .sorted() + .collect() + } } #[cfg(test)] diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 4ff3afac2..9b3e0331f 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,6 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::adjustment_runners::AdjustmentRunner; use std::time::SystemTime; pub trait PaymentAdjusterInner { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index cea5767a2..dc8c62b29 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -52,3 +52,61 @@ pub struct TransactionCountsWithin16bits { pub affordable: u16, pub required: u16, } + +impl TransactionCountsWithin16bits { + pub fn new(max_possible_tx_count: u128, number_of_accounts: usize) -> Self { + TransactionCountsWithin16bits { + affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), + required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), + } + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionCountsWithin16bits; + + #[test] + fn there_is_u16_ceiling_for_possible_tx_count() { + let result = [-3_i8, -1, 0, 1, 10] + .into_iter() + .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) + .map(|max_possible_tx_count| { + let detected_tx_counts = + TransactionCountsWithin16bits::new(max_possible_tx_count, 123); + detected_tx_counts.affordable + }) + .collect::>(); + + assert_eq!( + result, + vec![u16::MAX - 3, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] + ) + } + + #[test] + fn there_is_u16_ceiling_for_required_number_of_accounts() { + let result = [-9_i8, -1, 0, 1, 5] + .into_iter() + .map(|correction| plus_minus_correction_of_u16_max(correction)) + .map(|required_tx_count_usize| { + let detected_tx_counts = + TransactionCountsWithin16bits::new(123, required_tx_count_usize); + detected_tx_counts.required + }) + .collect::>(); + + assert_eq!( + result, + vec![u16::MAX - 9, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] + ) + } + + fn plus_minus_correction_of_u16_max(correction: i8) -> usize { + if correction < 0 { + (u16::MAX - correction.abs() as u16) as usize + } else { + u16::MAX as usize + correction as usize + } + } +} diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 86210c826..b27d71e3c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -31,7 +31,6 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{Adjust use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; use crate::diagnostics; -use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use itertools::Either; @@ -120,14 +119,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let affordable_accounts = self.run_adjustment(qualified_payables)?; - debug!( - self.logger, - "{}", - accounts_before_and_after_debug( - sketched_debug_info_opt.expectv("debug info"), - &affordable_accounts - ) - ); + self.complete_debug_info_if_enabled(sketched_debug_info_opt, &affordable_accounts); Ok(OutboundPaymentsInstructions { affordable_accounts, @@ -166,9 +158,9 @@ impl PaymentAdjusterReal { cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor); u128::try_from(max_possible_tx_count_u256).unwrap_or_else(|e| { panic!( - "Transaction fee balance {} wei in the consuming wallet cases panic given \ - estimated transaction fee per tx {} wei and resulting ratio {}, that should fit in \ - u128, respectively: \"{}\"", + "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ + transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ + respectively: \"{}\"", cw_transaction_fee_balance_minor, tx_fee_requirement_per_tx_minor, max_possible_tx_count_u256, @@ -186,10 +178,8 @@ impl PaymentAdjusterReal { per_transaction_requirement_minor, ); - let detected_tx_counts = Self::u16_ceiling_for_accounts_to_be_processed_at_a_time( - max_possible_tx_count, - number_of_qualified_accounts, - ); + let detected_tx_counts = + TransactionCountsWithin16bits::new(max_possible_tx_count, number_of_qualified_accounts); let max_tx_count_we_can_afford_u16 = detected_tx_counts.affordable; let required_tx_count_u16 = detected_tx_counts.required; @@ -215,16 +205,6 @@ impl PaymentAdjusterReal { } } - fn u16_ceiling_for_accounts_to_be_processed_at_a_time( - max_possible_tx_count: u128, - number_of_accounts: usize, - ) -> TransactionCountsWithin16bits { - TransactionCountsWithin16bits { - affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), - required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), - } - } - fn check_need_of_adjustment_by_service_fee( logger: &Logger, payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, @@ -639,6 +619,18 @@ impl PaymentAdjusterReal { .collect::>() }) } + + fn complete_debug_info_if_enabled( + &self, + sketched_debug_info_opt: Option>, + affordable_accounts: &[PayableAccount], + ) { + self.logger.debug(|| { + let sketched_debug_info = + sketched_debug_info_opt.expect("debug is enabled, so info should exist"); + accounts_before_and_after_debug(sketched_debug_info, affordable_accounts) + }) + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -681,7 +673,7 @@ impl Display for PaymentAdjusterError { PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { number_of_accounts, total_amount_demanded_minor, - cw_service_fee_balance_minor: cw_service_fee_balance_minor, + cw_service_fee_balance_minor, } => write!( f, "Analysis projected a possibility for an adjustment leaving each of the transactions \ @@ -967,14 +959,6 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } - fn plus_minus_correction_of_u16_max(correction: i8) -> usize { - if correction < 0 { - (u16::MAX - correction.abs() as u16) as usize - } else { - u16::MAX as usize + correction as usize - } - } - #[test] fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() { @@ -1007,48 +991,6 @@ mod tests { assert_eq!(err_msg, &expected_panic) } - #[test] - fn there_is_u16_ceiling_for_possible_tx_count() { - let result = [-3_i8, -1, 0, 1, 10] - .into_iter() - .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) - .map(|max_possible_tx_count| { - let detected_tx_counts = - PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( - max_possible_tx_count, - 123, - ); - detected_tx_counts.affordable - }) - .collect::>(); - - assert_eq!( - result, - vec![u16::MAX - 3, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] - ) - } - - #[test] - fn there_is_u16_ceiling_for_number_of_accounts() { - let result = [-9_i8, -1, 0, 1, 5] - .into_iter() - .map(|correction| plus_minus_correction_of_u16_max(correction)) - .map(|required_tx_count_usize| { - let detected_tx_counts = - PaymentAdjusterReal::u16_ceiling_for_accounts_to_be_processed_at_a_time( - 123, - required_tx_count_usize, - ); - detected_tx_counts.required - }) - .collect::>(); - - assert_eq!( - result, - vec![u16::MAX - 9, u16::MAX - 1, u16::MAX, u16::MAX, u16::MAX] - ) - } - #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { let now = SystemTime::now(); @@ -1284,7 +1226,7 @@ mod tests { assert_eq!(result.affordable_accounts, vec![]); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( - "INFO: {test_name}: Shortage of in your consuming wallet impacts on payable \ + "INFO: {test_name}: Shortage of MASQ in your consuming wallet impacts on payable \ {wallet}, ruled out from this round of payments. The proposed adjustment {} wei \ was less than half of the recorded debt, {} wei", proposed_adjusted_balance_in_this_iteration.separate_with_commas(), diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index a483f88e5..57e327c76 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -6,7 +6,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::test_utils::make_wallet; -use itertools::{Either, Itertools}; +use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; @@ -63,17 +63,4 @@ pub fn make_extreme_accounts( .collect() } -pub fn reinterpret_vec_of_values_on_x_axis( - literal_feed: [u128; L1], - exponent_determined_feed: [u32; L2], -) -> Vec { - let exponent_based_numbers = exponent_determined_feed - .into_iter() - .map(|exponent| 10_u128.pow(exponent)); - literal_feed - .into_iter() - .chain(exponent_based_numbers) - .sorted() - .collect() -} pub type Sentinel = Empty<(u128, PayableAccount)>; From 06948e4b5ffaf76a0e9d4714ebe49525dcbd0770 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:08:38 +0800 Subject: [PATCH 115/250] GH-711: successfully finished a vast majority of the revamp; with many places get simpler --- .../age_criterion_calculator.rs | 254 ++++++----- .../balance_criterion_calculator.rs | 164 ++++---- .../criteria_calculators/mod.rs | 393 ++++++++++++------ .../payment_adjuster/diagnostics.rs | 238 ++++++++--- node/src/accountant/payment_adjuster/mod.rs | 49 ++- 5 files changed, 672 insertions(+), 426 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index a1aca6770..5dc8b15db 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -1,15 +1,14 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; +use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, CriterionCalculator, +}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use crate::all_standard_impls_for_criterion_calculator; use std::time::SystemTime; test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::characteristics_config::AGE_DIAGNOSTICS_CONFIG_OPT; use std::sync::Mutex; ); @@ -20,45 +19,43 @@ const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; -pub struct AgeCriterionCalculator -where - I: Iterator, -{ - iter: I, - formula: Box u128>, +pub struct AgeCriterionCalculator { + formula: Box u128>, } -all_standard_impls_for_criterion_calculator!( - AgeCriterionCalculator, - AgeInput, - "AGE", - AGE_DIAGNOSTICS_CONFIG_OPT -); +impl CriterionCalculator for AgeCriterionCalculator { + fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128 { + &self.formula + } + + fn calculator_type(&self) -> CalculatorType { + CalculatorType::DebtAge + } +} -impl AgeCriterionCalculator -where - I: Iterator, -{ - pub fn new(iter: I, payment_adjuster: &PaymentAdjusterReal) -> Self { +impl AgeCriterionCalculator { + pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { let now = payment_adjuster.inner.now(); - let formula = Box::new(move |wrapped_last_paid_timestamp: AgeInput| { - let last_paid_timestamp = wrapped_last_paid_timestamp.0; - let elapsed_secs: u64 = Self::nonzero_elapsed(now, last_paid_timestamp); + let formula = Box::new( + move |last_paid_timestamp_in_holder: CalculatorInputHolder| { + let last_paid_timestamp = last_paid_timestamp_in_holder.age_input(); + let elapsed_secs: u64 = Self::nonzero_elapsed(now, last_paid_timestamp); - let divisor = Self::nonzero_compute_divisor(elapsed_secs); + let divisor = Self::nonzero_compute_divisor(elapsed_secs); - let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); + let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - (elapsed_secs as u128) - .checked_pow(AGE_MAIN_EXPONENT) - .expect("pow overflow") - .checked_div(divisor) - .expect("div overflow") - .checked_mul(log_multiplier) - .expect("mul overflow") - }); - Self { iter, formula } + (elapsed_secs as u128) + .checked_pow(AGE_MAIN_EXPONENT) + .expect("pow overflow") + .checked_div(divisor) + .expect("div overflow") + .checked_mul(log_multiplier) + .expect("mul overflow") + }, + ); + Self { formula } } fn nonzero_elapsed(now: SystemTime, previous_timestamp: SystemTime) -> u64 { @@ -111,84 +108,75 @@ where } } -#[derive(Debug, Clone, Copy)] -pub struct AgeInput(pub SystemTime); - -impl From<&PayableAccount> for AgeInput { - fn from(account: &PayableAccount) -> Self { - AgeInput(account.last_paid_timestamp) - } -} - -#[cfg(test)] -pub mod characteristics_config { - use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use lazy_static::lazy_static; - use std::sync::Mutex; - use std::time::Duration; - use std::time::SystemTime; - - lazy_static! { - pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let now = SystemTime::now(); - let literal_values = vec![ - 1, - 5, - 9, - 25, - 44, - 50, - 75, - 180, - 600, - 900, - 33_333, - 86_400, - 255_000, - 6_700_000, - 55_333_000, - 200_300_400, - 500_000_000, - 7_000_000_000, - 78_000_000_000, - 444_333_444_444, - ]; - let decadic_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - let horisontal_axis_data_suply = - serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); - Mutex::new(Some(DiagnosticsAxisX { - non_remarkable_values_supply: horisontal_axis_data_suply, - remarkable_values_opt: Some(vec![ - (60, "MINUTE"), - (3_600, "HOUR"), - (86_400, "DAY"), - (604_800, "WEEK"), - ]), - convertor_to_expected_formula_input_type: Box::new( - move |secs_since_last_paid_payable| { - let native_time = now - .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling"); - AgeInput(native_time) - }, - ), - })) - }; - } -} +// #[cfg(test)] +// pub mod characteristics_config { +// use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; +// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; +// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; +// use lazy_static::lazy_static; +// use std::sync::Mutex; +// use std::time::Duration; +// use std::time::SystemTime; +// +// lazy_static! { +// pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { +// let now = SystemTime::now(); +// let literal_values = vec![ +// 1, +// 5, +// 9, +// 25, +// 44, +// 50, +// 75, +// 180, +// 600, +// 900, +// 33_333, +// 86_400, +// 255_000, +// 6_700_000, +// 55_333_000, +// 200_300_400, +// 500_000_000, +// 7_000_000_000, +// 78_000_000_000, +// 444_333_444_444, +// ]; +// let decadic_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; +// let horisontal_axis_data_suply = +// serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); +// Mutex::new(Some(DiagnosticsAxisX { +// non_remarkable_values_supply: horisontal_axis_data_suply, +// remarkable_values_opt: Some(vec![ +// (60, "MINUTE"), +// (3_600, "HOUR"), +// (86_400, "DAY"), +// (604_800, "WEEK"), +// ]), +// convertor_to_expected_formula_input_type: Box::new( +// move |secs_since_last_paid_payable| { +// let native_time = now +// .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) +// .expect("time travelling"); +// AgeInput(native_time) +// }, +// ), +// })) +// }; +// } +// } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ - AgeCriterionCalculator, AgeInput, AGE_DESC_MULTIPLIER_ARG_EXP, - AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, - AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, - AGE_MAIN_EXPONENT, + AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, + AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, + AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_MAIN_EXPONENT, + }; + use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, CriterionCalculator, }; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; use crate::accountant::payment_adjuster::test_utils::{make_initialized_subject, Sentinel}; use std::iter::empty; use std::time::{Duration, SystemTime}; @@ -207,7 +195,7 @@ mod tests { fn nonzero_compute_divisor_works() { let result: Vec<_> = [1, 100, 81, 82, 80] .into_iter() - .map(|secs| AgeCriterionCalculator::::nonzero_compute_divisor(secs)) + .map(|secs| AgeCriterionCalculator::nonzero_compute_divisor(secs)) .collect(); assert_eq!(result, vec![1, 10, 9, 10, 9]) @@ -223,7 +211,7 @@ mod tests { now.checked_sub(Duration::from_secs(2)).unwrap(), ] .into_iter() - .map(|timestamp| AgeCriterionCalculator::::nonzero_elapsed(now, timestamp)) + .map(|timestamp| AgeCriterionCalculator::nonzero_elapsed(now, timestamp)) .collect(); assert_eq!(result, vec![1, 1, 2]) @@ -236,12 +224,8 @@ mod tests { .take(12) .map(|exp| 10_u64.pow(exp)) .map(|seconds_elapsed| { - let divisor = - AgeCriterionCalculator::::nonzero_compute_divisor(seconds_elapsed); - AgeCriterionCalculator::::compute_descending_multiplier( - seconds_elapsed, - divisor, - ) + let divisor = AgeCriterionCalculator::nonzero_compute_divisor(seconds_elapsed); + AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) }) .collect(); @@ -258,45 +242,41 @@ mod tests { fn nonzero_log_works() { let result = vec![0.0, 0.6, 1.3, 1.99999, 2.0, 2.1, 5.0, 9.0] .into_iter() - .map(|num| AgeCriterionCalculator::::nonzero_log_value(num)) + .map(|num| AgeCriterionCalculator::nonzero_log_value(num)) .collect::>(); assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 2, 3]) } #[test] - fn calculator_returns_the_right_main_param_name() { + fn calculator_knows_its_type() { let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); - let subject = AgeCriterionCalculator::new(empty(), &payment_adjuster); + let subject = AgeCriterionCalculator::new(&payment_adjuster); - let result = subject.input_parameter_name(); + let result = subject.calculator_type(); - assert_eq!(result, "AGE") + assert_eq!(result, CalculatorType::DebtAge) } #[test] fn age_criteria_calculation_works() { let now = SystemTime::now(); let payment_adjuster = make_initialized_subject(now, None, None); - let subject = AgeCriterionCalculator::new(empty(), &payment_adjuster); - let last_paid_timestamp_wrapped = AgeInput( - SystemTime::now() - .checked_sub(Duration::from_secs(1500)) - .unwrap(), - ); + let subject = AgeCriterionCalculator::new(&payment_adjuster); + let last_paid_timestamp = SystemTime::now() + .checked_sub(Duration::from_secs(1500)) + .unwrap(); + let last_paid_timestamp_holder = CalculatorInputHolder::DebtAge { + last_paid_timestamp, + }; - let result = subject.formula()(last_paid_timestamp_wrapped); + let result = subject.formula()(last_paid_timestamp_holder); let expected_criterion = { - let elapsed_secs: u64 = now - .duration_since(last_paid_timestamp_wrapped.0) - .unwrap() - .as_secs(); - let divisor = AgeCriterionCalculator::::nonzero_compute_divisor(elapsed_secs); - let log_multiplier = AgeCriterionCalculator::::compute_descending_multiplier( - elapsed_secs, - divisor, - ); + let elapsed_secs: u64 = now.duration_since(last_paid_timestamp).unwrap().as_secs(); + let divisor = AgeCriterionCalculator::nonzero_compute_divisor(elapsed_secs); + let log_multiplier = + AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) .checked_pow(AGE_MAIN_EXPONENT) .unwrap() diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index e82c280b5..1a1c7d88e 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -1,13 +1,12 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; +use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, CriterionCalculator, +}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; -use crate::all_standard_impls_for_criterion_calculator; test_only_use!( use std::sync::Mutex; - use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::characteristics_config::BALANCE_DIAGNOSTICS_CONFIG_OPT; use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; ); @@ -22,28 +21,24 @@ const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; // This parameter affects the steepness analogously, but energetically const BALANCE_FINAL_MULTIPLIER: u128 = 2; -pub struct BalanceCriterionCalculator -where - I: Iterator, -{ - iter: I, - formula: Box u128>, +pub struct BalanceCriterionCalculator { + formula: Box u128>, } -all_standard_impls_for_criterion_calculator!( - BalanceCriterionCalculator, - BalanceInput, - "BALANCE", - BALANCE_DIAGNOSTICS_CONFIG_OPT -); +impl CriterionCalculator for BalanceCriterionCalculator { + fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128 { + &self.formula + } + + fn calculator_type(&self) -> CalculatorType { + CalculatorType::DebtBalance + } +} -impl BalanceCriterionCalculator -where - I: Iterator, -{ - pub fn new(iter: I) -> Self { - let formula = Box::new(|wrapped_balance_minor: BalanceInput| { - let balance_minor = wrapped_balance_minor.0; +impl BalanceCriterionCalculator { + pub fn new() -> Self { + let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { + let balance_minor = balance_minor_holder.balance_input(); let argument_for_log = Self::calculate_binary_argument(balance_minor); let binary_weight = Self::nonzero_log2(argument_for_log); balance_minor @@ -51,7 +46,7 @@ where .expect("mul overflow") * BALANCE_FINAL_MULTIPLIER }); - Self { iter, formula } + Self { formula } } fn nonzero_log2(input: u128) -> u32 { @@ -68,61 +63,51 @@ where } } -#[derive(Debug, Clone, Copy)] -pub struct BalanceInput(pub u128); - -impl From<&PayableAccount> for BalanceInput { - fn from(account: &PayableAccount) -> Self { - BalanceInput(account.balance_wei) - } -} - -#[cfg(test)] -pub mod characteristics_config { - use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use lazy_static::lazy_static; - use std::sync::Mutex; - - lazy_static! { - pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { - let literal_values = vec![ - 123_456, - 7_777_777, - 1_888_999_999_888, - 543_210_000_000_000_000_000, - ]; - let decadic_exponents = vec![ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, - ]; - let horisontal_axis_decimal_exponents = - serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); - Mutex::new(Some(DiagnosticsAxisX { - non_remarkable_values_supply: horisontal_axis_decimal_exponents, - remarkable_values_opt: Some(vec![ - (10_u128.pow(9), "GWEI"), - (10_u128.pow(18), "MASQ"), - ]), - convertor_to_expected_formula_input_type: Box::new(|balance_wei| { - BalanceInput(balance_wei) - }), - })) - }; - } -} +// +// #[cfg(test)] +// pub mod characteristics_config { +// use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; +// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; +// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; +// use lazy_static::lazy_static; +// use std::sync::Mutex; +// +// lazy_static! { +// pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { +// let literal_values = vec![ +// 123_456, +// 7_777_777, +// 1_888_999_999_888, +// 543_210_000_000_000_000_000, +// ]; +// let decadic_exponents = vec![ +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +// 24, 25, +// ]; +// let horisontal_axis_decimal_exponents = +// serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); +// Mutex::new(Some(DiagnosticsAxisX { +// non_remarkable_values_supply: horisontal_axis_decimal_exponents, +// remarkable_values_opt: Some(vec![ +// (10_u128.pow(9), "GWEI"), +// (10_u128.pow(18), "MASQ"), +// ]), +// convertor_to_expected_formula_input_type: Box::new(|balance_wei| { +// BalanceInput(balance_wei) +// }), +// })) +// }; +// } +// } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ - BalanceCriterionCalculator, BalanceInput, BALANCE_FINAL_MULTIPLIER, - BALANCE_LOG_2_ARG_DIVISOR, + BalanceCriterionCalculator, BALANCE_FINAL_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, + }; + use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, CriterionCalculator, }; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; - use crate::accountant::payment_adjuster::test_utils::Sentinel; - use std::iter; #[test] fn constants_are_correct() { @@ -142,7 +127,7 @@ mod tests { let result: Vec<_> = arg_values .into_iter() - .map(|arg| BalanceCriterionCalculator::::calculate_binary_argument(arg)) + .map(|arg| BalanceCriterionCalculator::calculate_binary_argument(arg)) .collect(); assert_eq!( @@ -161,37 +146,34 @@ mod tests { fn nonzero_log2_works() { let result: Vec<_> = [0, 1, 2, 5, 66, 100, 131, 132, u64::MAX as u128 + 1] .into_iter() - .map(|balance| BalanceCriterionCalculator::::nonzero_log2(balance)) + .map(|balance| BalanceCriterionCalculator::nonzero_log2(balance)) .collect(); assert_eq!(result, vec![1, 1, 1, 2, 6, 6, 7, 7, 64]) } #[test] - fn calculator_returns_the_right_main_param_name() { - let subject = BalanceCriterionCalculator::new(iter::empty()); + fn balance_criterion_calculator_knows_its_type() { + let subject = BalanceCriterionCalculator::new(); - let result = subject.input_parameter_name(); + let result = subject.calculator_type(); - assert_eq!(result, "BALANCE") + assert_eq!(result, CalculatorType::DebtBalance) } #[test] fn balance_criteria_calculation_works() { - let subject = BalanceCriterionCalculator::new(iter::empty()); - let balance_wei_wrapped = BalanceInput(111_333_555_777); + let subject = BalanceCriterionCalculator::new(); + let balance_wei = 111_333_555_777; + let balance_wei_inside_input_holder = CalculatorInputHolder::DebtBalance(balance_wei); - let result = subject.formula()(balance_wei_wrapped); + let result = subject.formula()(balance_wei_inside_input_holder); let expected_result = { - let binary_weight = - BalanceCriterionCalculator::::nonzero_log2(BalanceCriterionCalculator::< - Sentinel, - >::calculate_binary_argument( - balance_wei_wrapped.0 - )); - balance_wei_wrapped - .0 + let binary_weight = BalanceCriterionCalculator::nonzero_log2( + BalanceCriterionCalculator::calculate_binary_argument(balance_wei), + ); + balance_wei .checked_mul(binary_weight as u128) .expect("mul overflow") * BALANCE_FINAL_MULTIPLIER diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 9385f226a..d94ccc477 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -4,11 +4,10 @@ pub mod age_criterion_calculator; pub mod balance_criterion_calculator; use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::inside_calculator_local_diagnostics; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use std::fmt::Debug; +use std::fmt::{Debug, Display, Formatter}; +use std::time::SystemTime; +use variant_count::VariantCount; test_only_use!( use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ compute_progressive_characteristics, DiagnosticsAxisX, COMPUTE_FORMULAS_CHARACTERISTICS, @@ -17,143 +16,291 @@ test_only_use!( ); // Caution: always remember to use checked math operations in the criteria formulas! -pub trait CriterionCalculator: - Iterator + CriterionCalculatorDiagnostics -{ - // The additional trait constrain comes from efforts to write the API more Rust-like. - // This implementation has its own pros and cons; the little cons are we have to learn to - // understand the need for a special wrapper of the input parameter. - // Don't be fooled trying writing a From implementation for third-party data types, hoping to - // satisfy the requirements. Because Rust disallow such things, this design has arisen. - type Input: for<'a> From<&'a PayableAccount>; - - // Good news, this is the only function you are supposed to implement for your calculator! - // All it does is to refer to the formula from inside of your calculator (see the existing - // implementations) and provide it for the outside - fn formula(&self) -> &dyn Fn(Self::Input) -> u128; - - fn calculate_criterion_and_add_in_total_weight( - &self, - (weight, account): (u128, PayableAccount), - ) -> (u128, PayableAccount) - where - ::Input: Debug, - { - #[cfg(test)] - self.compute_formula_characteristics_for_diagnostics(); +pub trait CriterionCalculator { + // Reference to the formula that is meant by design to be stored inside the calculator. + // The formula can keep its own context if required + fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128; - let input_wrapper = Self::Input::from(&account); - let criterion: u128 = self.formula()(input_wrapper); - let updated_weight = weight + criterion; + fn calculator_type(&self) -> CalculatorType; +} - inside_calculator_local_diagnostics(&account.wallet, self, criterion, updated_weight); +#[derive(PartialEq, Debug, VariantCount, Clone, Copy)] +pub enum CalculatorType { + DebtBalance, + DebtAge, +} - (updated_weight, account) - } +#[derive(PartialEq, Debug, VariantCount)] +pub enum CalculatorInputHolder { + DebtBalance(u128), + DebtAge { last_paid_timestamp: SystemTime }, } -pub trait CriterionCalculatorDiagnostics { - fn input_parameter_name(&self) -> &'static str; - #[cfg(test)] - fn diagnostics_config_location(&self) -> &Mutex>> - where - Self: CriterionCalculator; - #[cfg(test)] - fn diagnostics_config_opt(&self) -> Option> - where - Self: CriterionCalculator, - { - self.diagnostics_config_location() - .lock() - .expect("diagnostics poisoned") - .take() +impl CalculatorInputHolder { + fn age_input(self) -> SystemTime { + if let CalculatorInputHolder::DebtAge { + last_paid_timestamp, + } = self + { + last_paid_timestamp + } else { + todo!() + } } - #[cfg(test)] - fn compute_formula_characteristics_for_diagnostics(&self) - where - Self::Input: Debug, - Self: CriterionCalculator, - { - if COMPUTE_FORMULAS_CHARACTERISTICS { - compute_progressive_characteristics( - self.input_parameter_name(), - self.diagnostics_config_opt(), - self.formula(), - ) + + fn balance_input(self) -> u128 { + if let CalculatorInputHolder::DebtBalance(balance_wei) = self { + balance_wei + } else { + todo!() } } } -pub trait CriteriaCalculatorIterators { - fn calculate_age_criteria( - self, - payment_adjuster: &PaymentAdjusterReal, - ) -> AgeCriterionCalculator - where - Self: Iterator + Sized; - - fn calculate_balance_criteria(self) -> BalanceCriterionCalculator - where - Self: Iterator + Sized; +impl<'a> From<((CalculatorType, &'a PayableAccount))> for CalculatorInputHolder { + fn from((calculator_type, account): (CalculatorType, &'a PayableAccount)) -> Self { + match calculator_type { + CalculatorType::DebtBalance => CalculatorInputHolder::DebtBalance(account.balance_wei), + CalculatorType::DebtAge => CalculatorInputHolder::DebtAge { + last_paid_timestamp: account.last_paid_timestamp, + }, + } + } } -impl CriteriaCalculatorIterators for I -where - I: Iterator, -{ - fn calculate_age_criteria( - self, - payment_adjuster: &PaymentAdjusterReal, - ) -> AgeCriterionCalculator { - AgeCriterionCalculator::new(self, payment_adjuster) +impl Display for CalculatorType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CalculatorType::DebtBalance => write!(f, "BALANCE"), + CalculatorType::DebtAge => write!(f, "AGE"), + } } +} + +#[cfg(test)] +mod tests { + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, + }; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::test_utils::make_payable_account; + use crate::test_utils::make_wallet; + use std::panic::{catch_unwind, RefUnwindSafe}; + use std::time::{Duration, SystemTime}; - fn calculate_balance_criteria(self) -> BalanceCriterionCalculator { - BalanceCriterionCalculator::new(self) + #[test] + fn input_holders_can_be_derived_from_calculator_type_and_payable_account() { + let payment_adjuster = PaymentAdjusterReal::new(); + let balance_wei = 135_792_468; + let last_paid_timestamp = SystemTime::now() + .checked_sub(Duration::from_secs(3)) + .unwrap(); + let account = PayableAccount { + wallet: make_wallet("abc"), + balance_wei, + last_paid_timestamp, + pending_payable_opt: None, + }; + + let result = [CalculatorType::DebtAge, CalculatorType::DebtBalance] + .into_iter() + .map(|calculator_type| CalculatorInputHolder::from((calculator_type, &account))) + .collect::>(); + + let expected = vec![ + CalculatorInputHolder::DebtAge { + last_paid_timestamp, + }, + CalculatorInputHolder::DebtBalance(balance_wei), + ]; + assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); + assert_eq!(result, expected); } -} -#[macro_export] -macro_rules! all_standard_impls_for_criterion_calculator { - ($calculator: tt, $input_type: tt, $param_name: literal, $diagnostics_config_opt: expr) => { - impl Iterator for $calculator - where - I: Iterator, - { - type Item = (u128, PayableAccount); + #[test] + fn calculator_type_implements_display() { + assert_eq!(CalculatorType::DebtBalance.to_string(), "BALANCE"); + assert_eq!(CalculatorType::DebtAge.to_string(), "AGE") + } - fn next(&mut self) -> Option { - self.iter.next().map(|weight_and_account| { - self.calculate_criterion_and_add_in_total_weight(weight_and_account.into()) - }) - } - } + #[test] + fn input_values_can_be_fetched_from_input_holder() { + let last_paid_timestamp = SystemTime::now() + .checked_sub(Duration::from_millis(1234)) + .unwrap(); + let age_input_holder = CalculatorInputHolder::DebtAge { + last_paid_timestamp, + }; + let balance = 333_444_555_666; + let balance_input_holder = CalculatorInputHolder::DebtBalance(balance); - impl CriterionCalculator for $calculator - where - I: Iterator, - { - type Input = $input_type; + let result = vec![age_input_holder, balance_input_holder]; - fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { - self.formula.as_ref() - } - } + assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); + let mut result = result.into_iter(); + assert_eq!(result.next().unwrap().age_input(), last_paid_timestamp); + assert_eq!(result.next().unwrap().balance_input(), balance); + assert_eq!(result.next(), None) + } - impl CriterionCalculatorDiagnostics for $calculator - where - I: Iterator, - { - fn input_parameter_name(&self) -> &'static str { - $param_name - } - - #[cfg(test)] - fn diagnostics_config_location( - &self, - ) -> &Mutex::Input>>> { - &$diagnostics_config_opt - } - } - }; + #[test] + fn there_is_same_amount_of_calculator_types_as_calculator_input_holders() { + assert_eq!( + CalculatorType::VARIANT_COUNT, + CalculatorInputHolder::VARIANT_COUNT + ) + } + + #[test] + fn panics_for_age_input_when_the_enum_is_not_age_input_holder() { + test_panics_for_mismatched_input_holder( + CalculatorType::DebtAge, + |calculator_type, account| { + CalculatorInputHolder::from((calculator_type, account)).age_input(); + // should cause panic + }, + ) + } + + #[test] + fn panics_for_balance_input_when_the_enum_is_not_balance_input_holder() { + test_panics_for_mismatched_input_holder( + CalculatorType::DebtBalance, + |calculator_type, account| { + CalculatorInputHolder::from((calculator_type, account)).balance_input(); + // should cause panic + }, + ) + } + + fn test_panics_for_mismatched_input_holder( + the_only_correct_type: CalculatorType, + tested_function_call_for_panics: F, + ) where + F: Fn(CalculatorType, &PayableAccount) + RefUnwindSafe, + { + let account = make_payable_account(123); + let all_types = vec![CalculatorType::DebtBalance, CalculatorType::DebtAge]; + + assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); + all_types + .into_iter() + .filter(|calculator_type| calculator_type != &the_only_correct_type) + .for_each(|calculator_type| { + let result = + catch_unwind(|| tested_function_call_for_panics(calculator_type, &account)) + .unwrap_err(); + let panic_msg = result.downcast_ref::<&str>().unwrap(); + assert_eq!(panic_msg, &"blaaaaaaaaaaaah"); + }) + } } + +// +// pub trait CriterionCalculatorDiagnostics { +// fn input_parameter_name(&self) -> &'static str; +// #[cfg(test)] +// fn diagnostics_config_location(&self) -> &Mutex>> +// where +// Self: CriterionCalculator; +// #[cfg(test)] +// fn diagnostics_config_opt(&self) -> Option> +// where +// Self: CriterionCalculator, +// { +// self.diagnostics_config_location() +// .lock() +// .expect("diagnostics poisoned") +// .take() +// } +// #[cfg(test)] +// fn compute_formula_characteristics_for_diagnostics(&self) +// where +// Self::Input: Debug, +// Self: CriterionCalculator, +// { +// if COMPUTE_FORMULAS_CHARACTERISTICS { +// compute_progressive_characteristics( +// self.input_parameter_name(), +// self.diagnostics_config_opt(), +// self.formula(), +// ) +// } +// } +// } + +// pub trait CriteriaCalculatorIterators { +// fn calculate_age_criteria( +// self, +// payment_adjuster: &PaymentAdjusterReal, +// ) -> AgeCriterionCalculator +// where +// Self: Iterator + Sized; +// +// fn calculate_balance_criteria(self) -> BalanceCriterionCalculator +// where +// Self: Iterator + Sized; +// } + +// impl CriteriaCalculatorIterators for I +// where +// I: Iterator, +// { +// fn calculate_age_criteria( +// self, +// payment_adjuster: &PaymentAdjusterReal, +// ) -> AgeCriterionCalculator { +// AgeCriterionCalculator::new(self, payment_adjuster) +// } +// +// fn calculate_balance_criteria(self) -> BalanceCriterionCalculator { +// BalanceCriterionCalculator::new(self) +// } +// } +// +// #[macro_export] +// macro_rules! all_standard_impls_for_criterion_calculator { +// ($calculator: tt, $input_type: tt, $param_name: literal, $diagnostics_config_opt: expr) => { +// impl Iterator for $calculator +// where +// I: Iterator, +// { +// type Item = (u128, PayableAccount); +// +// fn next(&mut self) -> Option { +// self.iter.next().map(|weight_and_account| { +// let wrapped_input = Self::Item::from(weight_and_account); +// self.calculate_criterion_and_add_in_total_weight(wrapped_input) +// }) +// } +// } +// +// impl CriterionCalculator for $calculator +// where +// I: Iterator, +// { +// type Input = $input_type; +// +// fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { +// self.formula.as_ref() +// } +// } +// +// impl CriterionCalculatorDiagnostics for $calculator +// where +// I: Iterator, +// { +// fn input_parameter_name(&self) -> &'static str { +// $param_name +// } +// +// #[cfg(test)] +// fn diagnostics_config_location( +// &self, +// ) -> &Mutex::Input>>> { +// &$diagnostics_config_opt +// } +// } +// }; +// } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 57ccd5b25..dcba43730 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,7 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +#[cfg(test)] +use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ + render_complete_formulas_characteristics, COMPUTE_FORMULAS_CHARACTERISTICS, +}; +use itertools::Itertools; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; +use std::io::Read; const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; @@ -51,9 +57,12 @@ where } } -// Intended to be used through the overloaded macro diagnostics!() for better clearness -// and differentiation from the primary functionality -pub fn collection_diagnostics(label: &str, accounts: &[D]) { +// Should be used via the macro diagnostics!() for better clearness and differentiation from +// the prime functionality +pub fn collection_diagnostics( + label: &str, + accounts: &[DebuggableAccount], +) { if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { eprintln!("{}", label); accounts @@ -64,7 +73,7 @@ pub fn collection_diagnostics(label: &str, accounts: &[D]) { pub mod separately_defined_diagnostic_functions { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculatorDiagnostics; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::sub_lib::wallet::Wallet; @@ -150,9 +159,9 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn inside_calculator_local_diagnostics( + pub fn inside_calculator_local_diagnostics( wallet_ref: &Wallet, - calculator: &D, + calculator: &C, criterion: u128, added_in_the_sum: u128, ) { @@ -161,7 +170,7 @@ pub mod separately_defined_diagnostic_functions { wallet_ref, "PARTIAL CRITERION CALCULATED", "{:> = + static SUMMARIES_OF_FORMULA_CHARACTERISTICS_ONE_FOR_EACH_PARAMETER: Mutex> = Mutex::new(vec![]); // You must preserve the 'static' keyword // @@ -192,13 +220,115 @@ pub mod formulas_progressive_characteristics { // how many tests requested static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); - pub struct DiagnosticsAxisX { + fn supply_real_formulas_to_render_characteristics() { + let mut payment_adjuster = PaymentAdjusterReal::new(); + let cw_service_fee_balance = 12345; // doesn't play a role + let required_adjustment = Adjustment::ByServiceFee; // play a role neither + let now = SystemTime::now(); + payment_adjuster.initialize_inner(cw_service_fee_balance, required_adjustment, now); + + let rendering_params: Vec<(&'static str, Box, DiagnosticsAxisX)> = vec![ + ( + "BALANCE", + Box::new(BalanceCriterionCalculator::new()), + make_rendering_config_for_balance(), + ), + ( + "AGE", + Box::new(AgeCriterionCalculator::new(&payment_adjuster)), + make_rendering_config_for_age(), + ), + ]; + + rendering_params.into_iter().for_each( + |(param_name, criterion_calculator, param_rendering_config)| { + let param_calculation_formula = criterion_calculator.formula(); + compute_progressive_characteristics( + param_name, + param_calculation_formula, + param_rendering_config, + ); + }, + ) + } + + fn make_rendering_config_for_balance() -> DiagnosticsAxisX { + let literal_values = vec![ + 123_456, + 7_777_777, + 1_888_999_999_888, + 543_210_000_000_000_000_000, + ]; + let decimal_exponents = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, + ]; + let horizontal_axis_decimal_exponents = + serialize_values_on_x_axis_from_vecs(literal_values, decimal_exponents); + DiagnosticsAxisX { + non_remarkable_values_supply: horizontal_axis_decimal_exponents, + remarkable_values_opt: Some(vec![(10_u128.pow(9), "GWEI"), (10_u128.pow(18), "MASQ")]), + convertor_to_expected_formula_input_type: Box::new(|balance_wei| { + CalculatorInputHolder::DebtBalance(balance_wei) + }), + } + } + + fn make_rendering_config_for_age() -> DiagnosticsAxisX { + let now = SystemTime::now(); + let literal_values = vec![ + 1, + 5, + 9, + 25, + 44, + 50, + 75, + 180, + 600, + 900, + 33_333, + 86_400, + 255_000, + 6_700_000, + 55_333_000, + 200_300_400, + 500_000_000, + 7_000_000_000, + 78_000_000_000, + 444_333_444_444, + ]; + let decimal_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let horizontal_axis_data_supply = + serialize_values_on_x_axis_from_vecs(literal_values, decimal_exponents); + DiagnosticsAxisX { + non_remarkable_values_supply: horizontal_axis_data_supply, + remarkable_values_opt: Some(vec![ + (60, "MINUTE"), + (3_600, "HOUR"), + (86_400, "DAY"), + (604_800, "WEEK"), + ]), + convertor_to_expected_formula_input_type: Box::new( + move |secs_since_last_paid_payable| { + let native_time = now + .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling"); + CalculatorInputHolder::DebtAge { + last_paid_timestamp: native_time, + } + }, + ), + } + } + + pub struct DiagnosticsAxisX { pub non_remarkable_values_supply: Vec, pub remarkable_values_opt: Option>, - pub convertor_to_expected_formula_input_type: Box A + Send>, + pub convertor_to_expected_formula_input_type: Box CalculatorInputHolder>, } - impl DiagnosticsAxisX { + impl DiagnosticsAxisX { fn finalize_input_with_remarkable_values(&self) -> Vec { match self.remarkable_values_opt.as_ref() { Some(vals) => { @@ -216,18 +346,16 @@ pub mod formulas_progressive_characteristics { } } - pub fn render_formulas_characteristics_for_diagnostics_if_enabled() { - if COMPUTE_FORMULAS_CHARACTERISTICS { - FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { - let comprehend_debug_summary = - SUMMARIES_OF_FORMULA_CHARACTERISTICS_FOR_EACH_PARAMETER - .lock() - .expect("diagnostics poisoned") - .join("\n\n"); - - eprintln!("{}", comprehend_debug_summary) - }) - } + pub fn render_complete_formulas_characteristics() { + FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { + let comprehend_debug_summary = + SUMMARIES_OF_FORMULA_CHARACTERISTICS_ONE_FOR_EACH_PARAMETER + .lock() + .expect("diagnostics poisoned") + .join("\n\n"); + + eprintln!("{}", comprehend_debug_summary) + }) } fn render_notation( @@ -252,40 +380,36 @@ pub mod formulas_progressive_characteristics { } } - pub fn compute_progressive_characteristics( - main_param_name: &'static str, - config_opt: Option>, - formula: &dyn Fn(CriterionCalculatorInput) -> u128, - ) where - CriterionCalculatorInput: Debug, - { - config_opt.map(|mut config| { - let input_values = config.finalize_input_with_remarkable_values(); - let remarkable = config.remarkable_values_opt.take(); - let config_x_axis_type_formatter = config.convertor_to_expected_formula_input_type; - let characteristics = input_values.into_iter().map(|single_coordinate| { - let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); - let input_with_commas = - render_notation(single_coordinate, todo!("supply remarkable")); - let computed_value_with_commas = - formula(correctly_formatted_input).separate_with_commas(); - format!( - "x: {: u128, + rendering_config: DiagnosticsAxisX, + ) { + let input_values = rendering_config.finalize_input_with_remarkable_values(); + let remarkable_input_values = rendering_config + .remarkable_values_opt + .as_ref() + .map(|vals| vals.as_slice()); + let config_x_axis_type_formatter = + rendering_config.convertor_to_expected_formula_input_type; + let characteristics = input_values.into_iter().map(|single_coordinate| { + let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); + let input_with_commas = render_notation(single_coordinate, remarkable_input_values); + let computed_value_with_commas = + formula(correctly_formatted_input).separate_with_commas(); + format!( + "x: {: Vec { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b27d71e3c..8d4a151f3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -14,8 +14,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, }; -use crate::accountant::payment_adjuster::criteria_calculators::{CriteriaCalculatorIterators}; -use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; +use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics, display_formulas_characteristics_according_to_compilation_mode}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; @@ -41,8 +40,9 @@ use std::iter::once; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; -#[cfg(test)] -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::render_formulas_characteristics_for_diagnostics_if_enabled; +use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::proposed_adjusted_balance_diagnostics; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -308,8 +308,7 @@ impl PaymentAdjusterReal { let weights_and_accounts_sorted = self.calculate_weights_for_accounts(unresolved_qualified_accounts); - #[cfg(test)] - render_formulas_characteristics_for_diagnostics_if_enabled(); + display_formulas_characteristics_according_to_compilation_mode(); adjustment_runner.adjust_multiple(self, weights_and_accounts_sorted) } @@ -358,14 +357,6 @@ impl PaymentAdjusterReal { } } - fn calculate_weights_for_accounts( - &self, - accounts: Vec, - ) -> Vec<(u128, PayableAccount)> { - let zero_weights_accounts = Self::initialize_zero_weights(accounts); - self.apply_criteria(zero_weights_accounts) - } - fn propose_possible_adjustment_recursively( &mut self, weights_and_accounts: Vec<(u128, PayableAccount)>, @@ -442,13 +433,35 @@ impl PaymentAdjusterReal { weights_iterator.zip(qualified_payables.into_iter()) } + fn calculate_weights_for_accounts( + &self, + accounts: Vec, + ) -> Vec<(u128, PayableAccount)> { + let criteria_calculators: Vec> = vec![ + Box::new(BalanceCriterionCalculator::new()), + Box::new(AgeCriterionCalculator::new(self)), + ]; + + self.apply_criteria(criteria_calculators, accounts) + } + fn apply_criteria( &self, - zero_weights_accounts: impl Iterator, + criteria_calculators: Vec>, + qualified_accounts: Vec, ) -> Vec<(u128, PayableAccount)> { - let weights_and_accounts = zero_weights_accounts - .calculate_age_criteria(self) - .calculate_balance_criteria(); + let weights_and_accounts = qualified_accounts.into_iter().map(|account| { + let weight = + criteria_calculators + .iter() + .fold(0_u128, |weight, criterion_calculator| { + let calculator_type = criterion_calculator.calculator_type(); + let input_holder = CalculatorInputHolder::from((calculator_type, &account)); + let new_criterion = criterion_calculator.formula()(input_holder); + weight + new_criterion + }); + (weight, account) + }); sort_in_descendant_order_by_weights(weights_and_accounts) } From ff8ce4cf17d445785b8dd6c86fff19ecc08db9d4 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:58:38 +0800 Subject: [PATCH 116/250] GH-711: tests fixed but diagnostics stopped making it on the screen; investigate --- .../age_criterion_calculator.rs | 60 ------- .../balance_criterion_calculator.rs | 37 ---- .../criteria_calculators/mod.rs | 167 +++++------------- 3 files changed, 46 insertions(+), 218 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 5dc8b15db..62ebf69e2 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -108,65 +108,6 @@ impl AgeCriterionCalculator { } } -// #[cfg(test)] -// pub mod characteristics_config { -// use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeInput; -// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; -// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; -// use lazy_static::lazy_static; -// use std::sync::Mutex; -// use std::time::Duration; -// use std::time::SystemTime; -// -// lazy_static! { -// pub static ref AGE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { -// let now = SystemTime::now(); -// let literal_values = vec![ -// 1, -// 5, -// 9, -// 25, -// 44, -// 50, -// 75, -// 180, -// 600, -// 900, -// 33_333, -// 86_400, -// 255_000, -// 6_700_000, -// 55_333_000, -// 200_300_400, -// 500_000_000, -// 7_000_000_000, -// 78_000_000_000, -// 444_333_444_444, -// ]; -// let decadic_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; -// let horisontal_axis_data_suply = -// serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); -// Mutex::new(Some(DiagnosticsAxisX { -// non_remarkable_values_supply: horisontal_axis_data_suply, -// remarkable_values_opt: Some(vec![ -// (60, "MINUTE"), -// (3_600, "HOUR"), -// (86_400, "DAY"), -// (604_800, "WEEK"), -// ]), -// convertor_to_expected_formula_input_type: Box::new( -// move |secs_since_last_paid_payable| { -// let native_time = now -// .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) -// .expect("time travelling"); -// AgeInput(native_time) -// }, -// ), -// })) -// }; -// } -// } - #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ @@ -178,7 +119,6 @@ mod tests { CalculatorInputHolder, CalculatorType, CriterionCalculator, }; use crate::accountant::payment_adjuster::test_utils::{make_initialized_subject, Sentinel}; - use std::iter::empty; use std::time::{Duration, SystemTime}; #[test] diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 1a1c7d88e..3a73c497e 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -63,43 +63,6 @@ impl BalanceCriterionCalculator { } } -// -// #[cfg(test)] -// pub mod characteristics_config { -// use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceInput; -// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::serialize_values_on_x_axis_from_vecs; -// use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; -// use lazy_static::lazy_static; -// use std::sync::Mutex; -// -// lazy_static! { -// pub static ref BALANCE_DIAGNOSTICS_CONFIG_OPT: Mutex>> = { -// let literal_values = vec![ -// 123_456, -// 7_777_777, -// 1_888_999_999_888, -// 543_210_000_000_000_000_000, -// ]; -// let decadic_exponents = vec![ -// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -// 24, 25, -// ]; -// let horisontal_axis_decimal_exponents = -// serialize_values_on_x_axis_from_vecs(literal_values, decadic_exponents); -// Mutex::new(Some(DiagnosticsAxisX { -// non_remarkable_values_supply: horisontal_axis_decimal_exponents, -// remarkable_values_opt: Some(vec![ -// (10_u128.pow(9), "GWEI"), -// (10_u128.pow(18), "MASQ"), -// ]), -// convertor_to_expected_formula_input_type: Box::new(|balance_wei| { -// BalanceInput(balance_wei) -// }), -// })) -// }; -// } -// } - #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index d94ccc477..2fbccbd57 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -4,7 +4,6 @@ pub mod age_criterion_calculator; pub mod balance_criterion_calculator; use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::inside_calculator_local_diagnostics; use std::fmt::{Debug, Display, Formatter}; use std::time::SystemTime; use variant_count::VariantCount; @@ -44,7 +43,7 @@ impl CalculatorInputHolder { { last_paid_timestamp } else { - todo!() + self.mismatched_call_panic("age") } } @@ -52,9 +51,16 @@ impl CalculatorInputHolder { if let CalculatorInputHolder::DebtBalance(balance_wei) = self { balance_wei } else { - todo!() + self.mismatched_call_panic("balance") } } + + fn mismatched_call_panic(&self, param_name: &str) -> ! { + panic!( + "Call for {} while the underlying enum variant is {:?}", + param_name, self + ) + } } impl<'a> From<((CalculatorType, &'a PayableAccount))> for CalculatorInputHolder { @@ -180,127 +186,46 @@ mod tests { ) where F: Fn(CalculatorType, &PayableAccount) + RefUnwindSafe, { - let account = make_payable_account(123); - let all_types = vec![CalculatorType::DebtBalance, CalculatorType::DebtAge]; + let account = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 12345, + last_paid_timestamp: SystemTime::UNIX_EPOCH, + pending_payable_opt: None, + }; + let all_types = vec![ + (CalculatorType::DebtBalance, "balance", "DebtBalance(12345)"), + ( + CalculatorType::DebtAge, + "age", + "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }", + ), + ]; assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); + let (_, the_right_param_literal_name, _) = all_types + .iter() + .find(|(calculator_type, param_name, _)| calculator_type == &the_only_correct_type) + .unwrap(); + // To be able to shut the reference before we consume the vec + let the_right_param_literal_name = the_right_param_literal_name.to_string(); all_types .into_iter() - .filter(|calculator_type| calculator_type != &the_only_correct_type) - .for_each(|calculator_type| { - let result = - catch_unwind(|| tested_function_call_for_panics(calculator_type, &account)) - .unwrap_err(); - let panic_msg = result.downcast_ref::<&str>().unwrap(); - assert_eq!(panic_msg, &"blaaaaaaaaaaaah"); - }) + .filter(|(calculator_type, _, _)| calculator_type != &the_only_correct_type) + .for_each( + |(calculator_type, _, debug_rendering_of_the_corresponding_input_holder)| { + let result = + catch_unwind(|| tested_function_call_for_panics(calculator_type, &account)) + .unwrap_err(); + let panic_msg = result.downcast_ref::().unwrap(); + assert_eq!( + panic_msg, + &format!( + "Call for {} while the underlying enum variant is {}", + the_right_param_literal_name, + debug_rendering_of_the_corresponding_input_holder + ) + ); + }, + ) } } - -// -// pub trait CriterionCalculatorDiagnostics { -// fn input_parameter_name(&self) -> &'static str; -// #[cfg(test)] -// fn diagnostics_config_location(&self) -> &Mutex>> -// where -// Self: CriterionCalculator; -// #[cfg(test)] -// fn diagnostics_config_opt(&self) -> Option> -// where -// Self: CriterionCalculator, -// { -// self.diagnostics_config_location() -// .lock() -// .expect("diagnostics poisoned") -// .take() -// } -// #[cfg(test)] -// fn compute_formula_characteristics_for_diagnostics(&self) -// where -// Self::Input: Debug, -// Self: CriterionCalculator, -// { -// if COMPUTE_FORMULAS_CHARACTERISTICS { -// compute_progressive_characteristics( -// self.input_parameter_name(), -// self.diagnostics_config_opt(), -// self.formula(), -// ) -// } -// } -// } - -// pub trait CriteriaCalculatorIterators { -// fn calculate_age_criteria( -// self, -// payment_adjuster: &PaymentAdjusterReal, -// ) -> AgeCriterionCalculator -// where -// Self: Iterator + Sized; -// -// fn calculate_balance_criteria(self) -> BalanceCriterionCalculator -// where -// Self: Iterator + Sized; -// } - -// impl CriteriaCalculatorIterators for I -// where -// I: Iterator, -// { -// fn calculate_age_criteria( -// self, -// payment_adjuster: &PaymentAdjusterReal, -// ) -> AgeCriterionCalculator { -// AgeCriterionCalculator::new(self, payment_adjuster) -// } -// -// fn calculate_balance_criteria(self) -> BalanceCriterionCalculator { -// BalanceCriterionCalculator::new(self) -// } -// } -// -// #[macro_export] -// macro_rules! all_standard_impls_for_criterion_calculator { -// ($calculator: tt, $input_type: tt, $param_name: literal, $diagnostics_config_opt: expr) => { -// impl Iterator for $calculator -// where -// I: Iterator, -// { -// type Item = (u128, PayableAccount); -// -// fn next(&mut self) -> Option { -// self.iter.next().map(|weight_and_account| { -// let wrapped_input = Self::Item::from(weight_and_account); -// self.calculate_criterion_and_add_in_total_weight(wrapped_input) -// }) -// } -// } -// -// impl CriterionCalculator for $calculator -// where -// I: Iterator, -// { -// type Input = $input_type; -// -// fn formula(&self) -> &dyn Fn(Self::Input) -> u128 { -// self.formula.as_ref() -// } -// } -// -// impl CriterionCalculatorDiagnostics for $calculator -// where -// I: Iterator, -// { -// fn input_parameter_name(&self) -> &'static str { -// $param_name -// } -// -// #[cfg(test)] -// fn diagnostics_config_location( -// &self, -// ) -> &Mutex::Input>>> { -// &$diagnostics_config_opt -// } -// } -// }; -// } From 3f37e41ed00c4e4822af56e521028a7b45d9c62e Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:51:10 +0800 Subject: [PATCH 117/250] GH-711: diagnostics repaired --- .../age_criterion_calculator.rs | 7 +- .../balance_criterion_calculator.rs | 5 -- .../criteria_calculators/mod.rs | 13 +-- .../payment_adjuster/diagnostics.rs | 86 ++++++++----------- node/src/accountant/payment_adjuster/mod.rs | 47 +++++----- .../accountant/payment_adjuster/test_utils.rs | 3 - 6 files changed, 66 insertions(+), 95 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 62ebf69e2..1c78aeeb4 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -1,16 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::time::SystemTime; -test_only_use!( - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; - use std::sync::Mutex; -); const AGE_MAIN_EXPONENT: u32 = 3; const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; @@ -118,7 +113,7 @@ mod tests { use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; - use crate::accountant::payment_adjuster::test_utils::{make_initialized_subject, Sentinel}; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use std::time::{Duration, SystemTime}; #[test] diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 3a73c497e..09ec3e25b 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -1,14 +1,9 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; -test_only_use!( - use std::sync::Mutex; - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::DiagnosticsAxisX; -); // This parameter affects the steepness inversely, but just slowly. // diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 2fbccbd57..18d7cc7f7 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -7,12 +7,6 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use std::fmt::{Debug, Display, Formatter}; use std::time::SystemTime; use variant_count::VariantCount; -test_only_use!( - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - compute_progressive_characteristics, DiagnosticsAxisX, COMPUTE_FORMULAS_CHARACTERISTICS, - }; - use std::sync::Mutex; -); // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { @@ -63,7 +57,7 @@ impl CalculatorInputHolder { } } -impl<'a> From<((CalculatorType, &'a PayableAccount))> for CalculatorInputHolder { +impl<'a> From<(CalculatorType, &'a PayableAccount)> for CalculatorInputHolder { fn from((calculator_type, account): (CalculatorType, &'a PayableAccount)) -> Self { match calculator_type { CalculatorType::DebtBalance => CalculatorInputHolder::DebtBalance(account.balance_wei), @@ -89,15 +83,12 @@ mod tests { use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, }; - use crate::accountant::payment_adjuster::PaymentAdjusterReal; - use crate::accountant::test_utils::make_payable_account; use crate::test_utils::make_wallet; use std::panic::{catch_unwind, RefUnwindSafe}; use std::time::{Duration, SystemTime}; #[test] fn input_holders_can_be_derived_from_calculator_type_and_payable_account() { - let payment_adjuster = PaymentAdjusterReal::new(); let balance_wei = 135_792_468; let last_paid_timestamp = SystemTime::now() .checked_sub(Duration::from_secs(3)) @@ -204,7 +195,7 @@ mod tests { assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); let (_, the_right_param_literal_name, _) = all_types .iter() - .find(|(calculator_type, param_name, _)| calculator_type == &the_only_correct_type) + .find(|(calculator_type, _, _)| calculator_type == &the_only_correct_type) .unwrap(); // To be able to shut the reference before we consume the vec let the_right_param_literal_name = the_right_param_literal_name.to_string(); diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index dcba43730..f46b8f726 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -4,10 +4,8 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ render_complete_formulas_characteristics, COMPUTE_FORMULAS_CHARACTERISTICS, }; -use itertools::Itertools; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -use std::io::Read; const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; @@ -159,9 +157,9 @@ pub mod separately_defined_diagnostic_functions { ); } - pub fn inside_calculator_local_diagnostics( + pub fn calculated_criterion_and_weight_diagnostics( wallet_ref: &Wallet, - calculator: &C, + calculator: &dyn CriterionCalculator, criterion: u128, added_in_the_sum: u128, ) { @@ -197,35 +195,36 @@ pub mod formulas_progressive_characteristics { use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CriterionCalculator, }; - use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use itertools::Itertools; - use std::fmt::Debug; use std::fs::File; use std::io::Read; use std::iter::once; use std::path::Path; - use std::sync::{Mutex, Once}; + use std::sync::Once; use std::time::{Duration, SystemTime}; use thousands::Separable; // For debugging and tuning up purposes. It lets you see the curve of calculated criterion // in dependence to different values of a distinct parameter pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; - // You must preserve the 'static' keyword - static SUMMARIES_OF_FORMULA_CHARACTERISTICS_ONE_FOR_EACH_PARAMETER: Mutex> = - Mutex::new(vec![]); + // You must preserve the 'static' keyword // // The singleton ensures that the characteristics are always displayed only once, no matter // how many tests requested static FORMULAS_CHARACTERISTICS_SINGLETON: Once = Once::new(); - fn supply_real_formulas_to_render_characteristics() { - let mut payment_adjuster = PaymentAdjusterReal::new(); - let cw_service_fee_balance = 12345; // doesn't play a role - let required_adjustment = Adjustment::ByServiceFee; // play a role neither - let now = SystemTime::now(); - payment_adjuster.initialize_inner(cw_service_fee_balance, required_adjustment, now); + pub fn render_complete_formulas_characteristics() { + FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { + let comprehend_debug_summary = supply_real_formulas_to_render_characteristics(); + + eprintln!("{}", comprehend_debug_summary) + }) + } + + fn supply_real_formulas_to_render_characteristics() -> String { + let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); let rendering_params: Vec<(&'static str, Box, DiagnosticsAxisX)> = vec![ ( @@ -240,16 +239,19 @@ pub mod formulas_progressive_characteristics { ), ]; - rendering_params.into_iter().for_each( - |(param_name, criterion_calculator, param_rendering_config)| { - let param_calculation_formula = criterion_calculator.formula(); - compute_progressive_characteristics( - param_name, - param_calculation_formula, - param_rendering_config, - ); - }, - ) + rendering_params + .into_iter() + .map( + |(param_name, criterion_calculator, param_rendering_config)| { + let param_calculation_formula = criterion_calculator.formula(); + compute_progressive_characteristics( + param_name, + param_calculation_formula, + param_rendering_config, + ) + }, + ) + .join("\n\n") } fn make_rendering_config_for_balance() -> DiagnosticsAxisX { @@ -322,10 +324,10 @@ pub mod formulas_progressive_characteristics { } } - pub struct DiagnosticsAxisX { - pub non_remarkable_values_supply: Vec, - pub remarkable_values_opt: Option>, - pub convertor_to_expected_formula_input_type: Box CalculatorInputHolder>, + struct DiagnosticsAxisX { + non_remarkable_values_supply: Vec, + remarkable_values_opt: Option>, + convertor_to_expected_formula_input_type: Box CalculatorInputHolder>, } impl DiagnosticsAxisX { @@ -346,18 +348,6 @@ pub mod formulas_progressive_characteristics { } } - pub fn render_complete_formulas_characteristics() { - FORMULAS_CHARACTERISTICS_SINGLETON.call_once(|| { - let comprehend_debug_summary = - SUMMARIES_OF_FORMULA_CHARACTERISTICS_ONE_FOR_EACH_PARAMETER - .lock() - .expect("diagnostics poisoned") - .join("\n\n"); - - eprintln!("{}", comprehend_debug_summary) - }) - } - fn render_notation( coordinate_value: u128, remarkable_vals: Option<&[(u128, &'static str)]>, @@ -380,11 +370,11 @@ pub mod formulas_progressive_characteristics { } } - pub fn compute_progressive_characteristics( + fn compute_progressive_characteristics( param_name: &'static str, formula: &dyn Fn(CalculatorInputHolder) -> u128, rendering_config: DiagnosticsAxisX, - ) { + ) -> String { let input_values = rendering_config.finalize_input_with_remarkable_values(); let remarkable_input_values = rendering_config .remarkable_values_opt @@ -404,12 +394,8 @@ pub mod formulas_progressive_characteristics { length = 40 ) }); - let head = once(format!("CHARACTERISTICS OF THE FORMULA FOR {}", param_name)); - let full_text = head.into_iter().chain(characteristics).join("\n"); - SUMMARIES_OF_FORMULA_CHARACTERISTICS_ONE_FOR_EACH_PARAMETER - .lock() - .expect("diagnostics poisoned") - .push(full_text); + let head = once(format!("CHARACTERISTICS OF {} FORMULA", param_name)); + head.into_iter().chain(characteristics).join("\n") } fn read_diagnostics_inputs_from_file(path: &Path) -> Vec { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8d4a151f3..07a5db161 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -36,14 +36,13 @@ use itertools::Either; use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; -use std::iter::once; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::proposed_adjusted_balance_diagnostics; +use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -419,20 +418,6 @@ impl PaymentAdjusterReal { } } - fn initialize_zero_weights( - qualified_payables: Vec, - ) -> impl Iterator { - fn zero_weights_iterator(accounts_count: usize) -> impl Iterator { - let one_element = once(0_u128); - let endlessly_repeated = one_element.into_iter().cycle(); - endlessly_repeated.take(accounts_count) - } - - let accounts_count = qualified_payables.len(); - let weights_iterator = zero_weights_iterator(accounts_count); - weights_iterator.zip(qualified_payables.into_iter()) - } - fn calculate_weights_for_accounts( &self, accounts: Vec, @@ -455,17 +440,39 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let calculator_type = criterion_calculator.calculator_type(); - let input_holder = CalculatorInputHolder::from((calculator_type, &account)); - let new_criterion = criterion_calculator.formula()(input_holder); - weight + new_criterion + let new_criterion = Self::calculate_criterion_by_calculator( + &**criterion_calculator, + &account, + ); + + let summed_up = weight + new_criterion; + + calculated_criterion_and_weight_diagnostics( + &account.wallet, + &**criterion_calculator, + new_criterion, + summed_up, + ); + + summed_up }); + (weight, account) }); sort_in_descendant_order_by_weights(weights_and_accounts) } + fn calculate_criterion_by_calculator( + criterion_calculator: &dyn CriterionCalculator, + account: &PayableAccount, + ) -> u128 { + let calculator_type = criterion_calculator.calculator_type(); + let input_holder = CalculatorInputHolder::from((calculator_type, account)); + + criterion_calculator.formula()(input_holder) + } + fn perform_adjustment_by_service_fee( &self, weights_and_accounts: Vec<(u128, PayableAccount)>, diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 57e327c76..6a7dd4fe1 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -10,7 +10,6 @@ use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; -use std::iter::Empty; use std::time::{Duration, SystemTime}; lazy_static! { @@ -62,5 +61,3 @@ pub fn make_extreme_accounts( }) .collect() } - -pub type Sentinel = Empty<(u128, PayableAccount)>; From 00b3c87e7095cc2769d51afaaf5ce0cdef6518f7 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:37:46 +0800 Subject: [PATCH 118/250] GH-711: diagnostics can read input from files now --- node/Cargo.toml | 6 +- .../payment_adjuster/diagnostics.rs | 297 +++++++++++------- .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 2 +- node/src/test_utils/database_utils.rs | 9 +- .../database_version_0_sqls.txt} | 0 ...r_diagnostics_of_age_criterion_formula.txt | 59 ++++ ...agnostics_of_balance_criterion_formula.txt | 22 ++ node/src/test_utils/mod.rs | 9 + 9 files changed, 273 insertions(+), 133 deletions(-) rename node/src/test_utils/{database_version_0_sql.txt => input_data/database_version_0_sqls.txt} (100%) create mode 100644 node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt create mode 100644 node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt diff --git a/node/Cargo.toml b/node/Cargo.toml index 3635fc1de..4a9a992b7 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -15,7 +15,6 @@ automap = { path = "../automap"} backtrace = "0.3.57" base64 = "0.13.0" bytes = "0.4.12" -time = {version = "0.3.11", features = [ "macros" ]} clap = "2.33.3" crossbeam-channel = "0.5.1" dirs = "4.0.0" @@ -44,6 +43,7 @@ rlp = "0.4.6" rpassword = "5.0.1" rusqlite = {version = "0.28.0", features = ["bundled","functions"]} rustc-hex = "2.1.0" +secp256k1secrets = {package = "secp256k1", version = "0.17.2"} serde = "1.0.136" serde_derive = "1.0.136" serde_json = "1.0.79" @@ -51,6 +51,7 @@ serde_cbor = "0.11.2" sha1 = "0.6.0" sodiumoxide = "0.2.2" sysinfo = "0.21.1" +time = {version = "0.3.11", features = [ "macros" ]} tiny-bip39 = "0.8.2" tiny-hderive = "0.3.0" thousands = "0.2.0" @@ -60,11 +61,10 @@ toml = "0.5.8" trust-dns = "0.17.0" trust-dns-resolver = "0.12.0" unindent = "0.1.7" +uuid = "0.7.4" variant_count = "1.1.0" web3 = {version = "0.11.0", default-features = false, features = ["http", "tls"]} websocket = {version = "0.26.2", default-features = false, features = ["async", "sync"]} -secp256k1secrets = {package = "secp256k1", version = "0.17.2"} -uuid = "0.7.4" [target.'cfg(target_os = "macos")'.dependencies] system-configuration = "0.4.0" diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index f46b8f726..8919478a1 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -69,7 +69,7 @@ pub fn collection_diagnostics( } } -pub mod separately_defined_diagnostic_functions { +pub mod ordinary_diagnostic_functions { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; @@ -196,17 +196,23 @@ pub mod formulas_progressive_characteristics { CalculatorInputHolder, CriterionCalculator, }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::test_utils::unshared_test_utils::standard_dir_for_test_input_data; use itertools::Itertools; + use serde::de::Error; + use serde::{Deserialize as NormalImplDeserialize, Deserializer}; + use serde_derive::Deserialize; + use serde_json::Value; use std::fs::File; use std::io::Read; use std::iter::once; - use std::path::Path; + use std::path::{Path, PathBuf}; use std::sync::Once; use std::time::{Duration, SystemTime}; use thousands::Separable; - // For debugging and tuning up purposes. It lets you see the curve of calculated criterion - // in dependence to different values of a distinct parameter + // For the purposes of debugging and tuning the formulas up to work well together. It lets you + // imagine the curve of a criterion in dependence to different supplied input values for + // the give parameter pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; // You must preserve the 'static' keyword @@ -255,22 +261,12 @@ pub mod formulas_progressive_characteristics { } fn make_rendering_config_for_balance() -> DiagnosticsAxisX { - let literal_values = vec![ - 123_456, - 7_777_777, - 1_888_999_999_888, - 543_210_000_000_000_000_000, - ]; - let decimal_exponents = vec![ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, - ]; - let horizontal_axis_decimal_exponents = - serialize_values_on_x_axis_from_vecs(literal_values, decimal_exponents); + let file_path = file_path("input_data_for_diagnostics_of_balance_criterion_formula.txt"); + let horizontal_axis_data_supply = read_diagnostics_inputs_from_file(&file_path); + DiagnosticsAxisX { - non_remarkable_values_supply: horizontal_axis_decimal_exponents, - remarkable_values_opt: Some(vec![(10_u128.pow(9), "GWEI"), (10_u128.pow(18), "MASQ")]), - convertor_to_expected_formula_input_type: Box::new(|balance_wei| { + values: horizontal_axis_data_supply, + convertor_to_expected_formula_input: Box::new(|balance_wei| { CalculatorInputHolder::DebtBalance(balance_wei) }), } @@ -278,95 +274,133 @@ pub mod formulas_progressive_characteristics { fn make_rendering_config_for_age() -> DiagnosticsAxisX { let now = SystemTime::now(); - let literal_values = vec![ - 1, - 5, - 9, - 25, - 44, - 50, - 75, - 180, - 600, - 900, - 33_333, - 86_400, - 255_000, - 6_700_000, - 55_333_000, - 200_300_400, - 500_000_000, - 7_000_000_000, - 78_000_000_000, - 444_333_444_444, - ]; - let decimal_exponents = vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - let horizontal_axis_data_supply = - serialize_values_on_x_axis_from_vecs(literal_values, decimal_exponents); + let file_path = file_path("input_data_for_diagnostics_of_age_criterion_formula.txt"); + let horizontal_axis_data_supply = read_diagnostics_inputs_from_file(&file_path); + let convertor_to_expected_formula_input_type = Box::new( + move |secs_since_last_paid_payable: u128| { + let native_time = now + .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) + .expect("time travelling"); + CalculatorInputHolder::DebtAge { + last_paid_timestamp: native_time, + } + }, + ); + DiagnosticsAxisX { - non_remarkable_values_supply: horizontal_axis_data_supply, - remarkable_values_opt: Some(vec![ - (60, "MINUTE"), - (3_600, "HOUR"), - (86_400, "DAY"), - (604_800, "WEEK"), - ]), - convertor_to_expected_formula_input_type: Box::new( - move |secs_since_last_paid_payable| { - let native_time = now - .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling"); - CalculatorInputHolder::DebtAge { - last_paid_timestamp: native_time, - } - }, - ), + values: horizontal_axis_data_supply, + convertor_to_expected_formula_input: convertor_to_expected_formula_input_type, } } + fn file_path(file_name: &str)->PathBuf{ + standard_dir_for_test_input_data() + .join(file_name) + } + + #[derive(Deserialize)] + struct InputFromFile { + literals: Vec, + decimal_exponents: Vec, + marked_values: Vec, + } + + #[derive(Deserialize)] + struct MarkedValueFromFile { + value: IntegerValueAllowingUnderscores, + label: String, + } + + struct MarkedValue { + value: u128, + label: String, + } + + struct DeserializedInputValues { + non_marked_values: Vec, + marked_values: Vec, + } + struct DiagnosticsAxisX { - non_remarkable_values_supply: Vec, - remarkable_values_opt: Option>, - convertor_to_expected_formula_input_type: Box CalculatorInputHolder>, + values: DeserializedInputValues, + convertor_to_expected_formula_input: Box CalculatorInputHolder>, + } + + struct IntegerValueAllowingUnderscores { + numerical_value: u128, + } + + impl IntegerValueAllowingUnderscores { + fn new(value: u128) -> Self { + Self { + numerical_value: value, + } + } + } + + impl<'de> NormalImplDeserialize<'de> for IntegerValueAllowingUnderscores { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value: Value = NormalImplDeserialize::deserialize(deserializer)?; + if let Value::String(str) = value { + let underscore_less = str.chars().filter(|char| char != &'_').collect::(); + let num: u128 = underscore_less.parse().unwrap(); + Ok(IntegerValueAllowingUnderscores::new(num)) + } else { + Err(D::Error::custom(format!( + "Expected a string value but found: {:?}", + value + ))) + } + } + } + + impl From for MarkedValue { + fn from(marked_value_from_file: MarkedValueFromFile) -> Self { + MarkedValue { + value: marked_value_from_file.value.numerical_value, + label: marked_value_from_file.label, + } + } } impl DiagnosticsAxisX { - fn finalize_input_with_remarkable_values(&self) -> Vec { - match self.remarkable_values_opt.as_ref() { - Some(vals) => { - let filtered_remarkable_values = vals.iter().map(|(num, _)| num); - let standard_input = self.non_remarkable_values_supply.iter(); - filtered_remarkable_values - .chain(standard_input) - .sorted() - .dedup() - .map(|num| *num) - .collect() - } - None => self.non_remarkable_values_supply.clone(), + fn finalize_input_with_marked_values(&self) -> Vec { + if self.values.marked_values.is_empty() { + self.values.non_marked_values.clone() + } else { + let filtered_marked_values = self + .values + .marked_values + .iter() + .map(|marked_val| &marked_val.value); + let standard_input = self.values.non_marked_values.iter(); + filtered_marked_values + .chain(standard_input) + .sorted() + .dedup() + .map(|num| *num) + .collect() } } } - fn render_notation( - coordinate_value: u128, - remarkable_vals: Option<&[(u128, &'static str)]>, - ) -> String { - match should_mark_be_used(coordinate_value, remarkable_vals) { + fn render_notation(coordinate_value: u128, marked_vals: &[MarkedValue]) -> String { + match should_mark_be_used(coordinate_value, marked_vals) { Some(mark) => format!("{} {}", coordinate_value.separate_with_commas(), mark), None => coordinate_value.separate_with_commas(), } } - fn should_mark_be_used( - coordinate_value: u128, - remarkable_vals: Option<&[(u128, &'static str)]>, - ) -> Option<&'static str> { - match remarkable_vals { - Some(vals) => vals + fn should_mark_be_used(coordinate_value: u128, marked_vals: &[MarkedValue]) -> Option { + if marked_vals.is_empty() { + None + } else { + marked_vals .iter() - .find(|(val, _)| coordinate_value == *val) - .map(|(_, mark)| *mark), - None => None, + .find(|marked_val| marked_val.value == coordinate_value) + .map(|marked_val| marked_val.label.clone()) } } @@ -375,16 +409,14 @@ pub mod formulas_progressive_characteristics { formula: &dyn Fn(CalculatorInputHolder) -> u128, rendering_config: DiagnosticsAxisX, ) -> String { - let input_values = rendering_config.finalize_input_with_remarkable_values(); - let remarkable_input_values = rendering_config - .remarkable_values_opt - .as_ref() - .map(|vals| vals.as_slice()); + let input_values = rendering_config.finalize_input_with_marked_values(); + let marked_input_values = rendering_config.values.marked_values.as_slice(); let config_x_axis_type_formatter = - rendering_config.convertor_to_expected_formula_input_type; + rendering_config.convertor_to_expected_formula_input; + let characteristics = input_values.into_iter().map(|single_coordinate| { let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); - let input_with_commas = render_notation(single_coordinate, remarkable_input_values); + let input_with_commas = render_notation(single_coordinate, marked_input_values); let computed_value_with_commas = formula(correctly_formatted_input).separate_with_commas(); format!( @@ -394,38 +426,61 @@ pub mod formulas_progressive_characteristics { length = 40 ) }); + let head = once(format!("CHARACTERISTICS OF {} FORMULA", param_name)); head.into_iter().chain(characteristics).join("\n") } - fn read_diagnostics_inputs_from_file(path: &Path) -> Vec { - let mut file = File::open(path).expect("inputs badly prepared"); + fn read_diagnostics_inputs_from_file(path: &Path) -> DeserializedInputValues { + let mut file = File::open(path).unwrap_or_else(|e| { + panic!("Inputs badly prepared at path: {:?}, error: {:?}", path, e) + }); let mut buffer = String::new(); file.read_to_string(&mut buffer).unwrap(); - let mut first_two_lines = buffer.lines().take(2); - let first = first_two_lines.next().expect("first line missing"); - let second = first_two_lines.next().expect("second line missing"); - let first_line_starter = extract_line_starter(first); - if extract_line_starter(first) != "literals:" - || extract_line_starter(second) != "decimal_exponents" - { - panic!("Inputs in the file in {:?} should have the following format. First line starting \ - \"literals:\", second line with \"decimal_exponents:\", both immediately followed by comma \ - separated integers or left blank", path) - } - serialize_values_on_x_axis_from_vecs( - parse_numbers_from_line(first), - parse_numbers_from_line(second), - ) - } + let plain_json: String = buffer + .lines() + .filter(|line| !line.is_empty() && !line_is_comment(line)) + .collect(); + let processed_json_input = serde_json::from_str::(&plain_json) + .unwrap_or_else(|e| { + panic!( + "Error {:?} for file path: {:?}. Read string: {}", + e, path, plain_json + ) + }); + + let nums_declared_as_literals = processed_json_input + .literals + .into_iter() + .map(|wrapper| wrapper.numerical_value) + .collect(); + + let marked_values = processed_json_input + .marked_values + .into_iter() + .map(|marked_value_from_file| MarkedValue::from(marked_value_from_file)) + .collect(); - fn extract_line_starter(line: &str) -> String { - line.chars().take_while(|char| !char.is_numeric()).collect() + DeserializedInputValues { + non_marked_values: serialize_values_on_x_axis_from_vecs( + nums_declared_as_literals, + processed_json_input.decimal_exponents, + ), + marked_values, + } } - fn parse_numbers_from_line(line: &str) -> Vec { - todo!("implement me"); - // line.chars().take() + fn line_is_comment(line: &str) -> bool { + let char_collection = line + .chars() + .skip_while(|char| char.is_ascii_whitespace()) + .take(1) + .collect::>(); + let first_meaningful_char_opt = char_collection.first(); + if first_meaningful_char_opt.is_none() { + panic!("Something went wrong. Empty lines should have been already tested") + } + first_meaningful_char_opt.unwrap() == &'#' } pub fn serialize_values_on_x_axis_from_vecs( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 983fe8896..00a1c384e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -2,7 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{ +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ account_nominated_for_disqualification_diagnostics, exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, possibly_outweighed_accounts_diagnostics, try_finding_an_account_to_disqualify_diagnostics, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 07a5db161..d603999a0 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -42,7 +42,7 @@ use web3::types::U256; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; -use crate::accountant::payment_adjuster::diagnostics::separately_defined_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index ac64b0681..f6203946a 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -5,14 +5,13 @@ use crate::accountant::db_access_objects::utils::VigilantRusqliteFlatten; use crate::database::connection_wrapper::ConnectionWrapper; use crate::database::db_initializer::ExternalData; - use crate::database::db_migrations::db_migrator::DbMigrator; +use crate::test_utils::unshared_test_utils::standard_dir_for_test_input_data; use masq_lib::logger::Logger; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::utils::{to_string, NeighborhoodModeLight}; use rusqlite::{Connection, Error}; use std::cell::RefCell; -use std::env::current_dir; use std::fs::{remove_file, File}; use std::io::Read; use std::iter::once; @@ -26,11 +25,7 @@ pub fn bring_db_0_back_to_life_and_return_connection(db_path: &Path) -> Connecti _ => (), }; let conn = Connection::open(&db_path).unwrap(); - let file_path = current_dir() - .unwrap() - .join("src") - .join("test_utils") - .join("database_version_0_sql.txt"); + let file_path = standard_dir_for_test_input_data().join("database_version_0_sqls.txt"); let mut file = File::open(file_path).unwrap(); let mut buffer = String::new(); file.read_to_string(&mut buffer).unwrap(); diff --git a/node/src/test_utils/database_version_0_sql.txt b/node/src/test_utils/input_data/database_version_0_sqls.txt similarity index 100% rename from node/src/test_utils/database_version_0_sql.txt rename to node/src/test_utils/input_data/database_version_0_sqls.txt diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt new file mode 100644 index 000000000..c36d0c97d --- /dev/null +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt @@ -0,0 +1,59 @@ + +# These values follow the standard JSON format and will be processed into a coherent set of inputs to feed the respective +# criterion formula in the test. There are three different categories of values that can be supplied this way: +# "literals", "decimal_exponents" and "marked_values". +# +# Every value is allowed to incorporate an underscore ("_") for easier distinguishing numeral orders. +# +# First two come as name-value pairs with values in an array. +# +# Marked values come as objects with two fields: "value" and "label". +# If the same value has been defined by any of the other categories, marked values will replace them with no conflict. +# +# At least one category must be supplied while not empty at a time. + +{ + "literals": [ + "1", + "5", + "9", + "25", + "44", + "50", + "75", + "180", + "600", + "900", + "33_333", + "86_400", + "255_000", + "6_700_000", + "55_333_000", + "200_300_400", + "500_000_000", + "7_000_000_000", + "78_000_000_000", + "444_333_444_444" + ], + "decimal_exponents": [ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 + ], + "marked_values": [ + { + "value": "60", + "label": "MINUTE" + }, + { + "value": "3_600", + "label": "HOUR" + }, + { + "value": "86_400", + "label": "DAY" + }, + { + "value": "604_800", + "label": "WEEK" + } + ] +} \ No newline at end of file diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt new file mode 100644 index 000000000..9509e2eba --- /dev/null +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt @@ -0,0 +1,22 @@ + +{ + "literals": [ + "123_456", + "7_777_777", + "1_888_999_999_888", + "543_210_000_000_000_000_000" + ], + "decimal_exponents": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ], + "marked_values": [ + { + "value": "1_000_000_000", + "label": "GWEI" + }, + { + "value": "1_000_000_000_000_000_000", + "label": "MASQ" + } + ] +} \ No newline at end of file diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 9acff0c81..a7fe4ee0d 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -567,6 +567,7 @@ pub mod unshared_test_utils { use std::any::TypeId; use std::cell::RefCell; use std::collections::HashMap; + use std::env::current_dir; use std::num::ParseIntError; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::path::{Path, PathBuf}; @@ -814,6 +815,14 @@ pub mod unshared_test_utils { .collect() } + pub fn standard_dir_for_test_input_data() -> PathBuf { + current_dir() + .unwrap() + .join("src") + .join("test_utils") + .join("input_data") + } + pub mod system_killer_actor { use super::*; From 379880484b25eca2707ecd3b0b6a67032a55e3c3 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:52:44 +0800 Subject: [PATCH 119/250] GH-711: small details --- .../accountant/payment_adjuster/diagnostics.rs | 15 ++++++--------- node/src/accountant/payment_adjuster/mod.rs | 2 +- ...or_diagnostics_of_age_criterion_formula.txt | 18 +++++++++--------- ...iagnostics_of_balance_criterion_formula.txt | 15 ++++++++++++++- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 8919478a1..7107b2564 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -276,16 +276,15 @@ pub mod formulas_progressive_characteristics { let now = SystemTime::now(); let file_path = file_path("input_data_for_diagnostics_of_age_criterion_formula.txt"); let horizontal_axis_data_supply = read_diagnostics_inputs_from_file(&file_path); - let convertor_to_expected_formula_input_type = Box::new( - move |secs_since_last_paid_payable: u128| { + let convertor_to_expected_formula_input_type = + Box::new(move |secs_since_last_paid_payable: u128| { let native_time = now .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) .expect("time travelling"); CalculatorInputHolder::DebtAge { last_paid_timestamp: native_time, } - }, - ); + }); DiagnosticsAxisX { values: horizontal_axis_data_supply, @@ -293,9 +292,8 @@ pub mod formulas_progressive_characteristics { } } - fn file_path(file_name: &str)->PathBuf{ - standard_dir_for_test_input_data() - .join(file_name) + fn file_path(file_name: &str) -> PathBuf { + standard_dir_for_test_input_data().join(file_name) } #[derive(Deserialize)] @@ -411,8 +409,7 @@ pub mod formulas_progressive_characteristics { ) -> String { let input_values = rendering_config.finalize_input_with_marked_values(); let marked_input_values = rendering_config.values.marked_values.as_slice(); - let config_x_axis_type_formatter = - rendering_config.convertor_to_expected_formula_input; + let config_x_axis_type_formatter = rendering_config.convertor_to_expected_formula_input; let characteristics = input_values.into_iter().map(|single_coordinate| { let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d603999a0..044ca1073 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -//try to keep these modules private +// If possible, let these modules be private mod adjustment_runners; mod criteria_calculators; mod diagnostics; diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt index c36d0c97d..77aabd37e 100644 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt @@ -1,17 +1,17 @@ - -# These values follow the standard JSON format and will be processed into a coherent set of inputs to feed the respective -# criterion formula in the test. There are three different categories of values that can be supplied this way: -# "literals", "decimal_exponents" and "marked_values". # -# Every value is allowed to incorporate an underscore ("_") for easier distinguishing numeral orders. +# The data follows the standard JSON format and will be further processed into a row of integers to feed the respective +# criterion formula to be rendered. There are three different categories of values that can be supplied via an object +# with the following fields: "literals", "decimal_exponents" and "marked_values". # -# First two come as name-value pairs with values in an array. +# Literals are as string declared integers in an array whose notation can be made better readable by use of underscores. +# +# Decimal exponents are plain integers in an array and represent powers of a ten. +# +# Marked values come as objects with two fields: "value" and "label", both expecting string values. The "value" field +# also accepts underscores in the integers supplied. # -# Marked values come as objects with two fields: "value" and "label". # If the same value has been defined by any of the other categories, marked values will replace them with no conflict. # -# At least one category must be supplied while not empty at a time. - { "literals": [ "1", diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt index 9509e2eba..994870e85 100644 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt @@ -1,4 +1,17 @@ - +# +# The data follows the standard JSON format and will be further processed into a row of integers to feed the respective +# criterion formula to be rendered. There are three different categories of values that can be supplied via an object +# with the following fields: "literals", "decimal_exponents" and "marked_values". +# +# Literals are as string declared integers in an array whose notation can be made better readable by use of underscores. +# +# Decimal exponents are plain integers in an array and represent powers of a ten. +# +# Marked values come as objects with two fields: "value" and "label", both expecting string values. The "value" field +# also accepts underscores in the integers supplied. +# +# If the same value has been defined by any of the other categories, marked values will replace them with no conflict. +# { "literals": [ "123_456", From 36649f97cc2ed0a6469ec52ba0cb6f204bb861e2 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:09:45 +0800 Subject: [PATCH 120/250] GH-711: shifting code between two files for better organization --- node/src/accountant/payment_adjuster/mod.rs | 265 ++-------------- .../payment_adjuster/possibility_verifier.rs | 131 -------- .../pre_adjustment_analyzer.rs | 283 ++++++++++++++++++ .../accountant/payment_adjuster/test_utils.rs | 2 + 4 files changed, 313 insertions(+), 368 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/possibility_verifier.rs create mode 100644 node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 044ca1073..a43fdf8fd 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -7,7 +7,7 @@ mod diagnostics; mod inner; mod log_fns; mod miscellaneous; -mod possibility_verifier; +mod pre_adjustment_analyzer; mod test_utils; use crate::accountant::db_access_objects::payable_dao::PayableAccount; @@ -19,16 +19,15 @@ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - accounts_before_and_after_debug, log_adjustment_by_service_fee_is_required, + accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, - log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, TransactionCountsWithin16bits, ProposedAdjustmentResolution}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; -use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; +use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -63,6 +62,7 @@ pub trait PaymentAdjuster { } pub struct PaymentAdjusterReal { + analyzer: PreAdjustmentAnalyzer, inner: Box, logger: Logger, } @@ -75,11 +75,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) -> Result, PaymentAdjusterError> { let number_of_counts = qualified_payables.len(); - match Self::determine_transaction_count_limit_by_transaction_fee( - agent, - number_of_counts, - &self.logger, - ) { + match self + .analyzer + .determine_transaction_count_limit_by_transaction_fee( + agent, + number_of_counts, + &self.logger, + ) { Ok(None) => (), Ok(Some(affordable_transaction_count)) => { return Ok(Some(Adjustment::TransactionFeeInPriority { @@ -90,7 +92,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; let service_fee_balance_minor = agent.service_fee_balance_minor(); - match Self::check_need_of_adjustment_by_service_fee( + match self.analyzer.check_need_of_adjustment_by_service_fee( &self.logger, Either::Left(qualified_payables), service_fee_balance_minor, @@ -139,107 +141,12 @@ impl Default for PaymentAdjusterReal { impl PaymentAdjusterReal { pub fn new() -> Self { Self { + analyzer: PreAdjustmentAnalyzer::new(), inner: Box::new(PaymentAdjusterInnerNull {}), logger: Logger::new("PaymentAdjuster"), } } - fn determine_transaction_count_limit_by_transaction_fee( - agent: &dyn BlockchainAgent, - number_of_qualified_accounts: usize, - logger: &Logger, - ) -> Result, PaymentAdjusterError> { - fn max_possible_tx_count( - cw_transaction_fee_balance_minor: U256, - tx_fee_requirement_per_tx_minor: u128, - ) -> u128 { - let max_possible_tx_count_u256 = - cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor); - u128::try_from(max_possible_tx_count_u256).unwrap_or_else(|e| { - panic!( - "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ - transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ - respectively: \"{}\"", - cw_transaction_fee_balance_minor, - tx_fee_requirement_per_tx_minor, - max_possible_tx_count_u256, - e - ) - }) - } - - let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); - let per_transaction_requirement_minor = - agent.estimated_transaction_fee_per_transaction_minor(); - - let max_possible_tx_count = max_possible_tx_count( - cw_transaction_fee_balance_minor, - per_transaction_requirement_minor, - ); - - let detected_tx_counts = - TransactionCountsWithin16bits::new(max_possible_tx_count, number_of_qualified_accounts); - - let max_tx_count_we_can_afford_u16 = detected_tx_counts.affordable; - let required_tx_count_u16 = detected_tx_counts.required; - - if max_tx_count_we_can_afford_u16 == 0 { - Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: number_of_qualified_accounts, - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor, - }, - ) - } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { - Ok(None) - } else { - log_insufficient_transaction_fee_balance( - logger, - required_tx_count_u16, - cw_transaction_fee_balance_minor, - max_tx_count_we_can_afford_u16, - ); - Ok(Some(max_tx_count_we_can_afford_u16)) - } - } - - fn check_need_of_adjustment_by_service_fee( - logger: &Logger, - payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, - cw_service_fee_balance_minor: u128, - ) -> Result { - let qualified_payables: Vec<&PayableAccount> = match payables { - Either::Left(accounts) => accounts.iter().collect(), - Either::Right(weights_and_accounts) => weights_and_accounts - .iter() - .map(|(_, account)| account) - .collect(), - }; - - let required_service_fee_sum: u128 = - sum_as(&qualified_payables, |account: &&PayableAccount| { - account.balance_wei - }); - - if cw_service_fee_balance_minor >= required_service_fee_sum { - Ok(false) - } else { - TransactionFeeAdjustmentPossibilityVerifier {} - .verify_lowest_detectable_adjustment_possibility( - &qualified_payables, - cw_service_fee_balance_minor, - )?; - - log_adjustment_by_service_fee_is_required( - logger, - required_service_fee_sum, - cw_service_fee_balance_minor, - ); - Ok(true) - } - } - fn initialize_inner( &mut self, cw_service_fee_balance: u128, @@ -327,17 +234,18 @@ impl PaymentAdjusterReal { ); let unallocated_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let is_service_fee_adjustment_needed = match Self::check_need_of_adjustment_by_service_fee( - &self.logger, - Either::Right(&accounts_with_criteria_affordable_by_transaction_fee), - unallocated_balance, - ) { - Ok(answer) => answer, - Err(e) => { - log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); - return Err(e); - } - }; + let is_service_fee_adjustment_needed = + match self.analyzer.check_need_of_adjustment_by_service_fee( + &self.logger, + Either::Right(&accounts_with_criteria_affordable_by_transaction_fee), + unallocated_balance, + ) { + Ok(answer) => answer, + Err(e) => { + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); + return Err(e); + } + }; match is_service_fee_adjustment_needed { true => { @@ -440,7 +348,7 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = Self::calculate_criterion_by_calculator( + let new_criterion = Self::have_calculator_calculate_its_criterion( &**criterion_calculator, &account, ); @@ -463,7 +371,7 @@ impl PaymentAdjusterReal { sort_in_descendant_order_by_weights(weights_and_accounts) } - fn calculate_criterion_by_calculator( + fn have_calculator_calculate_its_criterion( criterion_calculator: &dyn CriterionCalculator, account: &PayableAccount, ) -> u128 { @@ -735,7 +643,6 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; - use std::panic::{AssertUnwindSafe, catch_unwind}; use thousands::Separable; use web3::types::U256; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -979,38 +886,6 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } - #[test] - fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() - { - let deadly_ratio = U256::from(u128::MAX) + 1; - let estimated_transaction_fee_per_one_tx_minor = 123_456_789; - let critical_wallet_balance = - deadly_ratio * U256::from(estimated_transaction_fee_per_one_tx_minor); - let blockchain_agent = BlockchainAgentMock::default() - .estimated_transaction_fee_per_transaction_minor_result( - estimated_transaction_fee_per_one_tx_minor, - ) - .transaction_fee_balance_minor_result(critical_wallet_balance); - - let panic_err = catch_unwind(AssertUnwindSafe(|| { - PaymentAdjusterReal::determine_transaction_count_limit_by_transaction_fee( - &blockchain_agent, - 123, - &Logger::new("test"), - ) - })) - .unwrap_err(); - - let err_msg = panic_err.downcast_ref::().unwrap(); - let expected_panic = format!( - "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ - transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ - respectively: \"integer overflow when casting to u128\"", - critical_wallet_balance, estimated_transaction_fee_per_one_tx_minor, deadly_ratio - ); - assert_eq!(err_msg, &expected_panic) - } - #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { let now = SystemTime::now(); @@ -1868,90 +1743,6 @@ mod tests { critical scarcity of MASQ balance. Operation will abort.")); } - #[test] - fn entry_check_advises_trying_adjustment_despite_lots_of_potentially_disqualified_accounts() { - // This test tries to prove that the entry check can reliably predict whether it makes any - // sense to set about the adjustment - let now = SystemTime::now(); - // Disqualified in the first iteration - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 10_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - // Disqualified in the second iteration - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 550_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(15000)).unwrap(), - pending_payable_opt: None, - }; - // Eventually picked fulfilling the keep condition and returned - let wallet_3 = make_wallet("ghi"); - let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let balance_3 = 100_000_000_000; - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: balance_3, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }; - // Disqualified in the third iteration (because it is younger debt than the one at wallet 3) - let account_4 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 100_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(20000)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = vec![account_1, account_2, account_3.clone(), account_4]; - let mut subject = PaymentAdjusterReal::new(); - // This cw balance should make the entry check pass. - // In the adjustment procedure, after three accounts are gradually eliminated, - // resolution of the third account will work out well because of the additional unit. - // - // Both the entry check and the adjustment algorithm strategies advance reversely. - // The initial check inspects the smallest account. - // The strategy of account disqualifications always cuts from the largest accounts piece - // by piece, one in every iteration. - // Consequently, we can evaluate early if the adjustment has chances to succeed and stop - // just at the entry check if it doesn't. - let service_fee_balance_in_minor_units = (balance_3 / 2) + 1; - let agent_id_stamp = ArbitraryIdStamp::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); - Box::new(mock) - }; - let adjustment_setup = PreparedAdjustment { - qualified_payables: qualified_payables.clone(), - agent, - adjustment: Adjustment::ByServiceFee, - response_skeleton_opt: None, - }; - - let check_result = PaymentAdjusterReal::check_need_of_adjustment_by_service_fee( - &Logger::new("test"), - Either::Left(&qualified_payables), - service_fee_balance_in_minor_units, - ); - let adjustment_result = subject.adjust_payments(adjustment_setup, now).unwrap(); - - assert_eq!(check_result, Ok(true)); - assert_eq!( - adjustment_result.affordable_accounts, - vec![PayableAccount { - wallet: wallet_3, - balance_wei: service_fee_balance_in_minor_units, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }] - ); - assert_eq!(adjustment_result.response_skeleton_opt, None); - assert_eq!(adjustment_result.agent.arbitrary_id_stamp(), agent_id_stamp); - } - struct TestConfigForServiceFeeBalances { // Either gwei or wei balances_of_accounts: Either, Vec>, diff --git a/node/src/accountant/payment_adjuster/possibility_verifier.rs b/node/src/accountant/payment_adjuster/possibility_verifier.rs deleted file mode 100644 index d47f77525..000000000 --- a/node/src/accountant/payment_adjuster/possibility_verifier.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - calculate_disqualification_edge, sum_as, -}; -use crate::accountant::payment_adjuster::PaymentAdjusterError; -use itertools::Itertools; - -pub struct TransactionFeeAdjustmentPossibilityVerifier {} - -impl TransactionFeeAdjustmentPossibilityVerifier { - // We cannot do much in this area, only step in if the balance can be zero or nearly zero by - // assumption we make about the smallest debt in the set and the disqualification limit applied - // on it. If so, we don't want to bother payment adjuster and so we will abort instead. - pub fn verify_lowest_detectable_adjustment_possibility( - &self, - accounts: &[&PayableAccount], - cw_service_fee_balance_minor: u128, - ) -> Result<(), PaymentAdjusterError> { - let sorted = accounts - .iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_a.balance_wei, &account_b.balance_wei) - }) - .collect::>(); - let smallest_account = sorted.first().expect("should be one at minimum"); - - if calculate_disqualification_edge(smallest_account.balance_wei) - <= cw_service_fee_balance_minor - { - Ok(()) - } else { - let total_amount_demanded_minor = sum_as(accounts, |account| account.balance_wei); - Err( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: accounts.len(), - total_amount_demanded_minor, - cw_service_fee_balance_minor, - }, - ) - } - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::possibility_verifier::TransactionFeeAdjustmentPossibilityVerifier; - use crate::accountant::payment_adjuster::PaymentAdjusterError; - use crate::accountant::test_utils::make_payable_account; - - fn test_body_for_adjustment_possibility_nearly_rejected( - original_accounts: Vec, - cw_service_fee_balance: u128, - ) { - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - let subject = TransactionFeeAdjustmentPossibilityVerifier {}; - - let result = subject.verify_lowest_detectable_adjustment_possibility( - &accounts_in_expected_format, - cw_service_fee_balance, - ); - - assert_eq!(result, Ok(())) - } - - #[test] - fn adjustment_possibility_nearly_rejected_when_cw_balance_one_more() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; - let original_accounts = vec![account_1, account_2]; - - test_body_for_adjustment_possibility_nearly_rejected( - original_accounts, - cw_service_fee_balance, - ) - } - - #[test] - fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei); - let original_accounts = vec![account_1, account_2]; - - test_body_for_adjustment_possibility_nearly_rejected( - original_accounts, - cw_service_fee_balance, - ) - } - - #[test] - fn adjustment_possibility_err_from_insufficient_balance_for_at_least_single_account_adjustment() - { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(222); - account_2.balance_wei = 2_000_000_002; - let mut account_3 = make_payable_account(333); - account_3.balance_wei = 1_000_000_002; - let cw_service_fee_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; - let original_accounts = vec![account_1, account_2, account_3]; - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - let subject = TransactionFeeAdjustmentPossibilityVerifier {}; - - let result = subject.verify_lowest_detectable_adjustment_possibility( - &accounts_in_expected_format, - cw_service_fee_balance, - ); - - assert_eq!( - result, - Err( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: 3, - total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, - cw_service_fee_balance_minor: cw_service_fee_balance - } - ) - ) - } -} diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs new file mode 100644 index 000000000..f7fa15f24 --- /dev/null +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -0,0 +1,283 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::log_fns::{ + log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, +}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionCountsWithin16bits; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + calculate_disqualification_edge, sum_as, +}; +use crate::accountant::payment_adjuster::PaymentAdjusterError; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use ethereum_types::U256; +use itertools::{Either, Itertools}; +use masq_lib::logger::Logger; + +pub struct PreAdjustmentAnalyzer {} + +impl PreAdjustmentAnalyzer { + pub fn new() -> Self { + Self {} + } + + pub fn determine_transaction_count_limit_by_transaction_fee( + &self, + agent: &dyn BlockchainAgent, + number_of_qualified_accounts: usize, + logger: &Logger, + ) -> Result, PaymentAdjusterError> { + let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); + let per_transaction_requirement_minor = + agent.estimated_transaction_fee_per_transaction_minor(); + + let max_possible_tx_count = Self::max_possible_tx_count( + cw_transaction_fee_balance_minor, + per_transaction_requirement_minor, + ); + + let detected_tx_counts = + TransactionCountsWithin16bits::new(max_possible_tx_count, number_of_qualified_accounts); + + let max_tx_count_we_can_afford_u16 = detected_tx_counts.affordable; + let required_tx_count_u16 = detected_tx_counts.required; + + if max_tx_count_we_can_afford_u16 == 0 { + Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: number_of_qualified_accounts, + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, + }, + ) + } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { + Ok(None) + } else { + log_insufficient_transaction_fee_balance( + logger, + required_tx_count_u16, + cw_transaction_fee_balance_minor, + max_tx_count_we_can_afford_u16, + ); + Ok(Some(max_tx_count_we_can_afford_u16)) + } + } + + fn max_possible_tx_count( + cw_transaction_fee_balance_minor: U256, + tx_fee_requirement_per_tx_minor: u128, + ) -> u128 { + let max_possible_tx_count_u256 = + cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor); + u128::try_from(max_possible_tx_count_u256).unwrap_or_else(|e| { + panic!( + "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ + transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ + respectively: \"{}\"", + cw_transaction_fee_balance_minor, + tx_fee_requirement_per_tx_minor, + max_possible_tx_count_u256, + e + ) + }) + } + + pub fn check_need_of_adjustment_by_service_fee( + &self, + logger: &Logger, + payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, + cw_service_fee_balance_minor: u128, + ) -> Result { + let qualified_payables: Vec<&PayableAccount> = match payables { + Either::Left(accounts) => accounts.iter().collect(), + Either::Right(weights_and_accounts) => weights_and_accounts + .iter() + .map(|(_, account)| account) + .collect(), + }; + + let required_service_fee_sum: u128 = + sum_as(&qualified_payables, |account: &&PayableAccount| { + account.balance_wei + }); + + if cw_service_fee_balance_minor >= required_service_fee_sum { + Ok(false) + } else { + TransactionFeeAdjustmentPossibilityVerifier {} + .verify_lowest_detectable_adjustment_possibility( + &qualified_payables, + cw_service_fee_balance_minor, + )?; + + log_adjustment_by_service_fee_is_required( + logger, + required_service_fee_sum, + cw_service_fee_balance_minor, + ); + Ok(true) + } + } +} + +pub struct TransactionFeeAdjustmentPossibilityVerifier {} + +impl TransactionFeeAdjustmentPossibilityVerifier { + // We cannot do much in this area, only step in if the balance can be zero or nearly zero by + // assumption we make about the smallest debt in the set and the disqualification limit applied + // on it. If so, we don't want to bother payment adjuster and so we will abort instead. + pub fn verify_lowest_detectable_adjustment_possibility( + &self, + accounts: &[&PayableAccount], + cw_service_fee_balance_minor: u128, + ) -> Result<(), PaymentAdjusterError> { + let sorted = accounts + .iter() + .sorted_by(|account_a, account_b| { + Ord::cmp(&account_a.balance_wei, &account_b.balance_wei) + }) + .collect::>(); + let smallest_account = sorted.first().expect("should be one at minimum"); + + if calculate_disqualification_edge(smallest_account.balance_wei) + <= cw_service_fee_balance_minor + { + Ok(()) + } else { + let total_amount_demanded_minor = sum_as(accounts, |account| account.balance_wei); + Err( + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts: accounts.len(), + total_amount_demanded_minor, + cw_service_fee_balance_minor, + }, + ) + } + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; + use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{ + PreAdjustmentAnalyzer, TransactionFeeAdjustmentPossibilityVerifier, + }; + use crate::accountant::payment_adjuster::PaymentAdjusterError; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::test_utils::make_payable_account; + use ethereum_types::U256; + use masq_lib::logger::Logger; + use std::panic::{catch_unwind, AssertUnwindSafe}; + + #[test] + fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() + { + let deadly_ratio = U256::from(u128::MAX) + 1; + let estimated_transaction_fee_per_one_tx_minor = 123_456_789; + let critical_wallet_balance = + deadly_ratio * U256::from(estimated_transaction_fee_per_one_tx_minor); + let blockchain_agent = BlockchainAgentMock::default() + .estimated_transaction_fee_per_transaction_minor_result( + estimated_transaction_fee_per_one_tx_minor, + ) + .transaction_fee_balance_minor_result(critical_wallet_balance); + let subject = PreAdjustmentAnalyzer::new(); + + let panic_err = catch_unwind(AssertUnwindSafe(|| { + subject.determine_transaction_count_limit_by_transaction_fee( + &blockchain_agent, + 123, + &Logger::new("test"), + ) + })) + .unwrap_err(); + + let err_msg = panic_err.downcast_ref::().unwrap(); + let expected_panic = format!( + "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ + transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ + respectively: \"integer overflow when casting to u128\"", + critical_wallet_balance, estimated_transaction_fee_per_one_tx_minor, deadly_ratio + ); + assert_eq!(err_msg, &expected_panic) + } + + fn test_body_for_adjustment_possibility_nearly_rejected( + original_accounts: Vec, + cw_service_fee_balance: u128, + ) { + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + let subject = TransactionFeeAdjustmentPossibilityVerifier {}; + + let result = subject.verify_lowest_detectable_adjustment_possibility( + &accounts_in_expected_format, + cw_service_fee_balance, + ); + + assert_eq!(result, Ok(())) + } + + #[test] + fn adjustment_possibility_nearly_rejected_when_cw_balance_one_more() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_possibility_nearly_rejected( + original_accounts, + cw_service_fee_balance, + ) + } + + #[test] + fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(333); + account_2.balance_wei = 1_000_000_000; + let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei); + let original_accounts = vec![account_1, account_2]; + + test_body_for_adjustment_possibility_nearly_rejected( + original_accounts, + cw_service_fee_balance, + ) + } + + #[test] + fn adjustment_possibility_err_from_insufficient_balance_for_at_least_single_account_adjustment() + { + let mut account_1 = make_payable_account(111); + account_1.balance_wei = 2_000_000_000; + let mut account_2 = make_payable_account(222); + account_2.balance_wei = 2_000_000_002; + let mut account_3 = make_payable_account(333); + account_3.balance_wei = 1_000_000_002; + let cw_service_fee_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; + let original_accounts = vec![account_1, account_2, account_3]; + let accounts_in_expected_format = + original_accounts.iter().collect::>(); + let subject = TransactionFeeAdjustmentPossibilityVerifier {}; + + let result = subject.verify_lowest_detectable_adjustment_possibility( + &accounts_in_expected_format, + cw_service_fee_balance, + ); + + assert_eq!( + result, + Err( + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + number_of_accounts: 3, + total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, + cw_service_fee_balance_minor: cw_service_fee_balance + } + ) + ) + } +} diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 6a7dd4fe1..3534265cd 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -4,6 +4,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; +use crate::accountant::payment_adjuster::pre_adjustment_analyzer::PreAdjustmentAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::test_utils::make_wallet; use itertools::Either; @@ -26,6 +27,7 @@ pub fn make_initialized_subject( let cw_masq_balance_minor = cw_masq_balance_minor_opt.unwrap_or(0); let logger = logger_opt.unwrap_or(Logger::new("test")); PaymentAdjusterReal { + analyzer: PreAdjustmentAnalyzer::new(), inner: Box::new(PaymentAdjusterInnerReal::new( now, None, From 401a1c2d4ab4e6b4c1c5bd31d6100ed83b0110ab Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:02:11 +0700 Subject: [PATCH 121/250] GH-711: interim commit --- node/src/accountant/mod.rs | 2 +- .../miscellaneous/data_structures.rs | 11 +++ node/src/accountant/payment_adjuster/mod.rs | 91 +++++++++++-------- .../accountant/payment_adjuster/test_utils.rs | 2 +- 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 4a68b742c..e44db4965 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1674,7 +1674,7 @@ mod tests { "payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) - .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsUnexpectedlyEliminated)); + .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); test_handling_payment_adjuster_error(test_name, payment_adjuster); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index dc8c62b29..905ecd141 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -11,6 +11,17 @@ pub enum AdjustmentIterationResult { }, } +pub struct GraduallyFormedResult{ + pub here_decided_accounts: Vec, + pub downstream_decided_accounts: Vec +} + +impl GraduallyFormedResult{ + pub fn new(here_decided_accounts: Vec, downstream_decided_accounts: Vec)->Self{ + Self{ here_decided_accounts, downstream_decided_accounts } + } +} + #[derive(Debug)] pub enum AfterAdjustmentSpecialTreatment { TreatInsignificantAccount, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index a43fdf8fd..05f551ad8 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -25,7 +25,7 @@ use crate::accountant::payment_adjuster::log_fns::{ use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, ProposedAdjustmentResolution}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, GraduallyFormedResult, ProposedAdjustmentResolution}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; @@ -267,11 +267,11 @@ impl PaymentAdjusterReal { fn propose_possible_adjustment_recursively( &mut self, weights_and_accounts: Vec<(u128, PayableAccount)>, - ) -> Vec { + ) -> Result, PaymentAdjusterError> { let adjustment_iteration_result = self.perform_adjustment_by_service_fee(weights_and_accounts); - let (here_decided_accounts, downstream_decided_accounts) = + let gradual_result = self.resolve_iteration_result(adjustment_iteration_result); let here_decided_iter = here_decided_accounts.into_iter(); @@ -281,19 +281,16 @@ impl PaymentAdjusterReal { diagnostics!("\nFINAL ADJUSTED ACCOUNTS:", &merged); - merged + Ok(merged) } fn resolve_iteration_result( &mut self, adjustment_iteration_result: AdjustmentIterationResult, - ) -> ( - Vec, - Vec, - ) { + ) -> Result { match adjustment_iteration_result { AdjustmentIterationResult::AllAccountsProcessedSmoothly(decided_accounts) => { - (decided_accounts, vec![]) + Ok(GraduallyFormedResult::new(decided_accounts, vec![])) } AdjustmentIterationResult::SpecialTreatmentNeeded { case: special_case, @@ -304,7 +301,8 @@ impl PaymentAdjusterReal { if remaining.is_empty() { debug!(self.logger, "Last remaining account ended up disqualified"); - return (vec![], vec![]); + todo!(); + return Err(PaymentAdjusterError::AllAccountsEliminated); } vec![] @@ -315,13 +313,14 @@ impl PaymentAdjusterReal { } }; + // Note: there always is at least one remaining account besides an outweighed one let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( remaining, ServiceFeeOnlyAdjustmentRunner {}, ); - (here_decided_accounts, down_stream_decided_accounts) + Ok(GraduallyFormedResult::new(here_decided_accounts, down_stream_decided_accounts)) } } } @@ -579,7 +578,7 @@ pub enum PaymentAdjusterError { total_amount_demanded_minor: u128, cw_service_fee_balance_minor: u128, }, - AllAccountsUnexpectedlyEliminated, + AllAccountsEliminated, } impl Display for PaymentAdjusterError { @@ -612,10 +611,9 @@ impl Display for PaymentAdjusterError { total_amount_demanded_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() ), - PaymentAdjusterError::AllAccountsUnexpectedlyEliminated => write!( + PaymentAdjusterError::AllAccountsEliminated => write!( f, - "While chances were according to the preliminary analysis, the adjustment \ - algorithm rejected each payable" + "The adjustment algorithm had to eliminate each payable given the resources." ), } } @@ -877,9 +875,8 @@ mod tests { 70,000,000,000,000 wei. Consuming wallet balance: 90,000 wei", ), ( - PaymentAdjusterError::AllAccountsUnexpectedlyEliminated, - "While chances were according to the preliminary analysis, the adjustment \ - algorithm rejected each payable", + PaymentAdjusterError::AllAccountsEliminated, + "The adjustment algorithm had to eliminate each payable given the resources.", ), ] .into_iter() @@ -1058,26 +1055,48 @@ mod tests { } #[test] - fn there_is_door_leading_out_if_we_accidentally_wind_up_with_last_account_also_disqualified() { - // Not really an expected behavior but cannot be ruled out with an absolute confidence + fn adjustment_started_but_all_accounts_were_eliminated_anyway() { + + XXXX write me correctly! + init_test_logging(); - let test_name = "there_is_door_leading_out_if_we_accidentally_wind_up_with_last_account_also_disqualified"; - let mut subject = - make_initialized_subject(SystemTime::now(), Some(123), Some(Logger::new(test_name))); - let adjustment_iteration_result = AdjustmentIterationResult::SpecialTreatmentNeeded { - case: TreatInsignificantAccount, - remaining: vec![], + let test_name = "adjustment_started_but_all_accounts_were_eliminated_anyway"; + let now = SystemTime::now(); + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 1, + last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), + pending_payable_opt: None, + }; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: 6_000_000_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1.clone(), account_2.clone()]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + let agent_id_stamp = ArbitraryIdStamp::new(); + let accounts_sum: u128 = + 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; + let service_fee_balance_in_minor_units = accounts_sum - 2_000_000_000_000_000_000; + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::ByServiceFee, + response_skeleton_opt: None, }; - let (here_decided_accounts, downstream_decided_accounts) = - subject.resolve_iteration_result(adjustment_iteration_result); + let result = subject.adjust_payments(adjustment_setup, now); - assert!(here_decided_accounts.is_empty()); - assert!(downstream_decided_accounts.is_empty()); - // Even though we normally don't assert on DEBUG logs, this one hardens the test - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {test_name}: Last remaining account ended up disqualified" - )); + assert_eq!(result, Err(PaymentAdjusterError::AllAccountsEliminated)) } #[test] @@ -1145,10 +1164,10 @@ mod tests { } #[test] - fn initial_accounts_count_evens_the_payments_count() { + fn qualified_accounts_count_evens_the_payments_count() { // Meaning adjustment by service fee but no account elimination init_test_logging(); - let test_name = "initial_accounts_count_evens_the_payments_count"; + let test_name = "qualified_accounts_count_evens_the_payments_count"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 3534265cd..aa50b1649 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -42,7 +42,7 @@ pub fn make_extreme_accounts( now: SystemTime, ) -> Vec { let accounts_seeds: Vec<(usize, u128)> = match months_of_debt_and_balance_minor { - Either::Left((vec, constant_balance)) => vec + Either::Left((vec_of_months, constant_balance)) => vec_of_months .into_iter() .map(|months| (months, constant_balance)) .collect(), From 8320e50fd1f063118c53f5ccba27b63113b93323 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:28:22 +0700 Subject: [PATCH 122/250] GH-711: continuing in fixing issues I found myself - not yet so close to the final change needed of evaluation of the disqualified accounts --- .../payment_adjuster/adjustment_runners.rs | 15 +++-- .../miscellaneous/data_structures.rs | 62 +++++++++++++++++-- node/src/accountant/payment_adjuster/mod.rs | 55 ++++++++-------- 3 files changed, 92 insertions(+), 40 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 5b7a8a20d..03a852464 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -72,8 +72,9 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { }; Ok(Either::Left( - payment_adjuster - .propose_possible_adjustment_recursively(weights_and_accounts_in_descending_order), + payment_adjuster.propose_possible_adjustment_recursively( + weights_and_accounts_in_descending_order, + )?, )) } } @@ -81,7 +82,7 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { pub struct ServiceFeeOnlyAdjustmentRunner {} impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { - type ReturnType = Vec; + type ReturnType = Result, PaymentAdjusterError>; fn adjust_last_one( &self, @@ -89,7 +90,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { last_account: PayableAccount, ) -> Self::ReturnType { let element_opt = adjust_last_one(payment_adjuster, last_account); - empty_or_single_element_vector(element_opt) + Ok(empty_or_single_element_vector(element_opt)) } fn adjust_multiple( @@ -206,7 +207,7 @@ mod tests { #[test] fn service_fee_only_adjust_last_one_works() { test_adjust_last_one(ServiceFeeOnlyAdjustmentRunner {}, |expected_vec| { - expected_vec + Ok(expected_vec) }) } @@ -290,7 +291,9 @@ mod tests { let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(accounts); - let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); + let result = subject + .adjust_multiple(&mut payment_adjuster, criteria_and_accounts) + .unwrap(); let returned_accounts = result .into_iter() diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 905ecd141..76b097921 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -11,14 +11,27 @@ pub enum AdjustmentIterationResult { }, } -pub struct GraduallyFormedResult{ +pub struct RecursionResults { pub here_decided_accounts: Vec, - pub downstream_decided_accounts: Vec + pub downstream_decided_accounts: Vec, } -impl GraduallyFormedResult{ - pub fn new(here_decided_accounts: Vec, downstream_decided_accounts: Vec)->Self{ - Self{ here_decided_accounts, downstream_decided_accounts } +impl RecursionResults { + pub fn new( + here_decided_accounts: Vec, + downstream_decided_accounts: Vec, + ) -> Self { + Self { + here_decided_accounts, + downstream_decided_accounts, + } + } + + pub fn merge_results_from_recursion(self) -> Vec { + self.here_decided_accounts + .into_iter() + .chain(self.downstream_decided_accounts.into_iter()) + .collect() } } @@ -75,7 +88,44 @@ impl TransactionCountsWithin16bits { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionCountsWithin16bits; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, + }; + use crate::accountant::test_utils::make_payable_account; + + #[test] + fn merging_results_from_recursion_works() { + let non_finalized_account_1 = AdjustedAccountBeforeFinalization { + original_account: make_payable_account(111), + proposed_adjusted_balance: 1234, + }; + let non_finalized_account_2 = AdjustedAccountBeforeFinalization { + original_account: make_payable_account(222), + proposed_adjusted_balance: 5555, + }; + let non_finalized_account_3 = AdjustedAccountBeforeFinalization { + original_account: make_payable_account(333), + proposed_adjusted_balance: 6789, + }; + let subject = RecursionResults { + here_decided_accounts: vec![non_finalized_account_1.clone()], + downstream_decided_accounts: vec![ + non_finalized_account_2.clone(), + non_finalized_account_3.clone(), + ], + }; + + let result = subject.merge_results_from_recursion(); + + assert_eq!( + result, + vec![ + non_finalized_account_1, + non_finalized_account_2, + non_finalized_account_3 + ] + ) + } #[test] fn there_is_u16_ceiling_for_possible_tx_count() { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 05f551ad8..399c5a4bf 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -25,7 +25,7 @@ use crate::accountant::payment_adjuster::log_fns::{ use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, GraduallyFormedResult, ProposedAdjustmentResolution}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, ProposedAdjustmentResolution}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; @@ -253,7 +253,7 @@ impl PaymentAdjusterReal { .propose_possible_adjustment_recursively( accounts_with_criteria_affordable_by_transaction_fee, ); - Ok(Either::Left(adjustment_result_before_verification)) + Ok(Either::Left(adjustment_result_before_verification?)) } false => { let finalized_accounts = isolate_accounts_from_weights( @@ -268,29 +268,24 @@ impl PaymentAdjusterReal { &mut self, weights_and_accounts: Vec<(u128, PayableAccount)>, ) -> Result, PaymentAdjusterError> { - let adjustment_iteration_result = - self.perform_adjustment_by_service_fee(weights_and_accounts); + let current_iteration_result = self.perform_adjustment_by_service_fee(weights_and_accounts); - let gradual_result = - self.resolve_iteration_result(adjustment_iteration_result); + let recursion_results = self.resolve_current_iteration_result(current_iteration_result)?; - let here_decided_iter = here_decided_accounts.into_iter(); - let downstream_decided_iter = downstream_decided_accounts.into_iter(); - let merged: Vec = - here_decided_iter.chain(downstream_decided_iter).collect(); + let merged = recursion_results.merge_results_from_recursion(); diagnostics!("\nFINAL ADJUSTED ACCOUNTS:", &merged); Ok(merged) } - fn resolve_iteration_result( + fn resolve_current_iteration_result( &mut self, adjustment_iteration_result: AdjustmentIterationResult, - ) -> Result { + ) -> Result { match adjustment_iteration_result { AdjustmentIterationResult::AllAccountsProcessedSmoothly(decided_accounts) => { - Ok(GraduallyFormedResult::new(decided_accounts, vec![])) + Ok(RecursionResults::new(decided_accounts, vec![])) } AdjustmentIterationResult::SpecialTreatmentNeeded { case: special_case, @@ -318,9 +313,12 @@ impl PaymentAdjusterReal { .calculate_criteria_and_propose_adjustments_recursively( remaining, ServiceFeeOnlyAdjustmentRunner {}, - ); + )?; - Ok(GraduallyFormedResult::new(here_decided_accounts, down_stream_decided_accounts)) + Ok(RecursionResults::new( + here_decided_accounts, + down_stream_decided_accounts, + )) } } } @@ -625,7 +623,7 @@ mod tests { use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, weights_total}; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, }; @@ -1031,7 +1029,7 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }; - let accounts = vec![account_1.clone(), account_2.clone()]; + let accounts = vec![account_1.clone(), account_2]; let subject = make_initialized_subject( now, Some(consuming_wallet_balance), @@ -1056,31 +1054,28 @@ mod tests { #[test] fn adjustment_started_but_all_accounts_were_eliminated_anyway() { - - XXXX write me correctly! - - init_test_logging(); let test_name = "adjustment_started_but_all_accounts_were_eliminated_anyway"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 1, + balance_wei: 3_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 6_000_000_000_000_000_000, + balance_wei: 1_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1.clone(), account_2.clone()]; + let qualified_payables = vec![account_1, account_2]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum: u128 = - 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; - let service_fee_balance_in_minor_units = accounts_sum - 2_000_000_000_000_000_000; + let service_fee_balance_in_minor_units = ((3_000_000_000_000 + * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) + - 1; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1096,7 +1091,11 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now); - assert_eq!(result, Err(PaymentAdjusterError::AllAccountsEliminated)) + let err = match result { + Err(e) => e, + Ok(_) => panic!("we expected to get an error but it was ok"), + }; + assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated) } #[test] From 36d285d80417ae976b9e77f0bee23e5dfcf216fa Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:48:38 +0700 Subject: [PATCH 123/250] GH-711: abandoned some of the ideas so now I can finally get down to the changing the eliminanion direction in disqualifications --- .../miscellaneous/helper_functions.rs | 30 ++++++++++--------- node/src/accountant/payment_adjuster/mod.rs | 6 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 00a1c384e..296f51f23 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -20,7 +20,7 @@ use std::iter::successors; use std::ops::Not; use web3::types::U256; -const MAX_EXPONENT_FOR_10_IN_U128: u32 = 38; +const MAX_EXPONENT_FOR_10_WITHIN_U128: u32 = 76; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; // Represents 50% pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = @@ -57,20 +57,20 @@ pub fn drop_accounts_that_cannot_be_afforded_due_to_service_fee( .collect() } -// TODO U256 possible + test for extreme input fed?? pub fn compute_mul_coefficient_preventing_fractional_numbers( cw_masq_balance_minor: u128, - account_weight: u128, + account_weights_total: u128, ) -> U256 { - let weight_digits_count = log_10(account_weight); + let weight_digits_count = log_10(account_weights_total); let cw_balance_digits_count = log_10(cw_masq_balance_minor); - let positive_difference = weight_digits_count.saturating_sub(cw_balance_digits_count); - let safe_mul_coefficient = positive_difference + EMPIRIC_PRECISION_COEFFICIENT; + let positive_only_difference = weight_digits_count.saturating_sub(cw_balance_digits_count); + let exponent = positive_only_difference + EMPIRIC_PRECISION_COEFFICIENT; U256::from(10) - .checked_pow(safe_mul_coefficient.into()) - // .unwrap_or_else(|| 10_u128.pow(MAX_EXPONENT_FOR_10_IN_U128)) - // TODO fix me in the test - .unwrap_or_else(|| U256::MAX) + .checked_pow(exponent.into()) + .expect("impossible to reach given weights total data type being u128") + // Note that reaching this limitation is highly unlikely, and even in the future, if we boosted the data type + // for account_weights_total up to U256, assuming such low inputs we would be feeding it now with real world + // scenario parameters } pub fn possibly_outweighed_accounts_fold_guts( @@ -386,7 +386,7 @@ mod tests { possibly_outweighed_accounts_fold_guts, try_finding_an_account_to_disqualify_in_this_iteration, weights_total, CwExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, - MAX_EXPONENT_FOR_10_IN_U128, + MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -401,7 +401,7 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(MAX_EXPONENT_FOR_10_IN_U128, 38); + assert_eq!(MAX_EXPONENT_FOR_10_WITHIN_U128, 76); assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); assert_eq!( ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, @@ -472,6 +472,8 @@ mod tests { 1_000_000_000_u128, 1_000_000_000_000_000, 1_000_000_000_000, + // The following values are the minimum. It turned out that it helps to reach better precision in + // the downstream computations 100_000_000, 100_000_000, 100_000_000, @@ -483,7 +485,7 @@ mod tests { } #[test] - fn multiplication_coefficient_extreme_feeding_and_safety_ceiling() { + fn multiplication_coefficient_extreme_feeding_with_possible_but_only_little_realistic_values() { // We cannot say by heart which of the evaluated weights from // these parameters below will be bigger than another and therefore // we cannot line them up in an order @@ -498,7 +500,7 @@ mod tests { ]; let (accounts_with_their_weights, reserved_initial_accounts_order_according_to_wallets) = get_extreme_weights_and_initial_accounts_order(accounts_as_months_and_balances); - let cw_balance_in_minor = 1; + let cw_balance_in_minor = 1; // Minimal possible balance 1 wei let results = accounts_with_their_weights .into_iter() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 399c5a4bf..fa03792cf 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1015,8 +1015,7 @@ mod tests { wallet: make_wallet("blah"), balance_wei: 1_000_000_000_000, last_paid_timestamp: now - // Greater age like this together with smaller balance usually causes the account - // to come outweighed + // Greater age like this together with smaller balance usually causes the account to outweigh .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) .unwrap(), pending_payable_opt: None, @@ -1047,8 +1046,7 @@ mod tests { } => remaining, x => panic!("we expected to see a disqualified account but got: {:?}", x), }; - // We eliminated (disqualified) the other account than which was going to qualify as - // outweighed + // We eliminated (disqualified) the other account than which was going to qualify as outweighed assert_eq!(remaining, vec![account_1]); } From bb9b631d2b6f66bd2efbdef46a30883cecb17092 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:06:12 +0700 Subject: [PATCH 124/250] GH-711: continuing in transition; the mechanism for outweighed is sanitized; will have just a smaller part left with the one for disqualified --- masq_lib/src/utils.rs | 13 +- .../payment_adjuster/diagnostics.rs | 7 +- .../miscellaneous/data_structures.rs | 47 ++- .../miscellaneous/helper_functions.rs | 281 +++++++++--------- node/src/accountant/payment_adjuster/mod.rs | 80 ++--- 5 files changed, 239 insertions(+), 189 deletions(-) diff --git a/masq_lib/src/utils.rs b/masq_lib/src/utils.rs index 0f05e9e4f..6a36a95da 100644 --- a/masq_lib/src/utils.rs +++ b/masq_lib/src/utils.rs @@ -364,6 +364,13 @@ pub fn type_name_of(_examined: T) -> &'static str { std::any::type_name::() } +pub fn convert_collection(inputs: Vec) -> Vec +where + Product: From, +{ + inputs.into_iter().map(Product::from).collect() +} + pub trait MutabilityConflictHelper where T: 'static, @@ -376,7 +383,7 @@ where F: FnOnce(&T, &mut Self) -> Self::Result, { //TODO we should seriously think about rewriting this in well tested unsafe code, - // Rust is unnecessarily strict as for this conflicting situation + // Rust is unnecessarily strict in this conflicting situation let helper = self.helper_access().take().expectv("helper"); let result = closure(&helper, self); self.helper_access().replace(helper); @@ -447,8 +454,8 @@ macro_rules! hashmap { () => { ::std::collections::HashMap::new() }; - ($($key:expr => $val:expr,)+) => { - hashmap!($($key => $val),+) + ($($key:expr => $value:expr,)+) => { + hashmap!($($key => $value),+) }; ($($key:expr => $value:expr),+) => { { diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 7107b2564..08f71d0ac 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -198,6 +198,7 @@ pub mod formulas_progressive_characteristics { use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use crate::test_utils::unshared_test_utils::standard_dir_for_test_input_data; use itertools::Itertools; + use masq_lib::utils::convert_collection; use serde::de::Error; use serde::{Deserialize as NormalImplDeserialize, Deserializer}; use serde_derive::Deserialize; @@ -452,11 +453,7 @@ pub mod formulas_progressive_characteristics { .map(|wrapper| wrapper.numerical_value) .collect(); - let marked_values = processed_json_input - .marked_values - .into_iter() - .map(|marked_value_from_file| MarkedValue::from(marked_value_from_file)) - .collect(); + let marked_values = convert_collection(processed_json_input.marked_values); DeserializedInputValues { non_marked_values: serialize_values_on_x_axis_from_vecs( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 76b097921..ab375689e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -4,10 +4,10 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; #[derive(Debug)] pub enum AdjustmentIterationResult { - AllAccountsProcessedSmoothly(Vec), - SpecialTreatmentNeeded { - case: AfterAdjustmentSpecialTreatment, - remaining: Vec, + AllAccountsProcessed(Vec), + SpecialTreatmentRequired { + case: RequiredSpecialTreatment, + remaining_undecided_accounts: Vec, }, } @@ -36,7 +36,7 @@ impl RecursionResults { } #[derive(Debug)] -pub enum AfterAdjustmentSpecialTreatment { +pub enum RequiredSpecialTreatment { TreatInsignificantAccount, TreatOutweighedAccounts(Vec), } @@ -56,8 +56,43 @@ impl AdjustedAccountBeforeFinalization { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct UnconfirmedAdjustment { + pub non_finalized_account: AdjustedAccountBeforeFinalization, + pub weight: u128, +} + +impl UnconfirmedAdjustment { + pub fn new(account: PayableAccount, weight: u128, proposed_adjusted_balance: u128) -> Self { + Self { + non_finalized_account: AdjustedAccountBeforeFinalization::new( + account, + proposed_adjusted_balance, + ), + weight, + } + } +} + +pub struct NonFinalizedAdjustmentWithResolution { + pub non_finalized_adjustment: AdjustedAccountBeforeFinalization, + pub adjustment_resolution: AdjustmentResolution, +} + +impl NonFinalizedAdjustmentWithResolution { + pub fn new( + non_finalized_adjustment: AdjustedAccountBeforeFinalization, + adjustment_resolution: AdjustmentResolution, + ) -> Self { + Self { + non_finalized_adjustment, + adjustment_resolution, + } + } +} + #[derive(Clone, Copy)] -pub enum ProposedAdjustmentResolution { +pub enum AdjustmentResolution { Finalize, Revert, } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 296f51f23..8fb68ecb4 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -9,8 +9,8 @@ use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functi }; use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, - ProposedAdjustmentResolution, + AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, + PercentageAccountInsignificance, UnconfirmedAdjustment, }; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -73,27 +73,28 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( // scenario parameters } -pub fn possibly_outweighed_accounts_fold_guts( - (mut outweighed, mut passing_through): ( - Vec, - Vec, - ), - current_account_info: AdjustedAccountBeforeFinalization, -) -> ( - Vec, - Vec, -) { - if current_account_info.proposed_adjusted_balance - > current_account_info.original_account.balance_wei +pub fn resolve_possibly_outweighed_account( + (mut outweighed, mut passing_through): (Vec, Vec), + mut current_account_info: UnconfirmedAdjustment, +) -> (Vec, Vec) { + if current_account_info + .non_finalized_account + .proposed_adjusted_balance + > current_account_info + .non_finalized_account + .original_account + .balance_wei { - possibly_outweighed_accounts_diagnostics(¤t_account_info); + possibly_outweighed_accounts_diagnostics(¤t_account_info.non_finalized_account); - let new_account_info = AdjustedAccountBeforeFinalization { - proposed_adjusted_balance: current_account_info.original_account.balance_wei, - ..current_account_info - }; + current_account_info + .non_finalized_account + .proposed_adjusted_balance = current_account_info + .non_finalized_account + .original_account + .balance_wei; - outweighed.push(new_account_info); + outweighed.push(current_account_info); (outweighed, passing_through) } else { passing_through.push(current_account_info); @@ -101,33 +102,6 @@ pub fn possibly_outweighed_accounts_fold_guts( } } -pub fn find_largest_nominated_account<'a>( - accounts: &'a [&'a AdjustedAccountBeforeFinalization], -) -> &'a AdjustedAccountBeforeFinalization { - let first_account = &accounts.first().expect("collection was empty"); - accounts - .iter() - .fold(**first_account, |largest_so_far, current| { - match Ord::cmp( - ¤t.original_account.balance_wei, - &largest_so_far.original_account.balance_wei, - ) { - Ordering::Less => largest_so_far, - Ordering::Greater => current, - Ordering::Equal => { - // Greater value means younger - if current.original_account.last_paid_timestamp - > largest_so_far.original_account.last_paid_timestamp - { - current - } else { - largest_so_far - } - } - } - }) -} - pub fn exhaust_cw_till_the_last_drop( approved_accounts: Vec, original_cw_service_fee_balance_minor: u128, @@ -145,7 +119,7 @@ pub fn exhaust_cw_till_the_last_drop( ) }); - let init = CwExhaustingStatus::new(cw_reminder); + let init = ConsumingWalletExhaustingStatus::new(cw_reminder); approved_accounts .into_iter() .sorted_by(|info_a, info_b| { @@ -165,9 +139,9 @@ pub fn exhaust_cw_till_the_last_drop( } fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( - status: CwExhaustingStatus, + status: ConsumingWalletExhaustingStatus, non_finalized_account: AdjustedAccountBeforeFinalization, -) -> CwExhaustingStatus { +) -> ConsumingWalletExhaustingStatus { if status.remainder != 0 { let balance_gap_minor = non_finalized_account .original_account @@ -207,7 +181,7 @@ pub fn try_finding_an_account_to_disqualify_in_this_iteration( .is_empty() .not() .then(|| { - let account_to_disqualify = find_largest_nominated_account( + let account_to_disqualify = find_account_with_smallest_weight( &disqualification_suspected_accounts ); @@ -233,22 +207,39 @@ pub fn try_finding_an_account_to_disqualify_in_this_iteration( }) } -pub fn finalize_collection( - non_finalized_accounts: Vec, - resolution: ProposedAdjustmentResolution, -) -> Vec { - non_finalized_accounts - .into_iter() - .map(|account_info| PayableAccount::from((account_info, resolution))) - .collect() +fn find_account_with_smallest_weight<'a>( + accounts: &'a [&'a AdjustedAccountBeforeFinalization], +) -> &'a AdjustedAccountBeforeFinalization { + let first_account = &accounts.first().expect("collection was empty"); + accounts + .iter() + .fold(**first_account, |largest_so_far, current| { + match Ord::cmp( + ¤t.original_account.balance_wei, + &largest_so_far.original_account.balance_wei, + ) { + Ordering::Less => largest_so_far, + Ordering::Greater => current, + Ordering::Equal => { + // Greater value means younger + if current.original_account.last_paid_timestamp + > largest_so_far.original_account.last_paid_timestamp + { + current + } else { + largest_so_far + } + } + } + }) } -struct CwExhaustingStatus { +struct ConsumingWalletExhaustingStatus { remainder: u128, accounts_finalized_so_far: Vec, } -impl CwExhaustingStatus { +impl ConsumingWalletExhaustingStatus { fn new(remainder: u128) -> Self { Self { remainder, @@ -273,9 +264,9 @@ impl CwExhaustingStatus { } fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { - let finalized_account = PayableAccount::from(( + let finalized_account = PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( non_finalized_account_info, - ProposedAdjustmentResolution::Finalize, + AdjustmentResolution::Finalize, )); self.accounts_finalized_so_far.push(finalized_account); self @@ -299,7 +290,7 @@ pub fn isolate_accounts_from_weights( .collect() } -pub fn list_accounts_nominated_for_disqualification( +fn list_accounts_nominated_for_disqualification( non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], ) -> Vec<&AdjustedAccountBeforeFinalization> { non_finalized_adjusted_accounts @@ -354,21 +345,31 @@ pub fn x_or_1(x: u128) -> u128 { } } -type NonFinalizedAccountAndAdjustmentResolution = ( - AdjustedAccountBeforeFinalization, - ProposedAdjustmentResolution, -); +impl From for PayableAccount { + fn from(_: UnconfirmedAdjustment) -> Self { + todo!() + } +} -impl From for PayableAccount { - fn from( - (account_info, proposed_adjustment_resolution): NonFinalizedAccountAndAdjustmentResolution, - ) -> Self { - match proposed_adjustment_resolution { - ProposedAdjustmentResolution::Finalize => PayableAccount { - balance_wei: account_info.proposed_adjusted_balance, - ..account_info.original_account +impl From for AdjustedAccountBeforeFinalization { + fn from(_: UnconfirmedAdjustment) -> Self { + todo!() + } +} + +impl From for PayableAccount { + fn from(resolution_info: NonFinalizedAdjustmentWithResolution) -> Self { + match resolution_info.adjustment_resolution { + // TODO if we do this it means it is an outweighed account and so it has already the balance adjusted + AdjustmentResolution::Finalize => PayableAccount { + balance_wei: resolution_info + .non_finalized_adjustment + .proposed_adjusted_balance, + ..resolution_info.non_finalized_adjustment.original_account }, - ProposedAdjustmentResolution::Revert => account_info.original_account, + AdjustmentResolution::Revert => { + resolution_info.non_finalized_adjustment.original_account + } } } } @@ -377,16 +378,16 @@ impl From for PayableAccount { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, + AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, UnconfirmedAdjustment, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, - exhaust_cw_till_the_last_drop, find_largest_nominated_account, + exhaust_cw_till_the_last_drop, find_account_with_smallest_weight, list_accounts_nominated_for_disqualification, log_10, log_2, - possibly_outweighed_accounts_fold_guts, - try_finding_an_account_to_disqualify_in_this_iteration, weights_total, CwExhaustingStatus, - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, - MAX_EXPONENT_FOR_10_WITHIN_U128, + resolve_possibly_outweighed_account, + try_finding_an_account_to_disqualify_in_this_iteration, weights_total, + ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -396,6 +397,7 @@ mod tests { use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; + use masq_lib::utils::convert_collection; use std::time::{Duration, SystemTime}; use web3::types::U256; @@ -468,7 +470,7 @@ mod tests { }) .collect::>(); - let expected_result = vec![ + let expected_result: Vec = convert_collection(vec![ 1_000_000_000_u128, 1_000_000_000_000_000, 1_000_000_000_000, @@ -477,10 +479,7 @@ mod tests { 100_000_000, 100_000_000, 100_000_000, - ] - .into_iter() - .map(U256::from) - .collect::>(); + ]); assert_eq!(result, expected_result) } @@ -530,7 +529,7 @@ mod tests { .sorted_by(|(idx_a, _, _), (idx_b, _, _)| Ord::cmp(&idx_b, &idx_a)) .map(|(_, coefficient, weight)| (coefficient, weight)) .collect::>(); - let templates_for_coefficients = vec![ + let templates_for_coefficients: Vec = convert_collection(vec![ 100000000000000000000000000000000000000_u128, 100000000000000000000000000000000000, 100000000000000000000000000000000000, @@ -538,10 +537,7 @@ mod tests { 10000000000000000000000000000000, 10000000000000000000000000000000, 100000000000000000000000000000000000, - ] - .into_iter() - .map(U256::from) - .collect::>(); + ]); // I was trying to write these assertions so that it wouldn't require us to rewrite // the expected values everytime someone pokes into the formulas. check_relation_to_computed_weight_fairly_but_with_enough_benevolence( @@ -600,12 +596,13 @@ mod tests { } fn make_non_finalized_adjusted_accounts( - payable_accounts: Vec<(PayableAccount, u128)>, + payable_accounts: Vec, ) -> Vec { + let garbage_proposed_balance = 1_000_000_000; // Unimportant for the usages in the tests this is for payable_accounts .into_iter() - .map(|(original_account, proposed_adjusted_balance)| { - AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance) + .map(|original_account| { + AdjustedAccountBeforeFinalization::new(original_account, garbage_proposed_balance) }) .collect() } @@ -627,20 +624,20 @@ mod tests { } #[test] - fn find_largest_nominated_account_works_for_unequal_balances() { - let account_1 = make_payable_account(111); - let account_2 = make_payable_account(333); - let account_3 = make_payable_account(222); - let account_4 = make_payable_account(332); + fn find_account_with_smallest_weight_works_for_unequal_weights() { + let account_1 = make_payable_account(1000); + let account_2 = make_payable_account(3000); + let account_3 = make_payable_account(2000); + let account_4 = make_payable_account(1000); let accounts = make_non_finalized_adjusted_accounts(vec![ - (account_1, 100_000_000), - (account_2.clone(), 222_000_000), - (account_3, 300_000_000), - (account_4, 400_000_000), + account_1, + account_2.clone(), + account_3, + account_4, ]); let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_nominated_account(&referenced_non_finalized_accounts); + let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); assert_eq!( result, @@ -652,7 +649,7 @@ mod tests { } #[test] - fn find_largest_nominated_account_for_equal_balances_chooses_younger_account() { + fn find_account_with_smallest_weight_for_equal_balances_chooses_younger_account() { // We prefer to keep the older and so more important accounts in the game let now = SystemTime::now(); let account_1 = make_payable_account(111); @@ -662,14 +659,14 @@ mod tests { let mut account_4 = make_payable_account(333); account_4.last_paid_timestamp = now.checked_sub(Duration::from_secs(9999)).unwrap(); let accounts = make_non_finalized_adjusted_accounts(vec![ - (account_1, 100_000_000), - (account_2, 200_000_000), - (account_3, 300_000_000), - (account_4.clone(), 444_000_000), + account_1, + account_2, + account_3, + account_4.clone(), ]); let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_nominated_account(&referenced_non_finalized_accounts); + let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); assert_eq!( result, @@ -681,7 +678,7 @@ mod tests { } #[test] - fn find_largest_nominated_account_for_accounts_with_equal_balances_as_well_as_age() { + fn find_account_with_smallest_weight_for_accounts_with_equal_balances_as_well_as_age() { let account = make_payable_account(111); let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); @@ -689,13 +686,10 @@ mod tests { account_1.wallet = wallet_1.clone(); let mut account_2 = account; account_2.wallet = wallet_2; - let accounts = make_non_finalized_adjusted_accounts(vec![ - (account_1.clone(), 100_111_000), - (account_2, 200_000_000), - ]); + let accounts = make_non_finalized_adjusted_accounts(vec![account_1.clone(), account_2]); let referenced_non_finalized_accounts = by_reference(&accounts); - let result = find_largest_nominated_account(&referenced_non_finalized_accounts); + let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); assert_eq!( result, @@ -708,66 +702,73 @@ mod tests { #[test] fn accounts_with_original_balances_equal_to_the_proposed_ones_are_not_outweighed() { - let account_info = AdjustedAccountBeforeFinalization { - original_account: PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 9_000_000_000, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, + let unconfirmed_adjustment = UnconfirmedAdjustment { + non_finalized_account: AdjustedAccountBeforeFinalization { + original_account: PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 9_000_000_000, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }, + proposed_adjusted_balance: 9_000_000_000, }, - proposed_adjusted_balance: 9_000_000_000, + weight: 123456, }; let init = (vec![], vec![]); - let (outweighed, ok) = possibly_outweighed_accounts_fold_guts(init, account_info.clone()); + let (outweighed, ok) = + resolve_possibly_outweighed_account(init, unconfirmed_adjustment.clone()); assert_eq!(outweighed, vec![]); - assert_eq!(ok, vec![account_info]) + assert_eq!(ok, vec![unconfirmed_adjustment]) } #[test] - fn picks_only_the_biggest_and_youngest_disqualified_account_in_one_iteration() { - let test_name = "picks_only_the_biggest_and_youngest_disqualified_account_in_one_iteration"; + fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { + let test_name = + "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; let now = SystemTime::now(); - let cw_masq_balance = 2_000_000_000_000; + let cw_masq_balance = 200_000_000_000; let logger = Logger::new(test_name); let subject = make_initialized_subject(now, Some(cw_masq_balance), None); + // None of these accounts would be outside the definition for disqualification + // even if any of them would be gifted by the complete balance from the cw let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { wallet: wallet_1.clone(), - balance_wei: 700_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + balance_wei: 120_000_000_001, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), pending_payable_opt: None, }; let wallet_2 = make_wallet("def"); let account_2 = PayableAccount { wallet: wallet_2.clone(), - balance_wei: 333_000_000_000, + balance_wei: 120_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghi"); let account_3 = PayableAccount { wallet: wallet_3.clone(), - balance_wei: 700_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(99_999)).unwrap(), + balance_wei: 119_999_999_999, + last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), pending_payable_opt: None, }; let wallet_4 = make_wallet("jkl"); let account_4 = PayableAccount { wallet: wallet_4.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(7_000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), pending_payable_opt: None, }; let weights_and_accounts = subject .calculate_weights_for_accounts(vec![account_1, account_2, account_3, account_4]); let weights_total = weights_total(&weights_and_accounts); - let non_finalized_adjusted_accounts = subject - .compute_adjusted_but_non_finalized_accounts(weights_and_accounts, weights_total); + let unconfirmed_adjustments = + subject.compute_adjusted_unconfirmed_adjustments(weights_and_accounts, weights_total); let result = try_finding_an_account_to_disqualify_in_this_iteration( - &non_finalized_adjusted_accounts, + &unconfirmed_adjustments, &logger, ); @@ -816,7 +817,7 @@ mod tests { fn exhaustive_status_is_constructed_properly() { let cw_balance_remainder = 45678; - let result = CwExhaustingStatus::new(cw_balance_remainder); + let result = ConsumingWalletExhaustingStatus::new(cw_balance_remainder); assert_eq!(result.remainder, cw_balance_remainder); assert_eq!(result.accounts_finalized_so_far, vec![]) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index fa03792cf..cdee5e1ca 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -22,11 +22,11 @@ use crate::accountant::payment_adjuster::log_fns::{ accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::{ +use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, ProposedAdjustmentResolution}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, finalize_collection, try_finding_an_account_to_disqualify_in_this_iteration, possibly_outweighed_accounts_fold_guts, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -38,6 +38,7 @@ use std::fmt::{Display, Formatter}; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; @@ -284,12 +285,12 @@ impl PaymentAdjusterReal { adjustment_iteration_result: AdjustmentIterationResult, ) -> Result { match adjustment_iteration_result { - AdjustmentIterationResult::AllAccountsProcessedSmoothly(decided_accounts) => { + AdjustmentIterationResult::AllAccountsProcessed(decided_accounts) => { Ok(RecursionResults::new(decided_accounts, vec![])) } - AdjustmentIterationResult::SpecialTreatmentNeeded { + AdjustmentIterationResult::SpecialTreatmentRequired { case: special_case, - remaining, + remaining_undecided_accounts: remaining, } => { let here_decided_accounts = match special_case { TreatInsignificantAccount => { @@ -384,7 +385,7 @@ impl PaymentAdjusterReal { ) -> AdjustmentIterationResult { let weights_total = weights_total(&weights_and_accounts); let non_finalized_adjusted_accounts = - self.compute_adjusted_but_non_finalized_accounts(weights_and_accounts, weights_total); + self.compute_adjusted_unconfirmed_adjustments(weights_and_accounts, weights_total); let still_unchecked_for_disqualified = match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { @@ -400,14 +401,14 @@ impl PaymentAdjusterReal { Either::Right(with_some_disqualified) => return with_some_disqualified, }; - AdjustmentIterationResult::AllAccountsProcessedSmoothly(verified_accounts) + AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) } - fn compute_adjusted_but_non_finalized_accounts( + fn compute_adjusted_unconfirmed_adjustments( &self, weights_with_accounts: Vec<(u128, PayableAccount)>, weights_total: u128, - ) -> Vec { + ) -> Vec { let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( @@ -430,10 +431,12 @@ impl PaymentAdjusterReal { weights_with_accounts .into_iter() - .map(|(weight, account)| (account, compute_proposed_adjusted_balance(weight))) - .map(|(account, proposed_adjusted_balance)| { + .map(|(weight, account)| { + let proposed_adjusted_balance = compute_proposed_adjusted_balance(weight); + proposed_adjusted_balance_diagnostics(&account, proposed_adjusted_balance); - AdjustedAccountBeforeFinalization::new(account, proposed_adjusted_balance) + + UnconfirmedAdjustment::new(account, proposed_adjusted_balance, weight) }) .collect() } @@ -476,41 +479,46 @@ impl PaymentAdjusterReal { let remaining_reverted = remaining .map(|account_info| { - PayableAccount::from((account_info, ProposedAdjustmentResolution::Revert)) + PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( + account_info, + AdjustmentResolution::Revert, + )) }) .collect(); - Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { + Either::Right(AdjustmentIterationResult::SpecialTreatmentRequired { case: TreatInsignificantAccount, - remaining: remaining_reverted, + remaining_undecided_accounts: remaining_reverted, }) } else { Either::Left(non_finalized_adjusted_accounts) } } - // the term "outweighed account" comes from a phenomenon with the criteria sum of an account + // The term "outweighed account" comes from a phenomenon with the criteria sum of an account // increased significantly based on a different parameter than the debt size, which could've // easily caused we would've granted the account (much) more money than what the accountancy // has recorded. fn handle_possibly_outweighed_accounts( &self, - non_finalized_adjusted_accounts: Vec, - ) -> Either, AdjustmentIterationResult> { + unconfirmed_adjustments: Vec, + ) -> Either, AdjustmentIterationResult> { let init = (vec![], vec![]); - let (outweighed, passing_through) = non_finalized_adjusted_accounts + let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments .into_iter() - .fold(init, possibly_outweighed_accounts_fold_guts); + .fold(init, resolve_possibly_outweighed_account); if outweighed.is_empty() { - Either::Left(passing_through) + Either::Left(properly_adjusted_accounts) } else { - let remaining = - finalize_collection(passing_through, ProposedAdjustmentResolution::Revert); - Either::Right(AdjustmentIterationResult::SpecialTreatmentNeeded { - case: TreatOutweighedAccounts(outweighed), - remaining, + let remaining_undecided_accounts: Vec = + convert_collection(properly_adjusted_accounts); + let ready_outweighed: Vec = + convert_collection(outweighed); + Either::Right(AdjustmentIterationResult::SpecialTreatmentRequired { + case: TreatOutweighedAccounts(ready_outweighed), + remaining_undecided_accounts, }) } } @@ -622,7 +630,7 @@ mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AfterAdjustmentSpecialTreatment::TreatInsignificantAccount; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, weights_total}; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -958,9 +966,11 @@ mod tests { // First, let's have an example of why this test is important let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); let weights_total = weights_total(&criteria_and_accounts); - let proposed_adjustments = subject - .compute_adjusted_but_non_finalized_accounts(criteria_and_accounts, weights_total); - let proposed_adjusted_balance_2 = proposed_adjustments[1].proposed_adjusted_balance; + let unconfirmed_adjustments = + subject.compute_adjusted_unconfirmed_adjustments(criteria_and_accounts, weights_total); + let proposed_adjusted_balance_2 = unconfirmed_adjustments[1] + .non_finalized_account + .proposed_adjusted_balance; // The criteria sum of the second account grew very progressively due to the effect of the greater age; // consequences would've been that redistributing the new balances according to the computed criteria would've // attributed the second account with a larger amount to pay than it would've had before the test started; @@ -1040,9 +1050,9 @@ mod tests { subject.perform_adjustment_by_service_fee(accounts_with_individual_criteria.clone()); let remaining = match result { - AdjustmentIterationResult::SpecialTreatmentNeeded { + AdjustmentIterationResult::SpecialTreatmentRequired { case: TreatInsignificantAccount, - remaining, + remaining_undecided_accounts: remaining, } => remaining, x => panic!("we expected to see a disqualified account but got: {:?}", x), }; @@ -1161,10 +1171,10 @@ mod tests { } #[test] - fn qualified_accounts_count_evens_the_payments_count() { + fn qualified_accounts_count_before_evens_the_payments_count_after() { // Meaning adjustment by service fee but no account elimination init_test_logging(); - let test_name = "qualified_accounts_count_evens_the_payments_count"; + let test_name = "qualified_accounts_count_before_evens_the_payments_count_after"; let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), From 99325391101f01eea2d4c6e76b6f8bdc0c6f3af9 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 2 Feb 2024 12:59:18 +0700 Subject: [PATCH 125/250] GH-711: digging into 'AllAcoountsEliminated' conditions --- .../payment_adjuster/adjustment_runners.rs | 37 ++- .../payment_adjuster/diagnostics.rs | 14 +- .../miscellaneous/helper_functions.rs | 274 ++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 51 ++-- 4 files changed, 178 insertions(+), 198 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 03a852464..36c170682 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -2,7 +2,9 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, +}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; @@ -23,9 +25,7 @@ pub trait AdjustmentRunner { // This specialized method: // a) helps with writing tests that target edge cases, - // b) allows to avoid performing unnecessary computation for an evident result, - // c) the initial check for adjustment possibility is built upon a condition that can be - // realized in its pureness by having this + // b) allows to avoid performing unnecessary computation for an evident result fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, @@ -52,8 +52,10 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - let element_opt = adjust_last_one(payment_adjuster, last_account); - Ok(Either::Left(empty_or_single_element_vector(element_opt))) + match adjust_last_one(payment_adjuster, last_account) { + Ok(non_finalized_account) => Ok(Either::Left(vec![non_finalized_account])), + Err(e) => todo!(), + } } fn adjust_multiple( @@ -89,8 +91,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - let element_opt = adjust_last_one(payment_adjuster, last_account); - Ok(empty_or_single_element_vector(element_opt)) + match adjust_last_one(payment_adjuster, last_account) { + Ok(non_finalized_account) => Ok(vec![non_finalized_account]), + Err(e) => todo!(), + } } fn adjust_multiple( @@ -106,7 +110,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { fn adjust_last_one( payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, -) -> Option { +) -> Result { let cw_balance = payment_adjuster .inner .unallocated_cw_service_fee_balance_minor(); @@ -121,16 +125,17 @@ fn adjust_last_one( cw_balance }; - let mut proposed_adjustment_vec = vec![AdjustedAccountBeforeFinalization::new( + let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( last_account, + u128::MAX, // Doesn't matter really proposed_adjusted_balance, )]; let logger = &payment_adjuster.logger; match try_finding_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { - Some(_) => None, - None => Some(proposed_adjustment_vec.remove(0)), + Some(_) => Err(PaymentAdjusterError::AllAccountsEliminated), + None => Ok(proposed_adjustment_vec.remove(0).non_finalized_account), } } @@ -152,7 +157,9 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; + use crate::accountant::payment_adjuster::{ + Adjustment, PaymentAdjusterError, PaymentAdjusterReal, + }; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -228,7 +235,7 @@ mod tests { assert_eq!( result, - Some(AdjustedAccountBeforeFinalization { + Ok(AdjustedAccountBeforeFinalization { original_account: account, proposed_adjusted_balance: cw_balance, }) @@ -247,7 +254,7 @@ mod tests { let result = adjust_last_one(&payment_adjuster, account.clone()); - assert_eq!(result, None) + assert_eq!(result, Err(PaymentAdjusterError::AllAccountsEliminated)) } #[test] diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 08f71d0ac..421441b15 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; @@ -73,7 +73,9 @@ pub mod ordinary_diagnostic_functions { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + }; use crate::sub_lib::wallet::Wallet; use thousands::Separable; @@ -95,12 +97,12 @@ pub mod ordinary_diagnostic_functions { } pub fn account_nominated_for_disqualification_diagnostics( - account_info: &AdjustedAccountBeforeFinalization, + account_info: &UnconfirmedAdjustment, proposed_adjusted_balance: u128, disqualification_edge: u128, ) { diagnostics!( - account_info.original_account.wallet, + account_info.non_finalized_account.original_account.wallet, "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", "Proposed: {}, disqualification limit: {}", proposed_adjusted_balance.separate_with_commas(), @@ -146,7 +148,7 @@ pub mod ordinary_diagnostic_functions { } pub fn try_finding_an_account_to_disqualify_diagnostics( - disqualification_suspected_accounts: &[&AdjustedAccountBeforeFinalization], + disqualification_suspected_accounts: &[&UnconfirmedAdjustment], wallet: &Wallet, ) { diagnostics!( @@ -214,7 +216,7 @@ pub mod formulas_progressive_characteristics { // For the purposes of debugging and tuning the formulas up to work well together. It lets you // imagine the curve of a criterion in dependence to different supplied input values for // the give parameter - pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; + pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = false; // You must preserve the 'static' keyword // diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8fb68ecb4..2c5c4d989 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -17,7 +17,6 @@ use itertools::Itertools; use masq_lib::logger::Logger; use std::cmp::Ordering; use std::iter::successors; -use std::ops::Not; use web3::types::U256; const MAX_EXPONENT_FOR_10_WITHIN_U128: u32 = 76; @@ -75,29 +74,29 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( pub fn resolve_possibly_outweighed_account( (mut outweighed, mut passing_through): (Vec, Vec), - mut current_account_info: UnconfirmedAdjustment, + mut current_adjustment_info: UnconfirmedAdjustment, ) -> (Vec, Vec) { - if current_account_info + if current_adjustment_info .non_finalized_account .proposed_adjusted_balance - > current_account_info + > current_adjustment_info .non_finalized_account .original_account .balance_wei { - possibly_outweighed_accounts_diagnostics(¤t_account_info.non_finalized_account); + possibly_outweighed_accounts_diagnostics(¤t_adjustment_info.non_finalized_account); - current_account_info + current_adjustment_info .non_finalized_account - .proposed_adjusted_balance = current_account_info + .proposed_adjusted_balance = current_adjustment_info .non_finalized_account .original_account .balance_wei; - outweighed.push(current_account_info); + outweighed.push(current_adjustment_info); (outweighed, passing_through) } else { - passing_through.push(current_account_info); + passing_through.push(current_adjustment_info); (outweighed, passing_through) } } @@ -172,27 +171,24 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( } pub fn try_finding_an_account_to_disqualify_in_this_iteration( - non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], + unconfirmed_adjustments: &[UnconfirmedAdjustment], logger: &Logger, ) -> Option { let disqualification_suspected_accounts = - list_accounts_nominated_for_disqualification(non_finalized_adjusted_accounts); - disqualification_suspected_accounts - .is_empty() - .not() - .then(|| { - let account_to_disqualify = find_account_with_smallest_weight( - &disqualification_suspected_accounts - ); + list_accounts_nominated_for_disqualification(unconfirmed_adjustments); - let wallet = account_to_disqualify.original_account.wallet.clone(); + if !disqualification_suspected_accounts.is_empty() { + let account_to_disqualify = + find_account_with_smallest_weight(&disqualification_suspected_accounts); - try_finding_an_account_to_disqualify_diagnostics( - &disqualification_suspected_accounts, - &wallet, - ); + let wallet = account_to_disqualify.original_account.wallet.clone(); + + try_finding_an_account_to_disqualify_diagnostics( + &disqualification_suspected_accounts, + &wallet, + ); - debug!( + debug!( logger, "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ for disqualification. Chose the least desirable disqualified account as the one \ @@ -201,37 +197,32 @@ pub fn try_finding_an_account_to_disqualify_in_this_iteration( wallet ); - info_log_for_disqualified_account(logger, account_to_disqualify); + info_log_for_disqualified_account(logger, account_to_disqualify); - wallet - }) + Some(wallet) + } else { + None + } } fn find_account_with_smallest_weight<'a>( - accounts: &'a [&'a AdjustedAccountBeforeFinalization], + accounts: &'a [&'a UnconfirmedAdjustment], ) -> &'a AdjustedAccountBeforeFinalization { let first_account = &accounts.first().expect("collection was empty"); - accounts + &accounts .iter() - .fold(**first_account, |largest_so_far, current| { - match Ord::cmp( - ¤t.original_account.balance_wei, - &largest_so_far.original_account.balance_wei, + .fold( + **first_account, + |with_smallest_weight_so_far, current| match Ord::cmp( + ¤t.weight, + &with_smallest_weight_so_far.weight, ) { - Ordering::Less => largest_so_far, - Ordering::Greater => current, - Ordering::Equal => { - // Greater value means younger - if current.original_account.last_paid_timestamp - > largest_so_far.original_account.last_paid_timestamp - { - current - } else { - largest_so_far - } - } - } - }) + Ordering::Less => current, + Ordering::Greater => with_smallest_weight_so_far, + Ordering::Equal => with_smallest_weight_so_far, + }, + ) + .non_finalized_account } struct ConsumingWalletExhaustingStatus { @@ -291,23 +282,29 @@ pub fn isolate_accounts_from_weights( } fn list_accounts_nominated_for_disqualification( - non_finalized_adjusted_accounts: &[AdjustedAccountBeforeFinalization], -) -> Vec<&AdjustedAccountBeforeFinalization> { - non_finalized_adjusted_accounts + unconfirmed_adjustments: &[UnconfirmedAdjustment], +) -> Vec<&UnconfirmedAdjustment> { + unconfirmed_adjustments .iter() - .flat_map(|account_info| { - let disqualification_edge = - calculate_disqualification_edge(account_info.original_account.balance_wei); - let proposed_adjusted_balance = account_info.proposed_adjusted_balance; + .flat_map(|adjustment_info| { + let disqualification_edge = calculate_disqualification_edge( + adjustment_info + .non_finalized_account + .original_account + .balance_wei, + ); + let proposed_adjusted_balance = adjustment_info + .non_finalized_account + .proposed_adjusted_balance; if proposed_adjusted_balance <= disqualification_edge { account_nominated_for_disqualification_diagnostics( - account_info, + adjustment_info, proposed_adjusted_balance, disqualification_edge, ); - Some(account_info) + Some(adjustment_info) } else { None } @@ -346,21 +343,20 @@ pub fn x_or_1(x: u128) -> u128 { } impl From for PayableAccount { - fn from(_: UnconfirmedAdjustment) -> Self { - todo!() + fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { + AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).original_account } } impl From for AdjustedAccountBeforeFinalization { - fn from(_: UnconfirmedAdjustment) -> Self { - todo!() + fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { + unconfirmed_adjustment.non_finalized_account } } impl From for PayableAccount { fn from(resolution_info: NonFinalizedAdjustmentWithResolution) -> Self { match resolution_info.adjustment_resolution { - // TODO if we do this it means it is an outweighed account and so it has already the balance adjusted AdjustmentResolution::Finalize => PayableAccount { balance_wei: resolution_info .non_finalized_adjustment @@ -595,21 +591,44 @@ mod tests { }) } - fn make_non_finalized_adjusted_accounts( - payable_accounts: Vec, - ) -> Vec { - let garbage_proposed_balance = 1_000_000_000; // Unimportant for the usages in the tests this is for - payable_accounts - .into_iter() - .map(|original_account| { - AdjustedAccountBeforeFinalization::new(original_account, garbage_proposed_balance) - }) - .collect() + enum ExpectedResultIdx { + First = 0, + Second = 1, + } + + fn make_unconfirmed_adjustments_by_weights_and_select_expected_result( + weights: Vec, + idx_of_expected_result: ExpectedResultIdx, + ) -> ( + Vec, + AdjustedAccountBeforeFinalization, + ) { + let init: ( + Vec, + Option, + ) = (vec![], None); + let usize_expected_idx = idx_of_expected_result as usize; + let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( + init, + |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { + let original_account = make_payable_account(actual_idx as u64); + let garbage_proposed_balance = 1_000_000_000; // Unimportant for the usages in the tests this is for; + let new_adjustment_to_be_added = + UnconfirmedAdjustment::new(original_account, weight, garbage_proposed_balance); + let expected_result_opt = + if expected_result_opt_so_far.is_none() && actual_idx == usize_expected_idx { + Some(new_adjustment_to_be_added.non_finalized_account.clone()) + } else { + expected_result_opt_so_far + }; + adjustments_so_far.push(new_adjustment_to_be_added); + (adjustments_so_far, expected_result_opt) + }, + ); + (adjustments, expected_result_opt.unwrap()) } - fn by_reference( - adjusted_accounts: &[AdjustedAccountBeforeFinalization], - ) -> Vec<&AdjustedAccountBeforeFinalization> { + fn by_reference(adjusted_accounts: &[UnconfirmedAdjustment]) -> Vec<&UnconfirmedAdjustment> { adjusted_accounts.iter().collect() } @@ -625,79 +644,30 @@ mod tests { #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { - let account_1 = make_payable_account(1000); - let account_2 = make_payable_account(3000); - let account_3 = make_payable_account(2000); - let account_4 = make_payable_account(1000); - let accounts = make_non_finalized_adjusted_accounts(vec![ - account_1, - account_2.clone(), - account_3, - account_4, - ]); - let referenced_non_finalized_accounts = by_reference(&accounts); - - let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); - - assert_eq!( - result, - &AdjustedAccountBeforeFinalization { - original_account: account_2, - proposed_adjusted_balance: 222_000_000 - } - ) - } - - #[test] - fn find_account_with_smallest_weight_for_equal_balances_chooses_younger_account() { - // We prefer to keep the older and so more important accounts in the game - let now = SystemTime::now(); - let account_1 = make_payable_account(111); - let mut account_2 = make_payable_account(333); - account_2.last_paid_timestamp = now.checked_sub(Duration::from_secs(10000)).unwrap(); - let account_3 = make_payable_account(222); - let mut account_4 = make_payable_account(333); - account_4.last_paid_timestamp = now.checked_sub(Duration::from_secs(9999)).unwrap(); - let accounts = make_non_finalized_adjusted_accounts(vec![ - account_1, - account_2, - account_3, - account_4.clone(), - ]); - let referenced_non_finalized_accounts = by_reference(&accounts); + let (adjustments, expected_result) = + make_unconfirmed_adjustments_by_weights_and_select_expected_result( + vec![1004, 1000, 1002, 1001], + ExpectedResultIdx::Second, + ); + let referenced_unconfirmed_adjustments = by_reference(&adjustments); - let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); + let result = find_account_with_smallest_weight(&referenced_unconfirmed_adjustments); - assert_eq!( - result, - &AdjustedAccountBeforeFinalization { - original_account: account_4, - proposed_adjusted_balance: 444_000_000 - } - ) + assert_eq!(result, &expected_result) } #[test] - fn find_account_with_smallest_weight_for_accounts_with_equal_balances_as_well_as_age() { - let account = make_payable_account(111); - let wallet_1 = make_wallet("abc"); - let wallet_2 = make_wallet("def"); - let mut account_1 = account.clone(); - account_1.wallet = wallet_1.clone(); - let mut account_2 = account; - account_2.wallet = wallet_2; - let accounts = make_non_finalized_adjusted_accounts(vec![account_1.clone(), account_2]); - let referenced_non_finalized_accounts = by_reference(&accounts); + fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_it_bumped_into() { + let (adjustments, expected_result) = + make_unconfirmed_adjustments_by_weights_and_select_expected_result( + vec![1111, 1113, 1111], + ExpectedResultIdx::First, + ); + let referenced_non_finalized_accounts = by_reference(&adjustments); let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); - assert_eq!( - result, - &AdjustedAccountBeforeFinalization { - original_account: account_1, - proposed_adjusted_balance: 100_111_000 - } - ) + assert_eq!(result, &expected_result) } #[test] @@ -765,7 +735,7 @@ mod tests { .calculate_weights_for_accounts(vec![account_1, account_2, account_3, account_4]); let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = - subject.compute_adjusted_unconfirmed_adjustments(weights_and_accounts, weights_total); + subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); let result = try_finding_an_account_to_disqualify_in_this_iteration( &unconfirmed_adjustments, @@ -929,6 +899,7 @@ mod tests { #[test] fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { let account_balance = 1_000_000; + let garbage_weight = 22222222; // it plays no role let prepare_account = |n: u64| { let mut account = make_payable_account(n); account.balance_wei = account_balance; @@ -939,27 +910,30 @@ mod tests { let payable_account_3 = prepare_account(3); let edge = calculate_disqualification_edge(account_balance); let proposed_ok_balance = edge + 1; - let account_info_1 = - AdjustedAccountBeforeFinalization::new(payable_account_1, proposed_ok_balance); + let unconfirmed_adjustment_1 = + UnconfirmedAdjustment::new(payable_account_1, garbage_weight, proposed_ok_balance); let proposed_bad_balance_because_equal = edge; - let account_info_2 = AdjustedAccountBeforeFinalization::new( + let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( payable_account_2, + garbage_weight, proposed_bad_balance_because_equal, ); let proposed_bad_balance_because_smaller = edge - 1; - let account_info_3 = AdjustedAccountBeforeFinalization::new( + let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( payable_account_3, + garbage_weight, proposed_bad_balance_because_smaller, ); - let non_finalized_adjusted_accounts = vec![ - account_info_1, - account_info_2.clone(), - account_info_3.clone(), + let unconfirmed_adjustments = vec![ + unconfirmed_adjustment_1, + unconfirmed_adjustment_2.clone(), + unconfirmed_adjustment_3.clone(), ]; - let result = list_accounts_nominated_for_disqualification(&non_finalized_adjusted_accounts); + let result = list_accounts_nominated_for_disqualification(&unconfirmed_adjustments); - let expected_disqualified_accounts = vec![&account_info_2, &account_info_3]; + let expected_disqualified_accounts = + vec![&unconfirmed_adjustment_2, &unconfirmed_adjustment_3]; assert_eq!(result, expected_disqualified_accounts) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index cdee5e1ca..82916e634 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -181,11 +181,13 @@ impl PaymentAdjusterReal { match accounts { Either::Left(non_exhausted_accounts) => { - let affordable_accounts_by_fully_exhausted_cw = exhaust_cw_till_the_last_drop( + let original_cw_service_fee_balance_minor = + self.inner.original_cw_service_fee_balance_minor(); + let exhaustive_affordable_accounts = exhaust_cw_till_the_last_drop( non_exhausted_accounts, - self.inner.original_cw_service_fee_balance_minor(), + original_cw_service_fee_balance_minor, ); - Ok(affordable_accounts_by_fully_exhausted_cw) + Ok(exhaustive_affordable_accounts) } Either::Right(finalized_accounts) => Ok(finalized_accounts), } @@ -208,7 +210,7 @@ impl PaymentAdjusterReal { let last_one = unresolved_qualified_accounts .into_iter() .next() - .expect("previous if must be wrong"); + .expect("previous if stmt must be wrong"); return adjustment_runner.adjust_last_one(self, last_one); } @@ -297,7 +299,7 @@ impl PaymentAdjusterReal { if remaining.is_empty() { debug!(self.logger, "Last remaining account ended up disqualified"); - todo!(); + todo!("I believe this can never happen!! it would first go by the short path of 'adjust_last_one'"); return Err(PaymentAdjusterError::AllAccountsEliminated); } @@ -309,7 +311,7 @@ impl PaymentAdjusterReal { } }; - // Note: there always is at least one remaining account besides an outweighed one + // Note: There always must be at least one remaining account besides an outweighed one, by definition let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( remaining, @@ -385,7 +387,7 @@ impl PaymentAdjusterReal { ) -> AdjustmentIterationResult { let weights_total = weights_total(&weights_and_accounts); let non_finalized_adjusted_accounts = - self.compute_adjusted_unconfirmed_adjustments(weights_and_accounts, weights_total); + self.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); let still_unchecked_for_disqualified = match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { @@ -404,7 +406,7 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) } - fn compute_adjusted_unconfirmed_adjustments( + fn compute_unconfirmed_adjustments( &self, weights_with_accounts: Vec<(u128, PayableAccount)>, weights_total: u128, @@ -436,7 +438,7 @@ impl PaymentAdjusterReal { proposed_adjusted_balance_diagnostics(&account, proposed_adjusted_balance); - UnconfirmedAdjustment::new(account, proposed_adjusted_balance, weight) + UnconfirmedAdjustment::new(account, weight, proposed_adjusted_balance) }) .collect() } @@ -462,25 +464,21 @@ impl PaymentAdjusterReal { } fn consider_account_disqualification( - non_finalized_adjusted_accounts: Vec, + unconfirmed_adjustments: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { if let Some(disqualified_account_wallet) = - try_finding_an_account_to_disqualify_in_this_iteration( - &non_finalized_adjusted_accounts, - logger, - ) + try_finding_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, logger) { - let remaining = non_finalized_adjusted_accounts - .into_iter() - .filter(|account_info| { - account_info.original_account.wallet != disqualified_account_wallet - }); + let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { + account_info.non_finalized_account.original_account.wallet + != disqualified_account_wallet + }); let remaining_reverted = remaining .map(|account_info| { PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( - account_info, + account_info.non_finalized_account, AdjustmentResolution::Revert, )) }) @@ -491,14 +489,13 @@ impl PaymentAdjusterReal { remaining_undecided_accounts: remaining_reverted, }) } else { - Either::Left(non_finalized_adjusted_accounts) + Either::Left(convert_collection(unconfirmed_adjustments)) } } - // The term "outweighed account" comes from a phenomenon with the criteria sum of an account - // increased significantly based on a different parameter than the debt size, which could've - // easily caused we would've granted the account (much) more money than what the accountancy - // has recorded. + // The term "outweighed account" comes from a phenomenon with account weight increasing + // significantly based on a different parameter than the debt size. Untreated, we would which + // grant the account (much) more money than what the accountancy has recorded for it. fn handle_possibly_outweighed_accounts( &self, unconfirmed_adjustments: Vec, @@ -967,7 +964,7 @@ mod tests { let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); let weights_total = weights_total(&criteria_and_accounts); let unconfirmed_adjustments = - subject.compute_adjusted_unconfirmed_adjustments(criteria_and_accounts, weights_total); + subject.compute_unconfirmed_adjustments(criteria_and_accounts, weights_total); let proposed_adjusted_balance_2 = unconfirmed_adjustments[1] .non_finalized_account .proposed_adjusted_balance; @@ -1080,7 +1077,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let service_fee_balance_in_minor_units = ((3_000_000_000_000 + let service_fee_balance_in_minor_units = ((1_000_000_000_000 * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) - 1; From 733993a6dedd74b5d1d53ed02de2bcb97e988804 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:21:51 +0700 Subject: [PATCH 126/250] GH-711: PA looks healthy now --- node/src/accountant/mod.rs | 3 +- .../payment_adjuster/adjustment_runners.rs | 37 ++++++------ .../payment_adjuster/diagnostics.rs | 2 +- .../miscellaneous/helper_functions.rs | 46 +++++++++++++- node/src/accountant/payment_adjuster/mod.rs | 60 +++++++++++++------ 5 files changed, 105 insertions(+), 43 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index e44db4965..2174763f4 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1682,8 +1682,7 @@ mod tests { log_handler.exists_log_containing(&format!( "WARN: {test_name}: Payment adjustment has not \ produced any executable payments. Please add funds into your consuming wallet in order to avoid bans \ - from your creditors. Failure reason: While chances were according to the preliminary analysis, \ - the adjustment algorithm rejected each payable" + from your creditors. Failure reason: The adjustment algorithm had to eliminate each payable given the resources" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 36c170682..32efdf643 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -52,10 +52,9 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - match adjust_last_one(payment_adjuster, last_account) { - Ok(non_finalized_account) => Ok(Either::Left(vec![non_finalized_account])), - Err(e) => todo!(), - } + Ok(Either::Left(empty_or_single_element_vector( + adjust_last_one_opt(payment_adjuster, last_account), + ))) } fn adjust_multiple( @@ -91,10 +90,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - match adjust_last_one(payment_adjuster, last_account) { - Ok(non_finalized_account) => Ok(vec![non_finalized_account]), - Err(e) => todo!(), - } + Ok(empty_or_single_element_vector(adjust_last_one_opt( + payment_adjuster, + last_account, + ))) } fn adjust_multiple( @@ -107,10 +106,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { } } -fn adjust_last_one( +fn adjust_last_one_opt( payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, -) -> Result { +) -> Option { let cw_balance = payment_adjuster .inner .unallocated_cw_service_fee_balance_minor(); @@ -134,8 +133,8 @@ fn adjust_last_one( let logger = &payment_adjuster.logger; match try_finding_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { - Some(_) => Err(PaymentAdjusterError::AllAccountsEliminated), - None => Ok(proposed_adjustment_vec.remove(0).non_finalized_account), + Some(_) => None, + None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), } } @@ -152,14 +151,12 @@ fn empty_or_single_element_vector( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one, empty_or_single_element_vector, AdjustmentRunner, + adjust_last_one_opt, empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::{ - Adjustment, PaymentAdjusterError, PaymentAdjusterReal, - }; + use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::make_payable_account; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -231,11 +228,11 @@ mod tests { }; let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - let result = adjust_last_one(&payment_adjuster, account.clone()); + let result = adjust_last_one_opt(&payment_adjuster, account.clone()); assert_eq!( result, - Ok(AdjustedAccountBeforeFinalization { + Some(AdjustedAccountBeforeFinalization { original_account: account, proposed_adjusted_balance: cw_balance, }) @@ -252,9 +249,9 @@ mod tests { }; let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - let result = adjust_last_one(&payment_adjuster, account.clone()); + let result = adjust_last_one_opt(&payment_adjuster, account.clone()); - assert_eq!(result, Err(PaymentAdjusterError::AllAccountsEliminated)) + assert_eq!(result, None) } #[test] diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 421441b15..c3897a960 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 2c5c4d989..b4db1f5f8 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -13,7 +13,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ PercentageAccountInsignificance, UnconfirmedAdjustment, }; use crate::sub_lib::wallet::Wallet; -use itertools::Itertools; +use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use std::cmp::Ordering; use std::iter::successors; @@ -28,6 +28,15 @@ pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance divisor: 2, }; +pub fn found_zero_affordable_accounts( + accounts: &Either, Vec>, +) -> bool { + match accounts { + Either::Left(vector) => vector.is_empty(), + Either::Right(vector) => vector.is_empty(), + } +} + pub fn sum_as(collection: &[T], arranger: F) -> N where N: From, @@ -379,8 +388,8 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, find_account_with_smallest_weight, - list_accounts_nominated_for_disqualification, log_10, log_2, - resolve_possibly_outweighed_account, + found_zero_affordable_accounts, list_accounts_nominated_for_disqualification, log_10, + log_2, resolve_possibly_outweighed_account, try_finding_an_account_to_disqualify_in_this_iteration, weights_total, ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, @@ -410,6 +419,37 @@ mod tests { ); } + #[test] + fn found_zero_affordable_accounts_returns_true_for_non_finalized_accounts() { + let result = found_zero_affordable_accounts(&Either::Left(vec![])); + + assert_eq!(result, true) + } + + #[test] + fn found_zero_affordable_accounts_returns_false_for_non_finalized_accounts() { + let result = found_zero_affordable_accounts(&Either::Left(vec![ + AdjustedAccountBeforeFinalization::new(make_payable_account(456), 1234), + ])); + + assert_eq!(result, false) + } + + #[test] + fn found_zero_affordable_accounts_returns_true_for_finalized_accounts() { + let result = found_zero_affordable_accounts(&Either::Right(vec![])); + + assert_eq!(result, true) + } + + #[test] + fn found_zero_affordable_accounts_returns_false_for_finalized_accounts() { + let result = + found_zero_affordable_accounts(&Either::Right(vec![make_payable_account(123)])); + + assert_eq!(result, false) + } + #[test] fn log_10_works() { [ diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 82916e634..d131c07fe 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -26,7 +26,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::Require TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, found_zero_affordable_accounts}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -179,6 +179,10 @@ impl PaymentAdjusterReal { TransactionAndServiceFeeAdjustmentRunner {}, )?; + if found_zero_affordable_accounts(&accounts) { + return Err(PaymentAdjusterError::AllAccountsEliminated); + } + match accounts { Either::Left(non_exhausted_accounts) => { let original_cw_service_fee_balance_minor = @@ -192,7 +196,6 @@ impl PaymentAdjusterReal { Either::Right(finalized_accounts) => Ok(finalized_accounts), } } - fn calculate_criteria_and_propose_adjustments_recursively( &mut self, unresolved_qualified_accounts: Vec, @@ -297,10 +300,10 @@ impl PaymentAdjusterReal { let here_decided_accounts = match special_case { TreatInsignificantAccount => { if remaining.is_empty() { - debug!(self.logger, "Last remaining account ended up disqualified"); - - todo!("I believe this can never happen!! it would first go by the short path of 'adjust_last_one'"); - return Err(PaymentAdjusterError::AllAccountsEliminated); + // a) only one account can be eliminated in a single iteration, + // b) if there is one last undecided account, it goes on into + // a shortcut section, never coming here + unreachable!("Not possible by original design") } vec![] @@ -626,7 +629,7 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustmentIterationResult; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustmentIterationResult, RequiredSpecialTreatment}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, weights_total}; use crate::accountant::payment_adjuster::test_utils::{ @@ -1064,7 +1067,7 @@ mod tests { let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: 3_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { @@ -1073,7 +1076,13 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2]; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 2_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2, account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1103,6 +1112,20 @@ mod tests { assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated) } + #[test] + #[should_panic( + expected = "internal error: entered unreachable code: Not possible by original design" + )] + fn disqualified_account_with_no_remaining_accounts_is_not_possible() { + let mut subject = PaymentAdjusterReal::new(); + let iteration_result = AdjustmentIterationResult::SpecialTreatmentRequired { + case: RequiredSpecialTreatment::TreatInsignificantAccount, + remaining_undecided_accounts: vec![], + }; + + let _ = subject.resolve_current_iteration_result(iteration_result); + } + #[test] fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { init_test_logging(); @@ -1137,11 +1160,14 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup, now); - // None on the output, because all the accounts were eventually disqualified; - // normally, the feasibility check at the entrance wouldn't allow this - assert_eq!(result.affordable_accounts, vec![]); + // The error isn't important. Received just because we set an almost empty wallet + let err = match result { + Ok(_) => panic!("we expected err but got ok"), + Err(e) => e, + }; + assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( "INFO: {test_name}: Shortage of MASQ in your consuming wallet impacts on payable \ @@ -1589,7 +1615,7 @@ mod tests { // scenario B let second_scenario_name = merge_test_name_with_test_scenario("first more significant by balance"); - let expected_wallet_of_the_winning_account = &w2; + let expected_wallet_of_the_winning_account = &w1; test_two_competitive_accounts_with_one_disqualified( &second_scenario_name, @@ -1606,7 +1632,7 @@ mod tests { // scenario C let third_scenario_name = merge_test_name_with_test_scenario("second more significant by age"); - let expected_wallet_of_the_winning_account = &w1; + let expected_wallet_of_the_winning_account = &w2; test_two_competitive_accounts_with_one_disqualified( &third_scenario_name, @@ -1614,8 +1640,8 @@ mod tests { common: common_input, account_1_balance_positive_correction_minor: 0, account_2_balance_positive_correction_minor: 0, - account_1_age_positive_correction_secs: 1, - account_2_age_positive_correction_secs: 0, + account_1_age_positive_correction_secs: 0, + account_2_age_positive_correction_secs: 1, }, expected_wallet_of_the_winning_account, ); From 2947e675e740a3b094fc60021fff8a8725c3269b Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:56:50 +0700 Subject: [PATCH 127/250] GH-711: fancier manipulation of the weight and account pairs --- .../payment_adjuster/adjustment_runners.rs | 20 +++--- .../miscellaneous/data_structures.rs | 18 +++++- .../miscellaneous/helper_functions.rs | 57 ++++++++++------- node/src/accountant/payment_adjuster/mod.rs | 63 ++++++++++--------- .../pre_adjustment_analyzer.rs | 10 +-- 5 files changed, 96 insertions(+), 72 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 32efdf643..c4bc49736 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -3,7 +3,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; @@ -35,7 +35,7 @@ pub trait AdjustmentRunner { fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weighted_accounts_in_descending_order: Vec, ) -> Self::ReturnType; } @@ -60,12 +60,12 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weighted_accounts_in_descending_order: Vec, ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { return payment_adjuster.begin_with_adjustment_by_transaction_fee( - weights_and_accounts_in_descending_order, + weighted_accounts_in_descending_order, limit, ) } @@ -73,9 +73,8 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { }; Ok(Either::Left( - payment_adjuster.propose_possible_adjustment_recursively( - weights_and_accounts_in_descending_order, - )?, + payment_adjuster + .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order)?, )) } } @@ -99,10 +98,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { fn adjust_multiple( &self, payment_adjuster: &mut PaymentAdjusterReal, - weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weighted_accounts_in_descending_order: Vec, ) -> Self::ReturnType { payment_adjuster - .propose_possible_adjustment_recursively(weights_and_accounts_in_descending_order) + .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order) } } @@ -125,8 +124,7 @@ fn adjust_last_one_opt( cw_balance }; let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - last_account, - u128::MAX, // Doesn't matter really + WeightedAccount::new(last_account, u128::MAX), // The weight doesn't matter really and is made up proposed_adjusted_balance, )]; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index ab375689e..a648f1d24 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -2,6 +2,18 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; +#[derive(Clone)] +pub struct WeightedAccount { + pub account: PayableAccount, + pub weight: u128, +} + +impl WeightedAccount { + pub fn new(account: PayableAccount, weight: u128) -> Self { + Self { account, weight } + } +} + #[derive(Debug)] pub enum AdjustmentIterationResult { AllAccountsProcessed(Vec), @@ -63,13 +75,13 @@ pub struct UnconfirmedAdjustment { } impl UnconfirmedAdjustment { - pub fn new(account: PayableAccount, weight: u128, proposed_adjusted_balance: u128) -> Self { + pub fn new(weighted_account: WeightedAccount, proposed_adjusted_balance: u128) -> Self { Self { non_finalized_account: AdjustedAccountBeforeFinalization::new( - account, + weighted_account.account, proposed_adjusted_balance, ), - weight, + weight: weighted_account.weight, } } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index b4db1f5f8..267f9ad77 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -10,7 +10,7 @@ use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functi use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, - PercentageAccountInsignificance, UnconfirmedAdjustment, + PercentageAccountInsignificance, UnconfirmedAdjustment, WeightedAccount, }; use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; @@ -45,21 +45,23 @@ where collection.iter().map(arranger).sum::().into() } -pub fn weights_total(weights_and_accounts: &[(u128, PayableAccount)]) -> u128 { - sum_as(weights_and_accounts, |(weight, _)| *weight) +pub fn weights_total(weights_and_accounts: &[WeightedAccount]) -> u128 { + sum_as(weights_and_accounts, |weighted_account| { + weighted_account.weight + }) } pub fn drop_accounts_that_cannot_be_afforded_due_to_service_fee( - weights_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, -) -> Vec<(u128, PayableAccount)> { +) -> Vec { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", affordable_transaction_count, - weights_and_accounts_in_descending_order.len() + weighted_accounts_in_descending_order.len() ); - weights_and_accounts_in_descending_order + weighted_accounts_in_descending_order .into_iter() .take(affordable_transaction_count as usize) .collect() @@ -274,19 +276,19 @@ impl ConsumingWalletExhaustingStatus { } pub fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, -) -> Vec<(u128, PayableAccount)> { + unsorted: impl Iterator, +) -> Vec { unsorted - .sorted_by(|(weight_a, _), (weight_b, _)| Ord::cmp(weight_b, weight_a)) + .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) .collect() } pub fn isolate_accounts_from_weights( - weights_and_accounts: Vec<(u128, PayableAccount)>, + weights_and_accounts: Vec, ) -> Vec { weights_and_accounts .into_iter() - .map(|(_, account)| account) + .map(|weighted_account| weighted_account.account) .collect() } @@ -384,6 +386,7 @@ mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, UnconfirmedAdjustment, + WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, @@ -539,13 +542,17 @@ mod tests { let results = accounts_with_their_weights .into_iter() - .map(|(weight, account)| { + .map(|weighted_account| { // Scenario simplification: we assume there is always just one account to process in a time let computed_coefficient = compute_mul_coefficient_preventing_fractional_numbers( cw_balance_in_minor, - weight, + weighted_account.weight, ); - (computed_coefficient, account.wallet, weight) + ( + computed_coefficient, + weighted_account.account.wallet, + weighted_account.weight, + ) }) .collect::>(); @@ -653,8 +660,10 @@ mod tests { |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { let original_account = make_payable_account(actual_idx as u64); let garbage_proposed_balance = 1_000_000_000; // Unimportant for the usages in the tests this is for; - let new_adjustment_to_be_added = - UnconfirmedAdjustment::new(original_account, weight, garbage_proposed_balance); + let new_adjustment_to_be_added = UnconfirmedAdjustment::new( + WeightedAccount::new(original_account, weight), + garbage_proposed_balance, + ); let expected_result_opt = if expected_result_opt_so_far.is_none() && actual_idx == usize_expected_idx { Some(new_adjustment_to_be_added.non_finalized_account.clone()) @@ -950,18 +959,18 @@ mod tests { let payable_account_3 = prepare_account(3); let edge = calculate_disqualification_edge(account_balance); let proposed_ok_balance = edge + 1; - let unconfirmed_adjustment_1 = - UnconfirmedAdjustment::new(payable_account_1, garbage_weight, proposed_ok_balance); + let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( + WeightedAccount::new(payable_account_1, garbage_weight), + proposed_ok_balance, + ); let proposed_bad_balance_because_equal = edge; let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( - payable_account_2, - garbage_weight, + WeightedAccount::new(payable_account_2, garbage_weight), proposed_bad_balance_because_equal, ); let proposed_bad_balance_because_smaller = edge - 1; let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( - payable_account_3, - garbage_weight, + WeightedAccount::new(payable_account_3, garbage_weight), proposed_bad_balance_because_smaller, ); let unconfirmed_adjustments = vec![ @@ -979,7 +988,7 @@ mod tests { fn get_extreme_weights_and_initial_accounts_order( months_of_debt_and_balances: Vec<(usize, u128)>, - ) -> (Vec<(u128, PayableAccount)>, Vec) { + ) -> (Vec, Vec) { let now = SystemTime::now(); let accounts = make_extreme_accounts(Either::Right(months_of_debt_and_balances), now); let wallets_in_order = accounts diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d131c07fe..c3dee1e82 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -25,7 +25,7 @@ use crate::accountant::payment_adjuster::log_fns::{ use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, found_zero_affordable_accounts}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; @@ -227,7 +227,7 @@ impl PaymentAdjusterReal { fn begin_with_adjustment_by_transaction_fee( &mut self, - criteria_and_accounts_in_descending_order: Vec<(u128, PayableAccount)>, + weighted_accounts_in_descending_order: Vec, already_known_affordable_transaction_count: u16, ) -> Result< Either, Vec>, @@ -235,7 +235,7 @@ impl PaymentAdjusterReal { > { let accounts_with_criteria_affordable_by_transaction_fee = drop_accounts_that_cannot_be_afforded_due_to_service_fee( - criteria_and_accounts_in_descending_order, + weighted_accounts_in_descending_order, already_known_affordable_transaction_count, ); let unallocated_balance = self.inner.unallocated_cw_service_fee_balance_minor(); @@ -272,9 +272,9 @@ impl PaymentAdjusterReal { fn propose_possible_adjustment_recursively( &mut self, - weights_and_accounts: Vec<(u128, PayableAccount)>, + weighed_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let current_iteration_result = self.perform_adjustment_by_service_fee(weights_and_accounts); + let current_iteration_result = self.perform_adjustment_by_service_fee(weighed_accounts); let recursion_results = self.resolve_current_iteration_result(current_iteration_result)?; @@ -332,7 +332,7 @@ impl PaymentAdjusterReal { fn calculate_weights_for_accounts( &self, accounts: Vec, - ) -> Vec<(u128, PayableAccount)> { + ) -> Vec { let criteria_calculators: Vec> = vec![ Box::new(BalanceCriterionCalculator::new()), Box::new(AgeCriterionCalculator::new(self)), @@ -345,8 +345,8 @@ impl PaymentAdjusterReal { &self, criteria_calculators: Vec>, qualified_accounts: Vec, - ) -> Vec<(u128, PayableAccount)> { - let weights_and_accounts = qualified_accounts.into_iter().map(|account| { + ) -> Vec { + let weighted_accounts = qualified_accounts.into_iter().map(|account| { let weight = criteria_calculators .iter() @@ -368,10 +368,10 @@ impl PaymentAdjusterReal { summed_up }); - (weight, account) + WeightedAccount::new(account, weight) }); - sort_in_descendant_order_by_weights(weights_and_accounts) + sort_in_descendant_order_by_weights(weighted_accounts) } fn have_calculator_calculate_its_criterion( @@ -386,11 +386,11 @@ impl PaymentAdjusterReal { fn perform_adjustment_by_service_fee( &self, - weights_and_accounts: Vec<(u128, PayableAccount)>, + weighted_accounts: Vec, ) -> AdjustmentIterationResult { - let weights_total = weights_total(&weights_and_accounts); + let weights_total = weights_total(&weighted_accounts); let non_finalized_adjusted_accounts = - self.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); + self.compute_unconfirmed_adjustments(weighted_accounts, weights_total); let still_unchecked_for_disqualified = match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { @@ -411,7 +411,7 @@ impl PaymentAdjusterReal { fn compute_unconfirmed_adjustments( &self, - weights_with_accounts: Vec<(u128, PayableAccount)>, + weighted_accounts: Vec, weights_total: u128, ) -> Vec { let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); @@ -421,7 +421,7 @@ impl PaymentAdjusterReal { weights_total, ); - let balanced_fragment_of_cw_balance = Self::compute_balanced_cw_fragment( + let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( cw_service_fee_balance, weights_total, multiplication_coefficient, @@ -429,24 +429,28 @@ impl PaymentAdjusterReal { let compute_proposed_adjusted_balance = |weight: u128| { u128::try_from( - U256::from(weight) * balanced_fragment_of_cw_balance / multiplication_coefficient, + U256::from(weight) * proportional_cw_balance_fragment / multiplication_coefficient, ) .expect("mul coefficient computation worked, this must too") }; - weights_with_accounts + weighted_accounts .into_iter() - .map(|(weight, account)| { - let proposed_adjusted_balance = compute_proposed_adjusted_balance(weight); + .map(|weighted_account| { + let proposed_adjusted_balance = + compute_proposed_adjusted_balance(weighted_account.weight); - proposed_adjusted_balance_diagnostics(&account, proposed_adjusted_balance); + proposed_adjusted_balance_diagnostics( + &weighted_account.account, + proposed_adjusted_balance, + ); - UnconfirmedAdjustment::new(account, weight, proposed_adjusted_balance) + UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) }) .collect() } - fn compute_balanced_cw_fragment( + fn compute_proportional_cw_fragment( cw_service_fee_balance: u128, weights_total: u128, multiplication_coefficient: U256, @@ -918,15 +922,15 @@ mod tests { let mut previous_weight = u128::MAX; let accounts_alone = criteria_and_accounts .into_iter() - .map(|(weight, account)| { + .map(|weighted_account| { assert!( - previous_weight > weight, + previous_weight > weighted_account.weight, "Previous criteria {} wasn't larger than {} but should've been", previous_weight, - weight + weighted_account.weight ); - previous_weight = weight; - account + previous_weight = weighted_account.weight; + weighted_account.account }) .collect::>(); assert_eq!(accounts_alone, vec![account_3, account_1, account_2]) @@ -1044,10 +1048,9 @@ mod tests { Some(consuming_wallet_balance), Some(Logger::new(test_name)), ); - let accounts_with_individual_criteria = subject.calculate_weights_for_accounts(accounts); + let weighted_accounts = subject.calculate_weights_for_accounts(accounts); - let result = - subject.perform_adjustment_by_service_fee(accounts_with_individual_criteria.clone()); + let result = subject.perform_adjustment_by_service_fee(weighted_accounts.clone()); let remaining = match result { AdjustmentIterationResult::SpecialTreatmentRequired { diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs index f7fa15f24..1f54c7f94 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -4,7 +4,9 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::log_fns::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionCountsWithin16bits; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + TransactionCountsWithin16bits, WeightedAccount, +}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, sum_as, }; @@ -85,14 +87,14 @@ impl PreAdjustmentAnalyzer { pub fn check_need_of_adjustment_by_service_fee( &self, logger: &Logger, - payables: Either<&[PayableAccount], &[(u128, PayableAccount)]>, + payables: Either<&[PayableAccount], &[WeightedAccount]>, cw_service_fee_balance_minor: u128, ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { Either::Left(accounts) => accounts.iter().collect(), - Either::Right(weights_and_accounts) => weights_and_accounts + Either::Right(weighted_accounts) => weighted_accounts .iter() - .map(|(_, account)| account) + .map(|weighted_account| &weighted_account.account) .collect(), }; From 0e989610b72d60e40f80c7eaeb39e709b8912ad2 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:46:06 +0700 Subject: [PATCH 128/250] GH-711: loading test shows good overall reliability --- .../payment_adjuster/adjustment_runners.rs | 2 +- .../balance_criterion_calculator.rs | 2 + .../payment_adjuster/diagnostics.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 2 +- .../payment_adjuster/loading_test/mod.rs | 400 ++++++++++++++++++ .../accountant/payment_adjuster/log_fns.rs | 2 +- .../miscellaneous/data_structures.rs | 2 +- .../miscellaneous/helper_functions.rs | 6 +- .../payment_adjuster/miscellaneous/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 13 +- .../pre_adjustment_analyzer.rs | 2 +- .../accountant/payment_adjuster/test_utils.rs | 2 +- 12 files changed, 425 insertions(+), 14 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/loading_test/mod.rs diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index c4bc49736..439fe60e9 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 09ec3e25b..901697274 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -4,6 +4,7 @@ use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; +use thousands::Separable; // This parameter affects the steepness inversely, but just slowly. // @@ -34,6 +35,7 @@ impl BalanceCriterionCalculator { pub fn new() -> Self { let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { let balance_minor = balance_minor_holder.balance_input(); + eprintln!("balance minor: {}", balance_minor.separate_with_commas()); let argument_for_log = Self::calculate_binary_argument(balance_minor); let binary_weight = Self::nonzero_log2(argument_for_log); balance_minor diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index c3897a960..8f44cd2f9 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #[cfg(test)] use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 9b3e0331f..b4f0ec7ba 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use std::time::SystemTime; diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs new file mode 100644 index 000000000..8b3c790d4 --- /dev/null +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -0,0 +1,400 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +#![cfg(test)] + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; +use crate::accountant::payment_adjuster::{ + Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, +}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; +use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; +use crate::sub_lib::wallet::Wallet; +use crate::test_utils::make_wallet; +use itertools::Itertools; +use masq_lib::test_utils::utils::ensure_node_home_directory_exists; +use rand; +use rand::rngs::ThreadRng; +use rand::{thread_rng, Rng}; +use std::fs::File; +use std::io::Write; +use std::time::SystemTime; +use thousands::Separable; + +#[test] +fn loading_test_with_randomized_params() { + let now = SystemTime::now(); + let mut gn = thread_rng(); + let mut subject = PaymentAdjusterReal::new(); + + let scenarios = generate_scenarios(&mut gn, now, 100); + + let scenario_results = scenarios + .into_iter() + .map(|prepared_adjustment| { + let account_infos = + suck_off_account_infos(&prepared_adjustment.qualified_payables, now); + let required_adjustment = prepared_adjustment.adjustment.clone(); + let cw_service_fee_balance_minor = + prepared_adjustment.agent.service_fee_balance_minor(); + + let payment_adjuster_result = subject.adjust_payments(prepared_adjustment, now); + + prepare_single_scenario_result( + payment_adjuster_result, + account_infos, + required_adjustment, + cw_service_fee_balance_minor, + ) + }) + .collect(); + + render_results(scenario_results) +} + +fn generate_scenarios( + gn: &mut ThreadRng, + now: SystemTime, + number_of_scenarios: usize, +) -> Vec { + (0..number_of_scenarios) + .map(|_| make_single_scenario(gn, now)) + .collect() +} + +fn make_single_scenario(gn: &mut ThreadRng, now: SystemTime) -> PreparedAdjustment { + let cw_service_fee_balance = { + let base = generate_non_zero_usize(gn, usize::MAX) as u128; + base * generate_non_zero_usize(gn, 1000) as u128 + }; + let qualified_payables = make_qualified_payables(gn, now, cw_service_fee_balance); + let agent = make_agent(cw_service_fee_balance); + let adjustment = make_adjustment(gn, qualified_payables.len()); + PreparedAdjustment::new(qualified_payables, Box::new(agent), None, adjustment) +} + +fn make_qualified_payables( + gn: &mut ThreadRng, + now: SystemTime, + cw_service_fee_balance: u128, +) -> Vec { + let accounts_count = generate_non_zero_usize(gn, 20) + 1; + let average_portion = cw_service_fee_balance / accounts_count as u128; + (0..accounts_count) + .map(|idx| { + let wallet = make_wallet(&format!("wallet{}", idx)); + let debt_age = 2000 + generate_non_zero_usize(gn, 200000); + let balance_wei = + average_portion + average_portion / 100 * generate_non_zero_usize(gn, 1000) as u128; + PayableAccount { + wallet, + balance_wei, + last_paid_timestamp: from_time_t(to_time_t(now) - debt_age as i64), + pending_payable_opt: None, + } + }) + .collect() +} + +fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { + BlockchainAgentMock::default() + // For scenario evaluation + .service_fee_balance_minor_result(cw_service_fee_balance) + // For PaymentAdjuster itself + .service_fee_balance_minor_result(cw_service_fee_balance) +} + +fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { + let also_by_transaction_fee = generate_boolean(gn); + if also_by_transaction_fee && accounts_count > 2 { + let can_afford = generate_non_zero_usize(gn, accounts_count); + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: u16::try_from(can_afford).unwrap(), + } + } else { + Adjustment::ByServiceFee + } +} + +fn prepare_single_scenario_result( + result: Result, + account_infos: Vec, + required_adjustment: Adjustment, + cw_service_fee_balance_minor: u128, +) -> ScenarioResult { + let common = CommonScenarioInfo { + cw_service_fee_balance_minor, + required_adjustment, + }; + let result = match result { + Ok(outbound_payment_instructions) => { + let mut adjusted_accounts = outbound_payment_instructions.affordable_accounts; + let adjusted_accounts = account_infos + .into_iter() + .map(|account_info| { + prepare_interpretable_account_resolution(account_info, &mut adjusted_accounts) + }) + .collect(); + let sorted_interpretable_adjustments = + sort_interpretable_adjustments(adjusted_accounts); + Ok(SuccessfulAdjustment { + common, + sorted_interpretable_adjustments, + }) + } + Err(adjuster_error) => Err(FailedAdjustment { + common, + account_infos, + adjuster_error, + }), + }; + + ScenarioResult::new(result) +} + +struct ScenarioResult { + result: Result, +} + +impl ScenarioResult { + fn new(result: Result) -> Self { + Self { result } + } +} + +struct SuccessfulAdjustment { + common: CommonScenarioInfo, + sorted_interpretable_adjustments: Vec, +} + +struct FailedAdjustment { + common: CommonScenarioInfo, + account_infos: Vec, + adjuster_error: PaymentAdjusterError, +} + +fn suck_off_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec { + accounts + .iter() + .map(|account| AccountInfo { + wallet: account.wallet.clone(), + initially_requested_service_fee_minor: account.balance_wei, + debt_age_s: now + .duration_since(account.last_paid_timestamp) + .unwrap() + .as_secs(), + }) + .collect() +} + +fn render_results(scenario_results: Vec) { + let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); + let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); + scenario_results + .into_iter() + .for_each(|scenario_result| render_single_scenario(&mut file, scenario_result)) +} + +fn render_single_scenario(file: &mut File, scenario: ScenarioResult) { + match scenario.result { + Ok(positive) => render_positive_scenario(file, positive), + Err(negative) => render_negative_scenario(file, negative), + } +} + +fn render_scenario_header( + file: &mut File, + cw_service_fee_balance_minor: u128, + required_adjustment: Adjustment, +) { + file.write_fmt(format_args!( + "CW service fee balance: {} wei\n\ + Maximal txt count due to CW txt fee balance: {}\n", + cw_service_fee_balance_minor.separate_with_commas(), + resolve_affordable_transaction_count(required_adjustment) + )) + .unwrap(); +} +fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { + write_thick_dividing_line(file); + render_scenario_header( + file, + result.common.cw_service_fee_balance_minor, + result.common.required_adjustment, + ); + write_thin_dividing_line(file); + let adjusted_accounts = result.sorted_interpretable_adjustments; + adjusted_accounts.into_iter().for_each(|account| { + single_account_liner( + file, + account.initial_balance, + account.debt_age_s, + account.bill_coverage_in_percentage_opt, + ) + }) +} + +const BALANCE_COLUMN_WIDTH: usize = 30; +const AGE_COLUMN_WIDTH: usize = 7; + +fn single_account_liner( + file: &mut File, + balance_minor: u128, + age_s: u64, + bill_coverage_in_percentage_opt: Option, +) { + let _ = file + .write_fmt(format_args!( + "{:>balance_width$} wei | {:>age_width$} s | {}\n", + balance_minor.separate_with_commas(), + age_s.separate_with_commas(), + resolve_account_ending_status_graphically(bill_coverage_in_percentage_opt), + balance_width = BALANCE_COLUMN_WIDTH, + age_width = AGE_COLUMN_WIDTH + )) + .unwrap(); +} + +fn resolve_account_ending_status_graphically( + bill_coverage_in_percentage_opt: Option, +) -> String { + match bill_coverage_in_percentage_opt { + Some(percentage) => format!("{} %", percentage), + None => "X".to_string(), + } +} + +fn prepare_interpretable_account_resolution( + account_info: AccountInfo, + resulted_affordable_accounts: &mut Vec, +) -> InterpretableAdjustmentResult { + let adjusted_account_idx_opt = resulted_affordable_accounts + .iter() + .position(|account| account.wallet == account_info.wallet); + let bill_coverage_in_percentage_opt = match adjusted_account_idx_opt { + Some(idx) => { + let adjusted_account = resulted_affordable_accounts.remove(idx); + let bill_coverage_in_percentage = u8::try_from( + (adjusted_account.balance_wei * 100) + / account_info.initially_requested_service_fee_minor, + ) + .unwrap(); + Some(bill_coverage_in_percentage) + } + None => None, + }; + InterpretableAdjustmentResult { + initial_balance: account_info.initially_requested_service_fee_minor, + debt_age_s: account_info.debt_age_s, + bill_coverage_in_percentage_opt, + } +} + +fn sort_interpretable_adjustments( + interpretable_adjustments: Vec, +) -> Vec { + let (finished, eliminated): ( + Vec, + Vec, + ) = interpretable_adjustments + .into_iter() + .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); + let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { + Ord::cmp( + &result_b.bill_coverage_in_percentage_opt.unwrap(), + &result_a.bill_coverage_in_percentage_opt.unwrap(), + ) + }); + let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { + Ord::cmp(&result_b.initial_balance, &result_a.initial_balance) + }); + finished_sorted.chain(eliminated_sorted).collect() +} + +fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) { + write_thick_dividing_line(file); + render_scenario_header( + file, + negative_result.common.cw_service_fee_balance_minor, + negative_result.common.required_adjustment, + ); + write_thin_dividing_line(file); + negative_result.account_infos.iter().for_each(|account| { + single_account_liner( + file, + account.initially_requested_service_fee_minor, + account.debt_age_s, + None, + ) + }); + write_thin_dividing_line(file); + write_error(file, negative_result.adjuster_error) +} + +fn write_error(file: &mut File, error: PaymentAdjusterError) { + file.write_fmt(format_args!( + "Scenario resulted in a failure: {:?}\n", + error + )) + .unwrap() +} + +fn resolve_affordable_transaction_count(adjustment: Adjustment) -> String { + match adjustment { + Adjustment::ByServiceFee => "Unlimited".to_string(), + Adjustment::TransactionFeeInPriority { + affordable_transaction_count, + } => affordable_transaction_count.to_string(), + } +} + +fn write_thick_dividing_line(file: &mut File) { + write_ln_made_of(file, '=') +} + +fn write_thin_dividing_line(file: &mut File) { + write_ln_made_of(file, '_') +} + +fn write_ln_made_of(file: &mut File, char: char) { + let _ = file + .write_fmt(format_args!("{}\n", char.to_string().repeat(100))) + .unwrap(); +} + +fn generate_usize_guts(gn: &mut ThreadRng, low: usize, up_to: usize) -> usize { + gn.gen_range(low..up_to) +} + +fn generate_non_zero_usize(gn: &mut ThreadRng, up_to: usize) -> usize { + generate_usize_guts(gn, 1, up_to) +} + +fn generate_boolean(gn: &mut ThreadRng) -> bool { + gn.gen() +} + +// struct ResultTypesCounter { +// oks: usize, +// all_accounts_eliminated: usize, +// other_errors: usize, +// } + +struct CommonScenarioInfo { + cw_service_fee_balance_minor: u128, + required_adjustment: Adjustment, +} + +struct InterpretableAdjustmentResult { + initial_balance: u128, + debt_age_s: u64, + // Account was eliminated from payment if None + bill_coverage_in_percentage_opt: Option, +} + +struct AccountInfo { + wallet: Wallet, + initially_requested_service_fee_minor: u128, + debt_age_s: u64, +} diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 60119819b..a7337db6f 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index a648f1d24..c9b6a2b4e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 267f9ad77..8fa904bdd 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; @@ -105,11 +105,11 @@ pub fn resolve_possibly_outweighed_account( .balance_wei; outweighed.push(current_adjustment_info); - (outweighed, passing_through) } else { passing_through.push(current_adjustment_info); - (outweighed, passing_through) } + + (outweighed, passing_through) } pub fn exhaust_cw_till_the_last_drop( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs index ceaef4c54..bc40584d2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod data_structures; pub mod helper_functions; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c3dee1e82..c55681428 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -5,6 +5,7 @@ mod adjustment_runners; mod criteria_calculators; mod diagnostics; mod inner; +mod loading_test; mod log_fns; mod miscellaneous; mod pre_adjustment_analyzer; @@ -239,6 +240,10 @@ impl PaymentAdjusterReal { already_known_affordable_transaction_count, ); let unallocated_balance = self.inner.unallocated_cw_service_fee_balance_minor(); + let original_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); + if unallocated_balance != original_service_fee_balance { + todo!("this is also weird...should be original balance, not unallocated") + } let is_service_fee_adjustment_needed = match self.analyzer.check_need_of_adjustment_by_service_fee( @@ -309,6 +314,9 @@ impl PaymentAdjusterReal { vec![] } TreatOutweighedAccounts(outweighed) => { + if remaining.is_empty() { + todo!("this is so wierd") + } self.adjust_cw_balance_down_as_result_of_current_iteration(&outweighed); outweighed } @@ -456,6 +464,7 @@ impl PaymentAdjusterReal { multiplication_coefficient: U256, ) -> U256 { let cw_service_fee_balance = U256::from(cw_service_fee_balance); + eprintln!("weights total: {}", weights_total.separate_with_commas()); let weights_total = U256::from(weights_total); cw_service_fee_balance @@ -518,10 +527,10 @@ impl PaymentAdjusterReal { } else { let remaining_undecided_accounts: Vec = convert_collection(properly_adjusted_accounts); - let ready_outweighed: Vec = + let pre_processed_outweighed: Vec = convert_collection(outweighed); Either::Right(AdjustmentIterationResult::SpecialTreatmentRequired { - case: TreatOutweighedAccounts(ready_outweighed), + case: TreatOutweighedAccounts(pre_processed_outweighed), remaining_undecided_accounts, }) } diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs index 1f54c7f94..c2c4a72de 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::log_fns::{ diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index aa50b1649..9ece1b502 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. #![cfg(test)] From 9b80fbc311ba787609c711a99504360176308769 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:35:38 +0700 Subject: [PATCH 129/250] GH-711: basic assertions added --- .../payment_adjuster/diagnostics.rs | 57 ++++++--- .../payment_adjuster/loading_test/mod.rs | 110 +++++++++++++----- .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 16 +-- 4 files changed, 129 insertions(+), 56 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 8f44cd2f9..b1784eb79 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -13,48 +13,71 @@ pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; #[macro_export] macro_rules! diagnostics { - // Display a brief description and values from a collection + // Displays only a description of an event + ($description: literal) => { + diagnostics( + None::String>, + $description, + None::String> + ) + }; + // Displays a brief description and values from a collection ($description: literal, $debuggable_collection: expr) => { collection_diagnostics($description, $debuggable_collection) }; - // Display a brief description and formatted literal with arguments + // Displays a brief description and formatted literal with arguments ($description: literal, $($formatted_values: tt)*) => { - diagnostics(None::String>, $description, || format!($($formatted_values)*)) + diagnostics( + None::String>, + $description, + Some(|| format!($($formatted_values)*)) + ) }; - // Display an account by wallet address, brief description and formatted literal with arguments + // Displays an account by wallet address, brief description and formatted literal with arguments ($wallet_ref: expr, $description: expr, $($formatted_values: tt)*) => { diagnostics( Some(||$wallet_ref.to_string()), $description, - || format!($($formatted_values)*) + Some(|| format!($($formatted_values)*)) ) }; } // Intended to be used through the overloaded macro diagnostics!() for better clearness // and differentiation from the primary functionality -pub fn diagnostics(subject_renderer_opt: Option, description: &str, value_renderer: F2) -where - F1: Fn() -> String, - F2: Fn() -> String, +pub fn diagnostics( + subject_renderer_opt: Option, + description: &str, + value_renderer_opt: Option, +) where + F1: FnOnce() -> String, + F2: FnOnce() -> String, { if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { - let subject = if let Some(subject_renderer) = subject_renderer_opt { - subject_renderer() - } else { - "".to_string() - }; + let subject = no_text_or_by_renderer(subject_renderer_opt); + let values = no_text_or_by_renderer(value_renderer_opt); eprintln!( - "{:(renderer_opt: Option) -> String +where + F: FnOnce() -> String, +{ + if let Some(renderer) = renderer_opt { + renderer() + } else { + "".to_string() + } +} + // Should be used via the macro diagnostics!() for better clearness and differentiation from // the prime functionality pub fn collection_diagnostics( diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 8b3c790d4..2a914c11b 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -27,14 +27,15 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); + let number_of_requested_scenarios = 100; - let scenarios = generate_scenarios(&mut gn, now, 100); + let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let scenario_results = scenarios .into_iter() .map(|prepared_adjustment| { let account_infos = - suck_off_account_infos(&prepared_adjustment.qualified_payables, now); + preserve_account_infos(&prepared_adjustment.qualified_payables, now); let required_adjustment = prepared_adjustment.adjustment.clone(); let cw_service_fee_balance_minor = prepared_adjustment.agent.service_fee_balance_minor(); @@ -50,7 +51,10 @@ fn loading_test_with_randomized_params() { }) .collect(); - render_results(scenario_results) + render_results_to_file_and_attempt_basic_assertions( + scenario_results, + number_of_requested_scenarios, + ) } fn generate_scenarios( @@ -77,20 +81,21 @@ fn make_single_scenario(gn: &mut ThreadRng, now: SystemTime) -> PreparedAdjustme fn make_qualified_payables( gn: &mut ThreadRng, now: SystemTime, - cw_service_fee_balance: u128, + cw_service_fee_balance_minor: u128, ) -> Vec { let accounts_count = generate_non_zero_usize(gn, 20) + 1; - let average_portion = cw_service_fee_balance / accounts_count as u128; + let average_portion_as_base = cw_service_fee_balance_minor / accounts_count as u128; (0..accounts_count) .map(|idx| { let wallet = make_wallet(&format!("wallet{}", idx)); let debt_age = 2000 + generate_non_zero_usize(gn, 200000); - let balance_wei = - average_portion + average_portion / 100 * generate_non_zero_usize(gn, 1000) as u128; + let balance_wei = average_portion_as_base + + ((average_portion_as_base / 100) * generate_non_zero_usize(gn, 1000) as u128); + let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); PayableAccount { wallet, balance_wei, - last_paid_timestamp: from_time_t(to_time_t(now) - debt_age as i64), + last_paid_timestamp, pending_payable_opt: None, } }) @@ -109,8 +114,9 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { let also_by_transaction_fee = generate_boolean(gn); if also_by_transaction_fee && accounts_count > 2 { let can_afford = generate_non_zero_usize(gn, accounts_count); + let affordable_transaction_count = u16::try_from(can_afford).unwrap(); Adjustment::TransactionFeeInPriority { - affordable_transaction_count: u16::try_from(can_afford).unwrap(), + affordable_transaction_count, } } else { Adjustment::ByServiceFee @@ -118,7 +124,7 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { } fn prepare_single_scenario_result( - result: Result, + payment_adjuster_result: Result, account_infos: Vec, required_adjustment: Adjustment, cw_service_fee_balance_minor: u128, @@ -127,7 +133,7 @@ fn prepare_single_scenario_result( cw_service_fee_balance_minor, required_adjustment, }; - let result = match result { + let reinterpreted_result = match payment_adjuster_result { Ok(outbound_payment_instructions) => { let mut adjusted_accounts = outbound_payment_instructions.affordable_accounts; let adjusted_accounts = account_infos @@ -140,7 +146,7 @@ fn prepare_single_scenario_result( sort_interpretable_adjustments(adjusted_accounts); Ok(SuccessfulAdjustment { common, - sorted_interpretable_adjustments, + partially_sorted_interpretable_adjustments: sorted_interpretable_adjustments, }) } Err(adjuster_error) => Err(FailedAdjustment { @@ -150,7 +156,7 @@ fn prepare_single_scenario_result( }), }; - ScenarioResult::new(result) + ScenarioResult::new(reinterpreted_result) } struct ScenarioResult { @@ -165,7 +171,7 @@ impl ScenarioResult { struct SuccessfulAdjustment { common: CommonScenarioInfo, - sorted_interpretable_adjustments: Vec, + partially_sorted_interpretable_adjustments: Vec, } struct FailedAdjustment { @@ -174,7 +180,7 @@ struct FailedAdjustment { adjuster_error: PaymentAdjusterError, } -fn suck_off_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec { +fn preserve_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec { accounts .iter() .map(|account| AccountInfo { @@ -188,18 +194,60 @@ fn suck_off_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec) { +fn render_results_to_file_and_attempt_basic_assertions( + scenario_results: Vec, + number_of_requested_scenarios: usize, +) { let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); - scenario_results + let init = TestOverallOutput::default(); + let overall_output = scenario_results .into_iter() - .for_each(|scenario_result| render_single_scenario(&mut file, scenario_result)) + .fold(init, |acc, scenario_result| { + process_single_scenario(&mut file, acc, scenario_result) + }); + + let total_scenarios_evaluated = overall_output.oks + + overall_output.all_accounts_eliminated + + overall_output.insufficient_service_fee_balance; + assert_eq!( + total_scenarios_evaluated, number_of_requested_scenarios, + "Evaluated scenarios count ({}) != requested scenarios count ({})", + total_scenarios_evaluated, number_of_requested_scenarios + ); + // This assertions depends heavily on the setup for the scenario generator!! + // It rather indicates how well the setting is so that you can adjust it eventually, + // to see more relevant results + let ok_percentage = (overall_output.oks * 100) / total_scenarios_evaluated; + let required_success_rate = 70; + assert!( + ok_percentage >= required_success_rate, + "Not at least {}% from {} of PaymentAdjuster runs finished with success", + required_success_rate, + total_scenarios_evaluated + ); } -fn render_single_scenario(file: &mut File, scenario: ScenarioResult) { +fn process_single_scenario( + file: &mut File, + mut test_overall_output: TestOverallOutput, + scenario: ScenarioResult, +) -> TestOverallOutput { match scenario.result { - Ok(positive) => render_positive_scenario(file, positive), - Err(negative) => render_negative_scenario(file, negative), + Ok(positive) => { + render_positive_scenario(file, positive); + test_overall_output.oks += 1; + test_overall_output + } + Err(negative) => { + match negative.adjuster_error{ + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => {panic!("impossible in this kind of test without the initial check")} + PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { .. } => {test_overall_output.insufficient_service_fee_balance += 1} + PaymentAdjusterError::AllAccountsEliminated => {test_overall_output.all_accounts_eliminated += 1} + } + render_negative_scenario(file, negative); + test_overall_output + } } } @@ -224,9 +272,9 @@ fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { result.common.required_adjustment, ); write_thin_dividing_line(file); - let adjusted_accounts = result.sorted_interpretable_adjustments; + let adjusted_accounts = result.partially_sorted_interpretable_adjustments; adjusted_accounts.into_iter().for_each(|account| { - single_account_liner( + single_account_output( file, account.initial_balance, account.debt_age_s, @@ -238,7 +286,7 @@ fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { const BALANCE_COLUMN_WIDTH: usize = 30; const AGE_COLUMN_WIDTH: usize = 7; -fn single_account_liner( +fn single_account_output( file: &mut File, balance_minor: u128, age_s: u64, @@ -321,7 +369,7 @@ fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) ); write_thin_dividing_line(file); negative_result.account_infos.iter().for_each(|account| { - single_account_liner( + single_account_output( file, account.initially_requested_service_fee_minor, account.debt_age_s, @@ -375,11 +423,13 @@ fn generate_boolean(gn: &mut ThreadRng) -> bool { gn.gen() } -// struct ResultTypesCounter { -// oks: usize, -// all_accounts_eliminated: usize, -// other_errors: usize, -// } +#[derive(Default)] +struct TestOverallOutput { + oks: usize, + // Errors + all_accounts_eliminated: usize, + insufficient_service_fee_balance: usize, +} struct CommonScenarioInfo { cw_service_fee_balance_minor: u128, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8fa904bdd..a89f9222e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -51,7 +51,7 @@ pub fn weights_total(weights_and_accounts: &[WeightedAccount]) -> u128 { }) } -pub fn drop_accounts_that_cannot_be_afforded_due_to_service_fee( +pub fn drop_unaffordable_accounts_due_to_service_fee( weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, ) -> Vec { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c55681428..8bab4879b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -5,10 +5,12 @@ mod adjustment_runners; mod criteria_calculators; mod diagnostics; mod inner; +#[cfg(test)] mod loading_test; mod log_fns; mod miscellaneous; mod pre_adjustment_analyzer; +#[cfg(test)] mod test_utils; use crate::accountant::db_access_objects::payable_dao::PayableAccount; @@ -27,7 +29,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::Require TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_accounts_that_cannot_be_afforded_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, found_zero_affordable_accounts}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, found_zero_affordable_accounts}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -235,21 +237,17 @@ impl PaymentAdjusterReal { PaymentAdjusterError, > { let accounts_with_criteria_affordable_by_transaction_fee = - drop_accounts_that_cannot_be_afforded_due_to_service_fee( + drop_unaffordable_accounts_due_to_service_fee( weighted_accounts_in_descending_order, already_known_affordable_transaction_count, ); - let unallocated_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let original_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); - if unallocated_balance != original_service_fee_balance { - todo!("this is also weird...should be original balance, not unallocated") - } + let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); let is_service_fee_adjustment_needed = match self.analyzer.check_need_of_adjustment_by_service_fee( &self.logger, Either::Right(&accounts_with_criteria_affordable_by_transaction_fee), - unallocated_balance, + cw_service_fee_balance, ) { Ok(answer) => answer, Err(e) => { @@ -260,6 +258,8 @@ impl PaymentAdjusterReal { match is_service_fee_adjustment_needed { true => { + diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); + let adjustment_result_before_verification = self .propose_possible_adjustment_recursively( accounts_with_criteria_affordable_by_transaction_fee, From e1d945b625024bd2128b47b0cb715f45c06a3da1 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:25:24 +0700 Subject: [PATCH 130/250] GH-711: improvements in reality-alikness it the loading test + big finding --- .../balance_criterion_calculator.rs | 13 +- .../payment_adjuster/loading_test/mod.rs | 277 ++++++++++++------ node/src/accountant/payment_adjuster/mod.rs | 10 +- 3 files changed, 205 insertions(+), 95 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index 901697274..f052136a7 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -4,15 +4,15 @@ use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; -use thousands::Separable; // This parameter affects the steepness inversely, but just slowly. // -// Don't worry to change the number; it's not as scientific as it looks, -// I arrived at it after many attempts, true, but only until I became -// aligned with the tuning compared to the values gotten from the Age -// parameter (to reproduce the process you probably need to use -// the rendering tools from the diagnostics module) +// Don't worry to joggle with this number; it's not as scientific as it looks like. +// True, I arrived at it after many attempts when I finally became aligned with +// the tuning, compared to the values the Age criterion calculator get +// (in order to follow my steps you'll need to enable the rendering tools from +// the 'diagnostics' module giving you a close look into the characteristics of +// the formulas and therefore also the effects of your new settings) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; // This parameter affects the steepness analogously, but energetically const BALANCE_FINAL_MULTIPLIER: u128 = 2; @@ -35,7 +35,6 @@ impl BalanceCriterionCalculator { pub fn new() -> Self { let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { let balance_minor = balance_minor_holder.balance_input(); - eprintln!("balance minor: {}", balance_minor.separate_with_commas()); let argument_for_log = Self::calculate_binary_argument(balance_minor); let binary_weight = Self::nonzero_log2(argument_for_log); balance_minor diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 2a914c11b..5ae6317ab 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -4,6 +4,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; @@ -21,17 +22,57 @@ use std::fs::File; use std::io::Write; use std::time::SystemTime; use thousands::Separable; +use web3::types::U256; #[test] fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 100; - + let number_of_requested_scenarios = 500; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); + let test_overall_output_collector = TestOverallOutputCollector::default(); + + struct FirstStageOutput { + test_overall_output_collector: TestOverallOutputCollector, + allowed_scenarios: Vec, + } + + let init = FirstStageOutput { + test_overall_output_collector, + allowed_scenarios: vec![], + }; + let first_stage_output = scenarios + .into_iter() + .fold(init, |mut output_collector, scenario|{ + // We watch only the service fee balance check, transaction fee can be added but it doesn't + // interact with the potential error 'AllAccountsEliminated' whose occurrence rate is interesting + // compared to the number of cases the initial check let the adjustment procedure go on + let initial_check_result = subject.search_for_indispensable_adjustment(&scenario.qualified_payables, &*scenario.agent); + let allowed_scenario_opt = match initial_check_result{ + Ok(adjustment_opt) => {match adjustment_opt{ + None => panic!("Wrong test setup. This test is designed to generate scenarios with balances always insufficient in some way!"), + Some(_) => () + }; + Some(scenario)} + Err(PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated {..}) => { + output_collector.test_overall_output_collector.scenarios_eliminated_before_adjustment_started += 1; + None + } + _e => Some(scenario) + }; + + match allowed_scenario_opt{ + Some(scenario) => output_collector.allowed_scenarios.push(scenario), + None => () + } + + output_collector + }); - let scenario_results = scenarios + let second_stage_scenarios = first_stage_output.allowed_scenarios; + let test_overall_output_collector = first_stage_output.test_overall_output_collector; + let scenario_adjustment_results = second_stage_scenarios .into_iter() .map(|prepared_adjustment| { let account_infos = @@ -52,8 +93,9 @@ fn loading_test_with_randomized_params() { .collect(); render_results_to_file_and_attempt_basic_assertions( - scenario_results, + scenario_adjustment_results, number_of_requested_scenarios, + test_overall_output_collector, ) } @@ -68,43 +110,55 @@ fn generate_scenarios( } fn make_single_scenario(gn: &mut ThreadRng, now: SystemTime) -> PreparedAdjustment { - let cw_service_fee_balance = { - let base = generate_non_zero_usize(gn, usize::MAX) as u128; - base * generate_non_zero_usize(gn, 1000) as u128 - }; - let qualified_payables = make_qualified_payables(gn, now, cw_service_fee_balance); + // let cw_service_fee_balance = { + // let base = generate_non_zero_usize(gn, usize::MAX) as u128; + // base * generate_non_zero_usize(gn, 1000) as u128 + // }; + let (cw_service_fee_balance, qualified_payables) = make_qualified_payables(gn, now); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, qualified_payables.len()); PreparedAdjustment::new(qualified_payables, Box::new(agent), None, adjustment) } -fn make_qualified_payables( - gn: &mut ThreadRng, - now: SystemTime, - cw_service_fee_balance_minor: u128, -) -> Vec { +fn make_qualified_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { let accounts_count = generate_non_zero_usize(gn, 20) + 1; - let average_portion_as_base = cw_service_fee_balance_minor / accounts_count as u128; - (0..accounts_count) + let accounts = (0..accounts_count) .map(|idx| { let wallet = make_wallet(&format!("wallet{}", idx)); let debt_age = 2000 + generate_non_zero_usize(gn, 200000); - let balance_wei = average_portion_as_base - + ((average_portion_as_base / 100) * generate_non_zero_usize(gn, 1000) as u128); + let service_fee_balance_minor = { + let mut generate_u128 = || -> u128 { gn.gen_range(1_000_000_000..2_000_000_000) }; + let parameter_a = generate_u128(); + let parameter_b = generate_u128(); + parameter_a * parameter_b + }; let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); PayableAccount { wallet, - balance_wei, + balance_wei: service_fee_balance_minor, last_paid_timestamp, pending_payable_opt: None, } }) - .collect() + .collect::>(); + let balance_average = { + let sum: u128 = sum_as(&accounts, |account| account.balance_wei); + sum / accounts_count as u128 + }; + let cw_service_fee_balance_minor = + balance_average * (generate_usize(gn, accounts_count - 1) as u128 + 1); + (cw_service_fee_balance_minor, accounts) } fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { BlockchainAgentMock::default() - // For scenario evaluation + // We don't care about this check in this test + .transaction_fee_balance_minor_result(U256::from(u128::MAX)) + // ...as well as we don't here + .estimated_transaction_fee_per_transaction_minor_result(1) + // Used in the entry check + .service_fee_balance_minor_result(cw_service_fee_balance) + // For evaluation preparations in the test .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) @@ -113,8 +167,8 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { let also_by_transaction_fee = generate_boolean(gn); if also_by_transaction_fee && accounts_count > 2 { - let can_afford = generate_non_zero_usize(gn, accounts_count); - let affordable_transaction_count = u16::try_from(can_afford).unwrap(); + let affordable_transaction_count = + u16::try_from(generate_non_zero_usize(gn, accounts_count)).unwrap(); Adjustment::TransactionFeeInPriority { affordable_transaction_count, } @@ -197,42 +251,88 @@ fn preserve_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec, number_of_requested_scenarios: usize, + test_overall_output_collector: TestOverallOutputCollector, ) { let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); - let init = TestOverallOutput::default(); - let overall_output = scenario_results + introduction(&mut file); + let test_overall_output_collector = scenario_results .into_iter() - .fold(init, |acc, scenario_result| { + .fold(test_overall_output_collector, |acc, scenario_result| { process_single_scenario(&mut file, acc, scenario_result) }); + let total_scenarios_evaluated = test_overall_output_collector + .scenarios_eliminated_before_adjustment_started + + test_overall_output_collector.oks + + test_overall_output_collector.all_accounts_eliminated + + test_overall_output_collector.insufficient_service_fee_balance; + write_in_test_overall_output_to_file( + &mut file, + &test_overall_output_collector, + total_scenarios_evaluated, + ); - let total_scenarios_evaluated = overall_output.oks - + overall_output.all_accounts_eliminated - + overall_output.insufficient_service_fee_balance; assert_eq!( total_scenarios_evaluated, number_of_requested_scenarios, "Evaluated scenarios count ({}) != requested scenarios count ({})", total_scenarios_evaluated, number_of_requested_scenarios ); - // This assertions depends heavily on the setup for the scenario generator!! + // The next assertions depend heavily on the setup for the scenario generator!! // It rather indicates how well the setting is so that you can adjust it eventually, // to see more relevant results - let ok_percentage = (overall_output.oks * 100) / total_scenarios_evaluated; + let entry_check_pass_rate = 100 + - ((test_overall_output_collector.scenarios_eliminated_before_adjustment_started * 100) + / total_scenarios_evaluated); + let required_pass_rate = 80; + assert!(entry_check_pass_rate >= required_pass_rate, "Not at least {}% from {} the scenarios generated for this test allows PaymentAdjuster to continue doing its job and ends too early. Instead only {}%. Setup of the test might be needed",required_pass_rate, total_scenarios_evaluated, entry_check_pass_rate); + let ok_adjustment_percentage = (test_overall_output_collector.oks * 100) + / (total_scenarios_evaluated + - test_overall_output_collector.scenarios_eliminated_before_adjustment_started); let required_success_rate = 70; assert!( - ok_percentage >= required_success_rate, - "Not at least {}% from {} of PaymentAdjuster runs finished with success", + ok_adjustment_percentage >= required_success_rate, + "Not at least {}% from {} adjustment procedures from PaymentAdjuster runs finished with success, only {}%", required_success_rate, - total_scenarios_evaluated + total_scenarios_evaluated, + ok_adjustment_percentage ); } +fn introduction(file: &mut File) { + write_thick_dividing_line(file); + write_thick_dividing_line(file); + file.write(b"For a brief overview of this formatted test output look at the end\n") + .unwrap(); + write_thick_dividing_line(file); + write_thick_dividing_line(file) +} + +fn write_in_test_overall_output_to_file( + file: &mut File, + test_overall_output_collector: &TestOverallOutputCollector, + total_of_scenarios_eliminated: usize, +) { + write_thick_dividing_line(file); + file.write_fmt(format_args!( + "Total scenarios generated: {}\n\ + Scenarios caught by the entry check: {}\n\ + Ok scenarios: {}\n\ + Scenarios with 'AllAccountsEliminated': {}\n\ + Scenarios with late insufficient balance errors: {}", + total_of_scenarios_eliminated, + test_overall_output_collector.scenarios_eliminated_before_adjustment_started, + test_overall_output_collector.oks, + test_overall_output_collector.all_accounts_eliminated, + test_overall_output_collector.insufficient_service_fee_balance + )) + .unwrap() +} + fn process_single_scenario( file: &mut File, - mut test_overall_output: TestOverallOutput, + mut test_overall_output: TestOverallOutputCollector, scenario: ScenarioResult, -) -> TestOverallOutput { +) -> TestOverallOutputCollector { match scenario.result { Ok(positive) => { render_positive_scenario(file, positive); @@ -313,53 +413,6 @@ fn resolve_account_ending_status_graphically( } } -fn prepare_interpretable_account_resolution( - account_info: AccountInfo, - resulted_affordable_accounts: &mut Vec, -) -> InterpretableAdjustmentResult { - let adjusted_account_idx_opt = resulted_affordable_accounts - .iter() - .position(|account| account.wallet == account_info.wallet); - let bill_coverage_in_percentage_opt = match adjusted_account_idx_opt { - Some(idx) => { - let adjusted_account = resulted_affordable_accounts.remove(idx); - let bill_coverage_in_percentage = u8::try_from( - (adjusted_account.balance_wei * 100) - / account_info.initially_requested_service_fee_minor, - ) - .unwrap(); - Some(bill_coverage_in_percentage) - } - None => None, - }; - InterpretableAdjustmentResult { - initial_balance: account_info.initially_requested_service_fee_minor, - debt_age_s: account_info.debt_age_s, - bill_coverage_in_percentage_opt, - } -} - -fn sort_interpretable_adjustments( - interpretable_adjustments: Vec, -) -> Vec { - let (finished, eliminated): ( - Vec, - Vec, - ) = interpretable_adjustments - .into_iter() - .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); - let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp( - &result_b.bill_coverage_in_percentage_opt.unwrap(), - &result_a.bill_coverage_in_percentage_opt.unwrap(), - ) - }); - let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp(&result_b.initial_balance, &result_a.initial_balance) - }); - finished_sorted.chain(eliminated_sorted).collect() -} - fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) { write_thick_dividing_line(file); render_scenario_header( @@ -411,6 +464,53 @@ fn write_ln_made_of(file: &mut File, char: char) { .unwrap(); } +fn prepare_interpretable_account_resolution( + account_info: AccountInfo, + resulted_affordable_accounts: &mut Vec, +) -> InterpretableAdjustmentResult { + let adjusted_account_idx_opt = resulted_affordable_accounts + .iter() + .position(|account| account.wallet == account_info.wallet); + let bill_coverage_in_percentage_opt = match adjusted_account_idx_opt { + Some(idx) => { + let adjusted_account = resulted_affordable_accounts.remove(idx); + let bill_coverage_in_percentage = u8::try_from( + (adjusted_account.balance_wei * 100) + / account_info.initially_requested_service_fee_minor, + ) + .unwrap(); + Some(bill_coverage_in_percentage) + } + None => None, + }; + InterpretableAdjustmentResult { + initial_balance: account_info.initially_requested_service_fee_minor, + debt_age_s: account_info.debt_age_s, + bill_coverage_in_percentage_opt, + } +} + +fn sort_interpretable_adjustments( + interpretable_adjustments: Vec, +) -> Vec { + let (finished, eliminated): ( + Vec, + Vec, + ) = interpretable_adjustments + .into_iter() + .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); + let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { + Ord::cmp( + &result_b.bill_coverage_in_percentage_opt.unwrap(), + &result_a.bill_coverage_in_percentage_opt.unwrap(), + ) + }); + let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { + Ord::cmp(&result_b.initial_balance, &result_a.initial_balance) + }); + finished_sorted.chain(eliminated_sorted).collect() +} + fn generate_usize_guts(gn: &mut ThreadRng, low: usize, up_to: usize) -> usize { gn.gen_range(low..up_to) } @@ -419,12 +519,21 @@ fn generate_non_zero_usize(gn: &mut ThreadRng, up_to: usize) -> usize { generate_usize_guts(gn, 1, up_to) } +fn generate_usize(gn: &mut ThreadRng, up_to: usize) -> usize { + generate_usize_guts(gn, 0, up_to) +} + fn generate_boolean(gn: &mut ThreadRng) -> bool { gn.gen() } #[derive(Default)] -struct TestOverallOutput { +struct TestOverallOutputCollector { + // First stage: entry check + // ____________________________________ + scenarios_eliminated_before_adjustment_started: usize, + // Second stage: proper adjustment + // ____________________________________ oks: usize, // Errors all_accounts_eliminated: usize, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8bab4879b..b03cb4e1b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -315,14 +315,17 @@ impl PaymentAdjusterReal { } TreatOutweighedAccounts(outweighed) => { if remaining.is_empty() { + //TODO explain in a comment + // now I know that: if an account is disqualified it can happen that + // the cw balance become sufficient for the rest of accounts, proceeding to another + // iteration, this results in all accounts classified as outweighed!! todo!("this is so wierd") } - self.adjust_cw_balance_down_as_result_of_current_iteration(&outweighed); + self.adjust_remaining_uallocated_cw_balance_down(&outweighed); outweighed } }; - // Note: There always must be at least one remaining account besides an outweighed one, by definition let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( remaining, @@ -464,7 +467,6 @@ impl PaymentAdjusterReal { multiplication_coefficient: U256, ) -> U256 { let cw_service_fee_balance = U256::from(cw_service_fee_balance); - eprintln!("weights total: {}", weights_total.separate_with_commas()); let weights_total = U256::from(weights_total); cw_service_fee_balance @@ -536,7 +538,7 @@ impl PaymentAdjusterReal { } } - fn adjust_cw_balance_down_as_result_of_current_iteration( + fn adjust_remaining_uallocated_cw_balance_down( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { From 97da5b9ed9bae124a09d7730780c1b6af803db69 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:50:02 +0700 Subject: [PATCH 131/250] GH-711: diagnostics readibility --- .../accountant/payment_adjuster/diagnostics.rs | 18 ++++++++++-------- node/src/accountant/payment_adjuster/mod.rs | 7 +++++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index b1784eb79..235b215d1 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; -pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 60; +pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 58; #[macro_export] macro_rules! diagnostics { @@ -54,15 +54,17 @@ pub fn diagnostics( F2: FnOnce() -> String, { if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { + let subject_column_length = if subject_renderer_opt.is_some() { + WALLET_ADDRESS_LENGTH + 2 + } else { + 0 + }; let subject = no_text_or_by_renderer(subject_renderer_opt); let values = no_text_or_by_renderer(value_renderer_opt); + let description_length = DIAGNOSTICS_MIDDLE_COLUMN_WIDTH; eprintln!( - "{:, { diagnostics!( - "\nUNRESOLVED QUALIFIED ACCOUNTS:", + "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", &unresolved_qualified_accounts ); @@ -285,7 +285,10 @@ impl PaymentAdjusterReal { let merged = recursion_results.merge_results_from_recursion(); - diagnostics!("\nFINAL ADJUSTED ACCOUNTS:", &merged); + diagnostics!( + "\nFINAL SET OF ADJUSTED ACCOUNTS IN CURRENT ITERATION:", + &merged + ); Ok(merged) } From 7dc45695de7a9a24d92e376583dee141d2bd0db4 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:15:40 +0700 Subject: [PATCH 132/250] GH-711: error name and message fixed --- .../payment_adjuster/diagnostics.rs | 2 +- .../payment_adjuster/loading_test/mod.rs | 16 ++++-- node/src/accountant/payment_adjuster/mod.rs | 50 +++++++++---------- .../pre_adjustment_analyzer.rs | 4 +- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 235b215d1..d066ad855 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 58; diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 5ae6317ab..774e447c3 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -55,7 +55,7 @@ fn loading_test_with_randomized_params() { Some(_) => () }; Some(scenario)} - Err(PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated {..}) => { + Err(PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction {..}) => { output_collector.test_overall_output_collector.scenarios_eliminated_before_adjustment_started += 1; None } @@ -340,10 +340,16 @@ fn process_single_scenario( test_overall_output } Err(negative) => { - match negative.adjuster_error{ - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => {panic!("impossible in this kind of test without the initial check")} - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { .. } => {test_overall_output.insufficient_service_fee_balance += 1} - PaymentAdjusterError::AllAccountsEliminated => {test_overall_output.all_accounts_eliminated += 1} + match negative.adjuster_error { + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => { + panic!("impossible in this kind of test without the initial check") + } + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + .. + } => test_overall_output.insufficient_service_fee_balance += 1, + PaymentAdjusterError::AllAccountsEliminated => { + test_overall_output.all_accounts_eliminated += 1 + } } render_negative_scenario(file, negative); test_overall_output diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index fb344f656..e49e416b7 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -597,7 +597,7 @@ pub enum PaymentAdjusterError { per_transaction_requirement_minor: u128, cw_transaction_fee_balance_minor: U256, }, - RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: usize, total_amount_demanded_minor: u128, cw_service_fee_balance_minor: u128, @@ -621,23 +621,23 @@ impl Display for PaymentAdjusterError { per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas() ), - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts, total_amount_demanded_minor, cw_service_fee_balance_minor, } => write!( f, - "Analysis projected a possibility for an adjustment leaving each of the transactions \ - with an uneconomical portion of money compared to the whole bills. Please proceed \ - by sending funds to your consuming wallet. Number of canceled payments: {}. \ - Total amount demanded: {} wei. Consuming wallet balance: {} wei", + "Found a smaller service fee balance than it does for a single payment. \ + Number of canceled payments: {}. Total amount demanded: {} wei. Consuming \ + wallet balance: {} wei", number_of_accounts.separate_with_commas(), total_amount_demanded_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() ), PaymentAdjusterError::AllAccountsEliminated => write!( f, - "The adjustment algorithm had to eliminate each payable given the resources." + "The adjustment algorithm had to eliminate each payable from payments \ + due to luck of resources." ), } } @@ -832,7 +832,7 @@ mod tests { assert_eq!( result, Err( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, total_amount_demanded_minor: 920_000_000_000, cw_service_fee_balance_minor @@ -877,30 +877,30 @@ mod tests { fn payment_adjuster_error_implements_display() { vec![ ( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { - number_of_accounts: 5, + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: 5, total_amount_demanded_minor: 6_000_000_000, cw_service_fee_balance_minor: 333_000_000, - }, - "Analysis projected a possibility for an adjustment leaving each of \ - the transactions with an uneconomical portion of money compared to the whole bills. \ - Please proceed by sending funds to your consuming wallet. Number of canceled \ - payments: 5. Total amount demanded: 6,000,000,000 wei. Consuming wallet balance: \ - 333,000,000 wei", + }, + "Found a smaller service fee balance than it does for a single payment. \ + Number of canceled payments: 5. Total amount demanded: 6,000,000,000 wei. \ + Consuming wallet balance: 333,000,000 wei", ), ( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: 4, - per_transaction_requirement_minor: 70_000_000_000_000, - cw_transaction_fee_balance_minor: U256::from(90_000), - }, - "Found a smaller transaction fee balance than it does for a single payment. \ - Number of canceled payments: 4. Transaction fee by single account: \ - 70,000,000,000,000 wei. Consuming wallet balance: 90,000 wei", + number_of_accounts: 4, + per_transaction_requirement_minor: 70_000_000_000_000, + cw_transaction_fee_balance_minor: U256::from(90_000), + }, + "Found a smaller transaction fee balance than it does for a single \ + payment. Number of canceled payments: 4. Transaction fee by single \ + account: 70,000,000,000,000 wei. Consuming wallet balance: 90,000 \ + wei", ), ( PaymentAdjusterError::AllAccountsEliminated, - "The adjustment algorithm had to eliminate each payable given the resources.", + "The adjustment algorithm had to eliminate each payable from payments \ + due to luck of resources.", ), ] .into_iter() @@ -1797,7 +1797,7 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 2, total_amount_demanded_minor: 333_000_000_000_000 + 222_000_000_000_000, cw_service_fee_balance_minor: service_fee_balance_in_minor_units diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs index c2c4a72de..5ee3b6402 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -148,7 +148,7 @@ impl TransactionFeeAdjustmentPossibilityVerifier { } else { let total_amount_demanded_minor = sum_as(accounts, |account| account.balance_wei); Err( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: accounts.len(), total_amount_demanded_minor, cw_service_fee_balance_minor, @@ -274,7 +274,7 @@ mod tests { assert_eq!( result, Err( - PaymentAdjusterError::RiskOfWastefulAdjustmentWithAllAccountsEventuallyEliminated { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, cw_service_fee_balance_minor: cw_service_fee_balance From 3dc12bda979798479c84f0c172c245ad1e3436e2 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:29:05 +0700 Subject: [PATCH 133/250] GH-711: little things --- node/src/accountant/mod.rs | 25 +++++++++---------- .../payment_adjuster/loading_test/mod.rs | 17 +++++++++---- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 2174763f4..2090be248 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1663,15 +1663,15 @@ mod tests { .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ - If matured payables stay untreated long, your creditors may impose a ban on you" + If mature payables stay untreated long, your creditors may impose ban on you" )); } #[test] - fn payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment() { + fn payment_adjuster_throws_out_an_error_meaning_entry_check_passed_but_adjustment_went_wrong() { init_test_logging(); let test_name = - "payment_adjuster_throws_out_an_error_it_became_dirty_from_the_job_on_the_adjustment"; + "payment_adjuster_throws_out_an_error_meaning_entry_check_passed_but_adjustment_went_wrong"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); @@ -1680,23 +1680,22 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Payment adjustment has not \ - produced any executable payments. Please add funds into your consuming wallet in order to avoid bans \ - from your creditors. Failure reason: The adjustment algorithm had to eliminate each payable given the resources" + "WARN: {test_name}: Payment adjustment has not produced any executable payments. Please add funds \ + into your consuming wallet in order to avoid bans from your creditors. Failure reason: The adjustment \ + algorithm had to eliminate each payable due to lack of resources" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. \ - If matured payables stay untreated long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If mature payables stay untreated long, your \ + creditors may impose ban on you" )); } #[test] - fn error_from_payment_adjuster_is_not_sent_by_an_exclusive_message_to_ui_if_not_manually_requested( - ) { + fn payment_adjuster_error_is_not_sent_by_exclusive_msg_to_ui_if_scan_not_manually_requested() { init_test_logging(); - let test_name = "error_from_payment_adjuster_is_not_sent_by_an_exclusive_message_to_ui_if_not_manually_requested"; + let test_name = "payment_adjuster_error_is_not_sent_by_exclusive_msg_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( @@ -1725,8 +1724,8 @@ mod tests { // Test didn't blow up while the subject was unbound to other actors // therefore we didn't attempt to send the NodeUiMessage TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. \ - If matured payables stay untreated long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated long, \ + your creditors may impose a ban on you" )); } diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 774e447c3..a728f0d61 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -1,6 +1,6 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -#![cfg(test)] +#![cfg(feature = "occasional_test")] use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; @@ -26,6 +26,17 @@ use web3::types::U256; #[test] fn loading_test_with_randomized_params() { + // This test needs to be understood as a generator of extensive amount of scenarios that + // the PaymentAdjuster might come to be asked to resolve while there are quite many combinations + // that a human has a hard time with to imagine, now we ought to think that some of them might + // be corner cases that there wasn't awareness of when it was being designed. Therefore, the main + // purpose of this test is to prove that out of a huge number of tries the PaymentAdjuster always + // comes along fairly well, especially, that it cannot kill the Node by an accidental panic or + // that it can live up to its original purpose and vast majority of the attempted adjustments + // end up with reasonable results. That said, a smaller amount of these attempts are expected + // to be vain because of some chance always be there that with a given combination of payables + // the algorithm will go step by step eliminating completely all accounts. There's hardly + // a way for the adjustment procedure as it proceeds now to anticipate if this is going to happen. let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); @@ -110,10 +121,6 @@ fn generate_scenarios( } fn make_single_scenario(gn: &mut ThreadRng, now: SystemTime) -> PreparedAdjustment { - // let cw_service_fee_balance = { - // let base = generate_non_zero_usize(gn, usize::MAX) as u128; - // base * generate_non_zero_usize(gn, 1000) as u128 - // }; let (cw_service_fee_balance, qualified_payables) = make_qualified_payables(gn, now); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, qualified_payables.len()); From 4163f68dd8546bb5a7224045d5258ea2d0d41816 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:08:46 +0700 Subject: [PATCH 134/250] GH-711: new test added but I'd have to chenge constants for age calculation so before I mess with so many other tests I want a backup --- .../payment_adjuster/adjustment_runners.rs | 15 +-- .../age_criterion_calculator.rs | 13 +-- .../payment_adjuster/diagnostics.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 16 +-- node/src/accountant/payment_adjuster/mod.rs | 99 +++++++++++++++---- 5 files changed, 99 insertions(+), 48 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 439fe60e9..17e08e57b 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -74,7 +74,7 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { Ok(Either::Left( payment_adjuster - .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order)?, + .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order), )) } } @@ -82,17 +82,14 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { pub struct ServiceFeeOnlyAdjustmentRunner {} impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { - type ReturnType = Result, PaymentAdjusterError>; + type ReturnType = Vec; fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, last_account: PayableAccount, ) -> Self::ReturnType { - Ok(empty_or_single_element_vector(adjust_last_one_opt( - payment_adjuster, - last_account, - ))) + empty_or_single_element_vector(adjust_last_one_opt(payment_adjuster, last_account)) } fn adjust_multiple( @@ -209,7 +206,7 @@ mod tests { #[test] fn service_fee_only_adjust_last_one_works() { test_adjust_last_one(ServiceFeeOnlyAdjustmentRunner {}, |expected_vec| { - Ok(expected_vec) + expected_vec }) } @@ -293,9 +290,7 @@ mod tests { let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(accounts); - let result = subject - .adjust_multiple(&mut payment_adjuster, criteria_and_accounts) - .unwrap(); + let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); let returned_accounts = result .into_iter() diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 1c78aeeb4..db2356a91 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -42,7 +42,7 @@ impl AgeCriterionCalculator { let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) - .checked_pow(AGE_MAIN_EXPONENT) + .checked_pow(AGE_EXPONENT_FOR_BASE) .expect("pow overflow") .checked_div(divisor) .expect("div overflow") @@ -105,11 +105,7 @@ impl AgeCriterionCalculator { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ - AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, - AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, - AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_MAIN_EXPONENT, - }; + use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_EXPONENT_FOR_BASE, AGE_MULTIPLIER_FOR_BASE}; use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; @@ -118,7 +114,8 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(AGE_MAIN_EXPONENT, 3); + assert_eq!(AGE_MULTIPLIER_FOR_BASE, 2); + assert_eq!(AGE_EXPONENT_FOR_BASE, 3); assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); @@ -213,7 +210,7 @@ mod tests { let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) - .checked_pow(AGE_MAIN_EXPONENT) + .checked_pow(AGE_EXPONENT_FOR_BASE) .unwrap() .checked_div(divisor) .unwrap() diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index d066ad855..a8ad10a7f 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_chara use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 58; @@ -241,7 +241,7 @@ pub mod formulas_progressive_characteristics { // For the purposes of debugging and tuning the formulas up to work well together. It lets you // imagine the curve of a criterion in dependence to different supplied input values for // the give parameter - pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = false; + pub const COMPUTE_FORMULAS_CHARACTERISTICS: bool = true; // You must preserve the 'static' keyword // diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index b4f0ec7ba..3d63c8729 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -7,7 +7,7 @@ pub trait PaymentAdjusterInner { fn transaction_fee_count_limit_opt(&self) -> Option; fn original_cw_service_fee_balance_minor(&self) -> u128; fn unallocated_cw_service_fee_balance_minor(&self) -> u128; - fn update_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128); + fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128); } pub struct PaymentAdjusterInnerReal { @@ -45,11 +45,11 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { self.unallocated_cw_service_fee_balance_minor } - fn update_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128) { + fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128) { let updated_thought_cw_balance = self .unallocated_cw_service_fee_balance_minor .checked_sub(subtrahend) - .expect("got into negative numbers"); + .expect("should never go beyond zero"); self.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance } } @@ -78,9 +78,9 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull { fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_service_fee_balance_minor()") } - fn update_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128) { + fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128) { PaymentAdjusterInnerNull::panicking_operation( - "update_unallocated_cw_service_fee_balance_minor()", + "subtract_from_unallocated_cw_service_fee_balance_minor()", ) } } @@ -160,11 +160,11 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the update_unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + expected = "Broken code: Called the null implementation of the subtract_from_unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] - fn inner_null_calling_update_unallocated_cw_service_fee_balance_minor() { + fn inner_null_calling_subtract_from_unallocated_cw_service_fee_balance_minor() { let mut subject = PaymentAdjusterInnerNull {}; - let _ = subject.update_unallocated_cw_service_fee_balance_minor(123); + let _ = subject.subtract_from_unallocated_cw_service_fee_balance_minor(123); } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e49e416b7..191f39429 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -211,6 +211,7 @@ impl PaymentAdjusterReal { "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", &unresolved_qualified_accounts ); + eprintln!("Unallocated balance for this iteration: {}", self.inner.unallocated_cw_service_fee_balance_minor().separate_with_commas()); if unresolved_qualified_accounts.len() == 1 { let last_one = unresolved_qualified_accounts @@ -264,7 +265,7 @@ impl PaymentAdjusterReal { .propose_possible_adjustment_recursively( accounts_with_criteria_affordable_by_transaction_fee, ); - Ok(Either::Left(adjustment_result_before_verification?)) + Ok(Either::Left(adjustment_result_before_verification)) } false => { let finalized_accounts = isolate_accounts_from_weights( @@ -278,10 +279,10 @@ impl PaymentAdjusterReal { fn propose_possible_adjustment_recursively( &mut self, weighed_accounts: Vec, - ) -> Result, PaymentAdjusterError> { + ) -> Vec { let current_iteration_result = self.perform_adjustment_by_service_fee(weighed_accounts); - let recursion_results = self.resolve_current_iteration_result(current_iteration_result)?; + let recursion_results = self.resolve_current_iteration_result(current_iteration_result); let merged = recursion_results.merge_results_from_recursion(); @@ -290,55 +291,52 @@ impl PaymentAdjusterReal { &merged ); - Ok(merged) + merged } fn resolve_current_iteration_result( &mut self, adjustment_iteration_result: AdjustmentIterationResult, - ) -> Result { + ) -> RecursionResults { match adjustment_iteration_result { AdjustmentIterationResult::AllAccountsProcessed(decided_accounts) => { - Ok(RecursionResults::new(decided_accounts, vec![])) + RecursionResults::new(decided_accounts, vec![]) } AdjustmentIterationResult::SpecialTreatmentRequired { case: special_case, - remaining_undecided_accounts: remaining, + remaining_undecided_accounts, } => { let here_decided_accounts = match special_case { TreatInsignificantAccount => { - if remaining.is_empty() { + if remaining_undecided_accounts.is_empty() { // a) only one account can be eliminated in a single iteration, - // b) if there is one last undecided account, it goes on into - // a shortcut section, never coming here + // b) if there is one last undecided account, it goes on through + // a shortcut, not reaching out here unreachable!("Not possible by original design") } vec![] } TreatOutweighedAccounts(outweighed) => { - if remaining.is_empty() { + if remaining_undecided_accounts.is_empty() { //TODO explain in a comment // now I know that: if an account is disqualified it can happen that // the cw balance become sufficient for the rest of accounts, proceeding to another // iteration, this results in all accounts classified as outweighed!! todo!("this is so wierd") } - self.adjust_remaining_uallocated_cw_balance_down(&outweighed); + self.adjust_remaining_unallocated_cw_balance_down(&outweighed); outweighed } }; let down_stream_decided_accounts = self .calculate_criteria_and_propose_adjustments_recursively( - remaining, + remaining_undecided_accounts, ServiceFeeOnlyAdjustmentRunner {}, - )?; + ); - Ok(RecursionResults::new( - here_decided_accounts, - down_stream_decided_accounts, - )) + RecursionResults::new(here_decided_accounts, down_stream_decided_accounts) } } } @@ -541,7 +539,7 @@ impl PaymentAdjusterReal { } } - fn adjust_remaining_uallocated_cw_balance_down( + fn adjust_remaining_unallocated_cw_balance_down( &mut self, processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { @@ -549,7 +547,7 @@ impl PaymentAdjusterReal { account.proposed_adjusted_balance }); self.inner - .update_unallocated_cw_service_fee_balance_minor(subtrahend_total); + .subtract_from_unallocated_cw_service_fee_balance_minor(subtrahend_total); diagnostics!( "LOWERED CW BALANCE", @@ -1143,6 +1141,67 @@ mod tests { let _ = subject.resolve_current_iteration_result(iteration_result); } + #[test] + fn disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them(){ + init_test_logging(); + let test_name = "disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them"; + let now = SystemTime::now(); + let balance_1 = 80_000_000_000_000_000_000; + let account_1 = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: balance_1, + last_paid_timestamp: now.checked_sub(Duration::from_secs(24_000)).unwrap(), + pending_payable_opt: None, + }; + let balance_2 = 60_000_000_000_000_000_000; + let account_2 = PayableAccount { + wallet: make_wallet("def"), + balance_wei: balance_2, + last_paid_timestamp: now.checked_sub(Duration::from_secs(200_000)).unwrap(), + pending_payable_opt: None, + }; + let balance_3 = 40_000_000_000_000_000_000; + let account_3 = PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(160_000)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let mut subject = PaymentAdjusterReal::new(); + subject.logger = Logger::new(test_name); + let agent_id_stamp = ArbitraryIdStamp::new(); + let accounts_sum: u128 = balance_1 + balance_2 + balance_3; + let service_fee_balance_in_minor_units = accounts_sum - ((balance_1*90)/100); + let agent = { + let mock = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); + Box::new(mock) + }; + let adjustment_setup = PreparedAdjustment { + qualified_payables, + agent, + adjustment: Adjustment::ByServiceFee, + response_skeleton_opt: None, + }; + + let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + + let expected_affordable_accounts = { + vec![account_2, account_3] + }; + assert_eq!( + result.affordable_accounts, + expected_affordable_accounts + ); + assert_eq!(result.response_skeleton_opt, None); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + let log_msg = format!( + "DEBUG: {test_name}: " + ); + } + #[test] fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { init_test_logging(); From 28d0a7e86572ed8004f4c439253955fe65ed0eae Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:15:16 +0700 Subject: [PATCH 135/250] GH-711: before having to fix tests because I changed some parameters in the formulas --- .../age_criterion_calculator.rs | 57 ++++++++++++++----- .../balance_criterion_calculator.rs | 24 +++++--- node/src/accountant/payment_adjuster/mod.rs | 25 ++++---- .../accountant/payment_adjuster/test_utils.rs | 29 ++++++++++ ...r_diagnostics_of_age_criterion_formula.txt | 16 ++++++ 5 files changed, 117 insertions(+), 34 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index db2356a91..78232f4f0 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -7,12 +7,16 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1 use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::time::SystemTime; -const AGE_MAIN_EXPONENT: u32 = 3; +// Base is the main body of the growing value +const AGE_EXPONENT_FOR_BASE: u32 = 3; + +// Descending multiplier is the parameter that slows down the growth of the base. We start with a huge multiplier that +// diminishes as the age of the debt stretches const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 1_000; -const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 10; -const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 3; +const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 550; +const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 13; +const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 4; pub struct AgeCriterionCalculator { formula: Box u128>, @@ -37,8 +41,14 @@ impl AgeCriterionCalculator { let last_paid_timestamp = last_paid_timestamp_in_holder.age_input(); let elapsed_secs: u64 = Self::nonzero_elapsed(now, last_paid_timestamp); + // This argument slows down the growth of the power of 3 in the base value, but unlike to the descending + // multiplier below, this happens consistently let divisor = Self::nonzero_compute_divisor(elapsed_secs); + // This argument impacts the curve so that it progresses slower the further in time we get + // (The idea is: it's not already such a difference if it is weeks or months. Put differently, while + // the age of the debt can overrule easily smaller balances, because it progresses more aggressively + // than metrics for the debt size, in contrary, we don't want it to shade the large ones) let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) @@ -105,22 +115,43 @@ impl AgeCriterionCalculator { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_EXPONENT_FOR_BASE, AGE_MULTIPLIER_FOR_BASE}; + use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ + AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, + AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, + AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_EXPONENT_FOR_BASE, + }; use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; - use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::accountant::payment_adjuster::test_utils::{ + assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes, + make_initialized_subject, + }; use std::time::{Duration, SystemTime}; #[test] fn constants_are_correct() { - assert_eq!(AGE_MULTIPLIER_FOR_BASE, 2); - assert_eq!(AGE_EXPONENT_FOR_BASE, 3); - assert_eq!(AGE_DESC_MULTIPLIER_ARG_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, 2); - assert_eq!(AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, 1_000); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, 10); - assert_eq!(AGE_DESC_MULTIPLIER_DIVISOR_EXP, 3); + let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ + (AGE_EXPONENT_FOR_BASE.try_into().unwrap(), 3), + (AGE_DESC_MULTIPLIER_ARG_EXP.try_into().unwrap(), 2), + (AGE_DESC_MULTIPLIER_LOG_STRESS_EXP.try_into().unwrap(), 2), + ( + AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER + .try_into() + .unwrap(), + 550, + ), + ( + AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER.try_into().unwrap(), + 13, + ), + (AGE_DESC_MULTIPLIER_DIVISOR_EXP.try_into().unwrap(), 4), + ]; + + assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( + &constants_and_their_expected_values, + 574, + ) } #[test] diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs index f052136a7..8d3e7eafb 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs @@ -7,12 +7,12 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; // This parameter affects the steepness inversely, but just slowly. // -// Don't worry to joggle with this number; it's not as scientific as it looks like. -// True, I arrived at it after many attempts when I finally became aligned with -// the tuning, compared to the values the Age criterion calculator get -// (in order to follow my steps you'll need to enable the rendering tools from -// the 'diagnostics' module giving you a close look into the characteristics of -// the formulas and therefore also the effects of your new settings) +// Don't worry to joggle with this number; it's not as scientific as it looks like. True, I arrived at it after many +// attempts when I finally aligned myself with the tuning. The issue is it needs to be carefully compared to the values +// the Age criterion calculator yields. +// (If you are preparing to follow my steps you'll need to enable the rendering from the 'diagnostics' module, getting +// back a chance for a close look into each characteristics of the calculators formulas and therefore also how sync +// they are. See and think about the effects of your new settings) const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; // This parameter affects the steepness analogously, but energetically const BALANCE_FINAL_MULTIPLIER: u128 = 2; @@ -67,11 +67,19 @@ mod tests { use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; + use crate::accountant::payment_adjuster::test_utils::assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes; #[test] fn constants_are_correct() { - assert_eq!(BALANCE_LOG_2_ARG_DIVISOR, 18_490_000); - assert_eq!(BALANCE_FINAL_MULTIPLIER, 2) + let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ + (BALANCE_LOG_2_ARG_DIVISOR.try_into().unwrap(), 18_490_000), + (BALANCE_FINAL_MULTIPLIER.try_into().unwrap(), 2), + ]; + + assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( + &constants_and_their_expected_values, + 18490002, + ) } #[test] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 191f39429..c5a5446b3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -211,7 +211,12 @@ impl PaymentAdjusterReal { "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", &unresolved_qualified_accounts ); - eprintln!("Unallocated balance for this iteration: {}", self.inner.unallocated_cw_service_fee_balance_minor().separate_with_commas()); + eprintln!( + "Unallocated balance for this iteration: {}", + self.inner + .unallocated_cw_service_fee_balance_minor() + .separate_with_commas() + ); if unresolved_qualified_accounts.len() == 1 { let last_one = unresolved_qualified_accounts @@ -1142,7 +1147,8 @@ mod tests { } #[test] - fn disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them(){ + fn disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them( + ) { init_test_logging(); let test_name = "disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); @@ -1172,7 +1178,7 @@ mod tests { subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); let accounts_sum: u128 = balance_1 + balance_2 + balance_3; - let service_fee_balance_in_minor_units = accounts_sum - ((balance_1*90)/100); + let service_fee_balance_in_minor_units = accounts_sum - ((balance_1 * 90) / 100); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1188,18 +1194,11 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_affordable_accounts = { - vec![account_2, account_3] - }; - assert_eq!( - result.affordable_accounts, - expected_affordable_accounts - ); + let expected_affordable_accounts = { vec![account_2, account_3] }; + assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); - let log_msg = format!( - "DEBUG: {test_name}: " - ); + let log_msg = format!("DEBUG: {test_name}: "); } #[test] diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 9ece1b502..3aeea3605 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -63,3 +63,32 @@ pub fn make_extreme_accounts( }) .collect() } + +pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( + constants_and_expected_values: &[(i128, i128)], + expected_num_sum: i128, +) { + constants_and_expected_values.iter().enumerate().for_each( + |(idx, (constant, expected_value))| { + assert_eq!( + constant, expected_value, + "constant wrong value at position {}", + idx + ) + }, + ); + + // This matters only if the constants participate in the calculator's formula. If that's not true, simply update + // the num sum and ignore the concern about synchronization + let actual_sum: i128 = constants_and_expected_values + .iter() + .map(|(val, _)| *val) + .sum(); + assert_eq!(actual_sum, expected_num_sum, + "The sum of constants used to calibre the calculator has changed, therefore you ought to see about \n\ + maintenance of the whole system with its all parameters (e.g. debt age, debt balance,...) and make \n\ + sure the weights coming from them are sensibly proportionate. There is a tool that can help you with \n\ + that, look for a global flag in the file 'diagnostics' in the PaymentAdjuster module. It will enable \n\ + rendering characteristics of the curves the calculations of these parameters are based on." + ) +} diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt index 77aabd37e..867b36aa2 100644 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt @@ -31,7 +31,19 @@ "55_333_000", "200_300_400", "500_000_000", + "4_000_000_000", "7_000_000_000", + "7_900_000_000", + # + # There is little anomaly between these values, a shift back caused by the attempts for cutting off + # the generally progressive curve for deceleration. While the cutback is almost never a dominate factor + # in the curve, here it cuts more than the base can grow there by, but just for a while. It seems + # tolerable speaking of debt ages larger than 24 years + # + "8_000_000_000", + "9_000_000_000", + "11_000_000_000", + "15_000_000_000", "78_000_000_000", "444_333_444_444" ], @@ -54,6 +66,10 @@ { "value": "604_800", "label": "WEEK" + }, + { + "value": "2_628_000", + "label": "MONTH" } ] } \ No newline at end of file From acf3e8a01fc331f3a4dbae046899bcc15d4553bf Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:54:06 +0700 Subject: [PATCH 136/250] GH-711: tests repaired --- .../age_criterion_calculator.rs | 72 ++++++++--------- .../miscellaneous/helper_functions.rs | 5 +- node/src/accountant/payment_adjuster/mod.rs | 80 ++++++++++--------- ...r_diagnostics_of_age_criterion_formula.txt | 12 ++- 4 files changed, 89 insertions(+), 80 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs index 78232f4f0..62aa596d4 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs @@ -3,7 +3,7 @@ use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::x_or_1; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::nonzero_positive; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use std::time::SystemTime; @@ -14,8 +14,7 @@ const AGE_EXPONENT_FOR_BASE: u32 = 3; // diminishes as the age of the debt stretches const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 550; -const AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER: u128 = 13; +const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 42; const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 4; pub struct AgeCriterionCalculator { @@ -43,7 +42,7 @@ impl AgeCriterionCalculator { // This argument slows down the growth of the power of 3 in the base value, but unlike to the descending // multiplier below, this happens consistently - let divisor = Self::nonzero_compute_divisor(elapsed_secs); + let divisor = Self::compute_divisor(elapsed_secs); // This argument impacts the curve so that it progresses slower the further in time we get // (The idea is: it's not already such a difference if it is weeks or months. Put differently, while @@ -75,11 +74,11 @@ impl AgeCriterionCalculator { } } - fn nonzero_compute_divisor(elapsed_sec: u64) -> u128 { + fn compute_divisor(elapsed_sec: u64) -> u128 { (elapsed_sec as f64).sqrt().ceil() as u128 } - fn nonzero_log_value(num: f64) -> u128 { + fn nonzero_log2_value(num: f64) -> u128 { if num < 2.0 { 1 } else { @@ -87,29 +86,23 @@ impl AgeCriterionCalculator { } } - // This multiplier is meant to push against the growth of the age criterion, - // slowing it down more and more as the time parameter increases. - // The reason is that balance numbers soon get huge but yet are not so unrealistic. - // For a balanced solution, the age criterion formula is designed to progress - // more steeply in the area of rather smaller amounts of seconds, while if - // we move on towards a couple of days, weeks, months and so on, the impact of the parameter - // diminishes + // This multiplier is meant to push against the growth of the age criterion, slowing it down more and more as + // the time goes on. The reason is that balances grow huge soon despite staying completely realistic and possible. + // For the different manner of time, the age formula is designed to up more steeply in rather smaller amounts of + // seconds, but while shifting towards days, weeks, months and so on, the inflating tendency diminishes fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { - let fast_growing_argument = (elapsed_secs as u128) + let too_fast_growing_argument = (elapsed_secs as u128) .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) .expect("pow blew up") as f64; - let log_value = Self::nonzero_log_value(fast_growing_argument); + let log_value = Self::nonzero_log2_value(too_fast_growing_argument); let log_stressed = log_value.pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; - let divisor_stressed = divisor * AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER; + let final_log_multiplier = (log_stressed / divisor).pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); - let final_log_multiplier = - (log_stressed / divisor_stressed).pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); - - x_or_1(final_log_multiplier) + nonzero_positive(final_log_multiplier) } } @@ -117,8 +110,8 @@ impl AgeCriterionCalculator { mod tests { use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, - AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER, AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, - AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, AGE_EXPONENT_FOR_BASE, + AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, + AGE_EXPONENT_FOR_BASE, }; use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, CriterionCalculator, @@ -139,18 +132,14 @@ mod tests { AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER .try_into() .unwrap(), - 550, - ), - ( - AGE_DESC_MULTIPLIER_DIVISOR_MULTIPLIER.try_into().unwrap(), - 13, + 42, ), (AGE_DESC_MULTIPLIER_DIVISOR_EXP.try_into().unwrap(), 4), ]; assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( &constants_and_their_expected_values, - 574, + 53, ) } @@ -158,7 +147,7 @@ mod tests { fn nonzero_compute_divisor_works() { let result: Vec<_> = [1, 100, 81, 82, 80] .into_iter() - .map(|secs| AgeCriterionCalculator::nonzero_compute_divisor(secs)) + .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) .collect(); assert_eq!(result, vec![1, 10, 9, 10, 9]) @@ -182,12 +171,11 @@ mod tests { #[test] fn compute_descending_multiplier_works() { - let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 18] + let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 18] .into_iter() - .take(12) .map(|exp| 10_u64.pow(exp)) .map(|seconds_elapsed| { - let divisor = AgeCriterionCalculator::nonzero_compute_divisor(seconds_elapsed); + let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) }) .collect(); @@ -195,8 +183,20 @@ mod tests { assert_eq!( result, vec![ - 729000000, 4826809000, 1435249152, 308915776, 40353607, 3511808, 287496, 21952, - 1331, 1, 1, 1 + 20415837456, + // Harmless irregularity at the beginning + 252688187761, + 50054665441, + 6414247921, + 429981696, + 15752961, + 614656, + 14641, + 256, + 1, + 1, + 1, + 1 ] ) } @@ -205,7 +205,7 @@ mod tests { fn nonzero_log_works() { let result = vec![0.0, 0.6, 1.3, 1.99999, 2.0, 2.1, 5.0, 9.0] .into_iter() - .map(|num| AgeCriterionCalculator::nonzero_log_value(num)) + .map(|num| AgeCriterionCalculator::nonzero_log2_value(num)) .collect::>(); assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 2, 3]) @@ -237,7 +237,7 @@ mod tests { let expected_criterion = { let elapsed_secs: u64 = now.duration_since(last_paid_timestamp).unwrap().as_secs(); - let divisor = AgeCriterionCalculator::nonzero_compute_divisor(elapsed_secs); + let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); let log_multiplier = AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); (elapsed_secs as u128) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index a89f9222e..f372ab6f2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -328,8 +328,7 @@ pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor } -// Replace with std lib method log10() for u128 which will be introduced by -// Rust 1.67.0; this was written using 1.63.0 +// Replace with std lib method log10() for u128 which will be introduced by Rust 1.67.0; this was written using 1.63.0 pub fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } @@ -345,7 +344,7 @@ pub fn log_2(x: u128) -> u32 { num_bits::() as u32 - x.leading_zeros() - 1 } -pub fn x_or_1(x: u128) -> u128 { +pub fn nonzero_positive(x: u128) -> u128 { if x == 0 { 1 } else { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c5a5446b3..a6f4dae01 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -315,8 +315,8 @@ impl PaymentAdjusterReal { TreatInsignificantAccount => { if remaining_undecided_accounts.is_empty() { // a) only one account can be eliminated in a single iteration, - // b) if there is one last undecided account, it goes on through - // a shortcut, not reaching out here + // b) if there is one last undecided account, it goes on through a shortcut, not reaching + // out down here unreachable!("Not possible by original design") } @@ -324,12 +324,12 @@ impl PaymentAdjusterReal { } TreatOutweighedAccounts(outweighed) => { if remaining_undecided_accounts.is_empty() { - //TODO explain in a comment - // now I know that: if an account is disqualified it can happen that - // the cw balance become sufficient for the rest of accounts, proceeding to another - // iteration, this results in all accounts classified as outweighed!! - todo!("this is so wierd") + debug!(self.logger, "Every account outweighed (Probably excessive funds after preceding \ + disqualification). Returning from recursion"); + + return RecursionResults::new(outweighed, vec![]); } + self.adjust_remaining_unallocated_cw_balance_down(&outweighed); outweighed } @@ -1147,10 +1147,10 @@ mod tests { } #[test] - fn disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them( + fn account_disqualification_causes_all_other_accounts_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them( ) { init_test_logging(); - let test_name = "disqualification_causes_every_other_account_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them"; + let test_name = "account_disqualification_causes_all_other_accounts_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); let balance_1 = 80_000_000_000_000_000_000; let account_1 = PayableAccount { @@ -1198,7 +1198,10 @@ mod tests { assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); - let log_msg = format!("DEBUG: {test_name}: "); + TestLogHandler::new().exists_log_containing(&format!( + "DEBUG: {test_name}: Every account outweighed (Probably \ + excessive funds after preceding disqualification). Returning from recursion" + )); } #[test] @@ -1207,8 +1210,8 @@ mod tests { let test_name = "overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely"; let now = SystemTime::now(); - // Each of the 3 accounts refers to a debt sized as the entire masq token supply and being - // 10 years old which generates enormously large numbers in the criteria + // Each of the 3 accounts refers to a debt sized as the entire masq token supply and being 10 years old which + // generates enormously large numbers in the criteria let qualified_payables = { let debt_age_in_months = vec![120, 120, 120]; make_extreme_accounts( @@ -1269,26 +1272,29 @@ mod tests { } #[test] - fn qualified_accounts_count_before_evens_the_payments_count_after() { + fn qualified_accounts_count_before_equals_the_payments_count_after() { // Meaning adjustment by service fee but no account elimination init_test_logging(); - let test_name = "qualified_accounts_count_before_evens_the_payments_count_after"; + let test_name = "qualified_accounts_count_before_equals_the_payments_count_after"; let now = SystemTime::now(); + let balance_1 = 4_444_444_444_444_444_444; let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 4_444_444_444_444_444_444, + balance_wei: balance_1, last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), pending_payable_opt: None, }; + let balance_2 = 6_000_000_000_000_000_000; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 6_000_000_000_000_000_000, + balance_wei: balance_2, last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }; + let balance_3 = 6_666_666_666_000_000_000; let account_3 = PayableAccount { wallet: make_wallet("ghi"), - balance_wei: 6_666_666_666_000_000_000, + balance_wei: balance_3, last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }; @@ -1296,9 +1302,8 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum: u128 = - 4_444_444_444_444_444_444 + 6_000_000_000_000_000_000 + 6_666_666_666_000_000_000; - let service_fee_balance_in_minor_units = accounts_sum - 2_000_000_000_000_000_000; + let accounts_sum = balance_1 + balance_2 + balance_3; + let service_fee_balance_in_minor_units = accounts_sum - 3_000_000_000_000_000_000; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1314,20 +1319,20 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_balance_1 = 3_900_336_673_839_282_582; - let expected_balance_2 = 5_817_708_802_473_506_572; - let expected_balance_3 = 5_393_065_634_131_655_290; + let expected_adjusted_balance_1 = 3_878_112_909_226_659_278; + let expected_adjusted_balance_2 = 5_941_743_288_347_289_696; + let expected_adjusted_balance_3 = 4_291_254_912_870_495_470; let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { - balance_wei: expected_balance_1, + balance_wei: expected_adjusted_balance_1, ..account_1 }; let account_2_adjusted = PayableAccount { - balance_wei: expected_balance_2, + balance_wei: expected_adjusted_balance_2, ..account_2 }; let account_3_adjusted = PayableAccount { - balance_wei: expected_balance_3, + balance_wei: expected_adjusted_balance_3, ..account_3 }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] @@ -1345,15 +1350,18 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000646566 6,000,000,000,000,000,000 +|0x0000000000000000000000000000000000646566 {} | {} -|0x0000000000000000000000000000000000676869 6,666,666,666,000,000,000 +|0x0000000000000000000000000000000000676869 {} | {} -|0x0000000000000000000000000000000000616263 4,444,444,444,444,444,444 +|0x0000000000000000000000000000000000616263 {} | {}", - expected_balance_2.separate_with_commas(), - expected_balance_3.separate_with_commas(), - expected_balance_1.separate_with_commas() + balance_2.separate_with_commas(), + expected_adjusted_balance_2.separate_with_commas(), + balance_3.separate_with_commas(), + expected_adjusted_balance_3.separate_with_commas(), + balance_1.separate_with_commas(), + expected_adjusted_balance_1.separate_with_commas() ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1478,8 +1486,7 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // Account_1, the least important one, was eliminated for not big enough - // transaction fee balance + // Account_1, the least important one, was eliminated for not big enough transaction fee balance let expected_accounts = { let account_2_adjusted = PayableAccount { balance_wei: 222_000_000_000_000, @@ -1537,8 +1544,7 @@ mod tests { client_id: 111, context_id: 234, }); - // Another place where I pick a populated response - // skeleton for hardening + // Another place where I pick a populated response skeleton for hardening let adjustment_setup = PreparedAdjustment { qualified_payables, agent, @@ -1568,7 +1574,7 @@ mod tests { TestLogHandler::new().exists_log_containing(&format!( "INFO: {test_name}: Shortage of MASQ \ in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ - ruled out from this round of payments. The proposed adjustment 69,153,137,093 wei was less \ + ruled out from this round of payments. The proposed adjustment 79,556,958,958 wei was less \ than half of the recorded debt, 600,000,000,000 wei" )); } diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt index 867b36aa2..019d50300 100644 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt +++ b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt @@ -13,6 +13,9 @@ # If the same value has been defined by any of the other categories, marked values will replace them with no conflict. # { + # anomalies: places where the trend changes from ascending in the big picture to a short descending one. Such places + # are always minor and limited so the major trend continues after an interruption. It's always a question how big + # issue this is, usually none at all, because the bounces are shallow. "literals": [ "1", "5", @@ -20,6 +23,9 @@ "25", "44", "50", + # + # Local anomaly* somewhere near here + # "75", "180", "600", @@ -35,10 +41,8 @@ "7_000_000_000", "7_900_000_000", # - # There is little anomaly between these values, a shift back caused by the attempts for cutting off - # the generally progressive curve for deceleration. While the cutback is almost never a dominate factor - # in the curve, here it cuts more than the base can grow there by, but just for a while. It seems - # tolerable speaking of debt ages larger than 24 years + # A bit more significant anomaly* in a long range of values, however, if taken seriously, we speak of + # section on the time scale representing approximately 24 years. This makes the issue irrelevant. # "8_000_000_000", "9_000_000_000", From 314f99e61533c7a755f722d7513a238d3a091467 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 13 Mar 2024 13:53:34 +0100 Subject: [PATCH 137/250] GH-711-b: interim commit --- node/src/accountant/mod.rs | 66 +++++++------ .../payment_adjuster/adjustment_runners.rs | 2 + .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 29 +++--- .../pre_adjustment_analyzer.rs | 5 +- .../payable_scanner/mod.rs | 6 +- node/src/accountant/scanners/mod.rs | 99 ++++++++----------- .../src/accountant/scanners/scanners_utils.rs | 15 +-- node/src/accountant/scanners/test_utils.rs | 4 +- node/src/accountant/test_utils.rs | 13 ++- node/src/blockchain/blockchain_bridge.rs | 12 +-- node/src/proxy_server/mod.rs | 28 +++--- node/src/sub_lib/accountant.rs | 4 +- node/src/sub_lib/blockchain_bridge.rs | 10 +- 14 files changed, 150 insertions(+), 145 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 2090be248..5c95871f4 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -13,7 +13,7 @@ use core::fmt::Debug; use masq_lib::constants::{SCAN_ERROR, WEIS_IN_GWEI}; use std::cell::{Ref, RefCell}; -use crate::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoError}; +use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDao, PayableDaoError}; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayableDao; use crate::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoError}; use crate::accountant::db_access_objects::utils::{ @@ -127,6 +127,12 @@ pub struct ReceivedPayments { pub response_skeleton_opt: Option, } +#[derive(Debug, PartialEq, Clone)] +pub struct QualifiedPayableAccount{ + pub account: PayableAccount, + pub payment_threshold_intercept: u128 +} + #[derive(Debug, Message, PartialEq)] pub struct SentPayables { pub payment_procedure_result: Result, PayableTransactionError>, @@ -341,7 +347,7 @@ pub trait SkeletonOptHolder { #[derive(Debug, PartialEq, Eq, Message, Clone)] pub struct RequestTransactionReceipts { - pub pending_payable: Vec, + pub pending_payables: Vec, pub response_skeleton_opt: Option, } @@ -623,17 +629,17 @@ impl Accountant { self.logger, "MsgId {}: Accruing debt to {} for consuming {} exited bytes", msg_id, - msg.exit.earning_wallet, - msg.exit.payload_size + msg.exit_service.earning_wallet, + msg.exit_service.payload_size ); self.record_service_consumed( - msg.exit.service_rate, - msg.exit.byte_rate, + msg.exit_service.service_rate, + msg.exit_service.byte_rate, msg.timestamp, - msg.exit.payload_size, - &msg.exit.earning_wallet, + msg.exit_service.payload_size, + &msg.exit_service.earning_wallet, ); - msg.routing.iter().for_each(|routing_service| { + msg.routing_services.iter().for_each(|routing_service| { debug!( self.logger, "MsgId {}: Accruing debt to {} for consuming {} routed bytes", @@ -1471,7 +1477,7 @@ mod tests { #[test] fn received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge( ) { - // the numbers for balances don't do real math, they need not to match either the condition for + // the numbers for balances don't do real math, they need not match either the condition for // the payment adjustment or the actual values that come from the payable size reducing algorithm; // all that is mocked in this test init_test_logging(); @@ -1483,26 +1489,26 @@ mod tests { .start() .recipient(); let mut subject = AccountantBuilder::default().build(); - let unadjusted_account_1 = make_payable_account(111_111); - let unadjusted_account_2 = make_payable_account(222_222); + let unadjusted_account_1 = QualifiedPayableAccount{account: make_payable_account(111_111), payment_threshold_intercept: 1234567}; + let unadjusted_account_2 = QualifiedPayableAccount{account: make_payable_account(222_222), payment_threshold_intercept: 444555666}; let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1.clone() + ..unadjusted_account_1.account.clone() }; let adjusted_account_2 = PayableAccount { balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2.clone() + ..unadjusted_account_2.account.clone() }; let response_skeleton = ResponseSkeleton { client_id: 12, context_id: 55, }; - let unadjusted_accounts = vec![unadjusted_account_1, unadjusted_account_2]; + let unadjusted_qualified_accounts = vec![unadjusted_account_1, unadjusted_account_2]; let agent_id_stamp_first_phase = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp_first_phase); let payable_payments_setup_msg = BlockchainAgentWithContextMessage { - protected_qualified_payables: protect_payables_in_test(unadjusted_accounts.clone()), + protected_qualified_payables: protect_payables_in_test(unadjusted_qualified_accounts.clone()), agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; @@ -1543,7 +1549,7 @@ mod tests { ); assert_eq!( actual_prepared_adjustment.qualified_payables, - unadjusted_accounts + unadjusted_qualified_accounts ); assert_eq!( actual_prepared_adjustment.agent.arbitrary_id_stamp(), @@ -1775,7 +1781,7 @@ mod tests { assert_eq!( blockchain_bridge_recording.get_record::(0), &RequestTransactionReceipts { - pending_payable: vec![fingerprint], + pending_payables: vec![fingerprint], response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -2190,7 +2196,7 @@ mod tests { .begin_scan_params(&begin_scan_params_arc) .begin_scan_result(Err(BeginScanError::NothingToProcess)) .begin_scan_result(Ok(RequestTransactionReceipts { - pending_payable: vec![], + pending_payables: vec![], response_skeleton_opt: None, })) .stop_the_system_after_last_msg(); @@ -2639,7 +2645,7 @@ mod tests { assert_eq!( received_msg, &RequestTransactionReceipts { - pending_payable: vec![payable_fingerprint_1, payable_fingerprint_2], + pending_payables: vec![payable_fingerprint_1, payable_fingerprint_2], response_skeleton_opt: None, } ); @@ -2951,14 +2957,14 @@ mod tests { subject_addr .try_send(ReportServicesConsumedMessage { timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: earning_wallet_exit.clone(), payload_size: 1200, service_rate: 120, byte_rate: 30, }, routing_payload_size: 3456, - routing: vec![ + routing_services: vec![ RoutingServiceConsumed { earning_wallet: earning_wallet_routing_1.clone(), service_rate: 42, @@ -3048,14 +3054,14 @@ mod tests { let timestamp = SystemTime::now(); let report_message = ReportServicesConsumedMessage { timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: foreign_wallet.clone(), payload_size: 1234, service_rate: 45, byte_rate: 10, }, routing_payload_size: 3333, - routing: vec![RoutingServiceConsumed { + routing_services: vec![RoutingServiceConsumed { earning_wallet: consuming_wallet.clone(), service_rate: 42, byte_rate: 6, @@ -3090,14 +3096,14 @@ mod tests { let timestamp = SystemTime::now(); let report_message = ReportServicesConsumedMessage { timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: foreign_wallet.clone(), payload_size: 1234, service_rate: 45, byte_rate: 10, }, routing_payload_size: 3333, - routing: vec![RoutingServiceConsumed { + routing_services: vec![RoutingServiceConsumed { earning_wallet: earning_wallet.clone(), service_rate: 42, byte_rate: 6, @@ -3130,14 +3136,14 @@ mod tests { let config = bc_from_wallets(consuming_wallet.clone(), make_wallet("own earning wallet")); let report_message = ReportServicesConsumedMessage { timestamp: SystemTime::now(), - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: consuming_wallet.clone(), payload_size: 1234, service_rate: 42, byte_rate: 24, }, routing_payload_size: 3333, - routing: vec![], + routing_services: vec![], }; let more_money_payable_params_arc = @@ -3160,14 +3166,14 @@ mod tests { let config = bc_from_earning_wallet(earning_wallet.clone()); let report_message = ReportServicesConsumedMessage { timestamp: SystemTime::now(), - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: earning_wallet.clone(), payload_size: 1234, service_rate: 42, byte_rate: 24, }, routing_payload_size: 3333, - routing: vec![], + routing_services: vec![], }; let more_money_payable_params_arc = diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 17e08e57b..512163e4d 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -9,6 +9,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_fi use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; use std::vec; +use crate::accountant::QualifiedPayableAccount; // There are just two runners. Different by the adjustment they can perform, either adjusting by // both the transaction fee and service fee, or exclusively by the transaction fee. The idea is @@ -158,6 +159,7 @@ mod tests { use itertools::Either; use std::fmt::Debug; use std::time::{Duration, SystemTime}; + use crate::accountant::QualifiedPayableAccount; fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { let adjustment = Adjustment::ByServiceFee; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index f372ab6f2..fc3d00076 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -995,7 +995,7 @@ mod tests { .map(|account| account.wallet.clone()) .collect(); let subject = make_initialized_subject(now, None, None); - // The initial order is remembered because when the weight are applied the collection the collection + // The initial order is remembered because when the weight are applied the collection // also gets sorted and will not necessarily have to match the initial order let weights_and_accounts = subject.calculate_weights_for_accounts(accounts); (weights_and_accounts, wallets_in_order) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index a6f4dae01..bc57c606b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -46,13 +46,14 @@ use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_cal use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; +use crate::accountant::QualifiedPayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[PayableAccount], + qualified_payables: &[QualifiedPayableAccount], agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError>; @@ -74,7 +75,7 @@ pub struct PaymentAdjusterReal { impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[PayableAccount], + qualified_payables: &[QualifiedPayableAccount], agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError> { let number_of_counts = qualified_payables.len(); @@ -126,11 +127,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.complete_debug_info_if_enabled(sketched_debug_info_opt, &affordable_accounts); - Ok(OutboundPaymentsInstructions { - affordable_accounts, - response_skeleton_opt, + Ok(OutboundPaymentsInstructions::new( + Either::Right(affordable_accounts), agent, - }) + response_skeleton_opt, + )) } as_any_in_trait_impl!(); @@ -175,18 +176,18 @@ impl PaymentAdjusterReal { fn run_adjustment( &mut self, - qualified_accounts: Vec, + qualified_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let accounts = self.calculate_criteria_and_propose_adjustments_recursively( + let processed_accounts = self.calculate_criteria_and_propose_adjustments_recursively( qualified_accounts, TransactionAndServiceFeeAdjustmentRunner {}, )?; - if found_zero_affordable_accounts(&accounts) { + if found_zero_affordable_accounts(&processed_accounts) { return Err(PaymentAdjusterError::AllAccountsEliminated); } - match accounts { + match processed_accounts { Either::Left(non_exhausted_accounts) => { let original_cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); @@ -201,7 +202,7 @@ impl PaymentAdjusterReal { } fn calculate_criteria_and_propose_adjustments_recursively( &mut self, - unresolved_qualified_accounts: Vec, + unresolved_qualified_accounts: Vec, adjustment_runner: AR, ) -> RT where @@ -223,7 +224,7 @@ impl PaymentAdjusterReal { .into_iter() .next() .expect("previous if stmt must be wrong"); - return adjustment_runner.adjust_last_one(self, last_one); + return adjustment_runner.adjust_last_one(self, last_one.account); } let weights_and_accounts_sorted = @@ -348,7 +349,7 @@ impl PaymentAdjusterReal { fn calculate_weights_for_accounts( &self, - accounts: Vec, + accounts: Vec, ) -> Vec { let criteria_calculators: Vec> = vec![ Box::new(BalanceCriterionCalculator::new()), @@ -564,7 +565,7 @@ impl PaymentAdjusterReal { fn sketch_debug_info_opt( &self, - qualified_payables: &[PayableAccount], + qualified_payables: &[QualifiedPayableAccount], ) -> Option> { self.logger.debug_enabled().then(|| { qualified_payables diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs index 5ee3b6402..899260c07 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -15,6 +15,7 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockch use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; +use crate::accountant::QualifiedPayableAccount; pub struct PreAdjustmentAnalyzer {} @@ -87,11 +88,11 @@ impl PreAdjustmentAnalyzer { pub fn check_need_of_adjustment_by_service_fee( &self, logger: &Logger, - payables: Either<&[PayableAccount], &[WeightedAccount]>, + payables: Either<&[QualifiedPayableAccount], &[WeightedAccount]>, cw_service_fee_balance_minor: u128, ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { - Either::Left(accounts) => accounts.iter().collect(), + Either::Left(accounts) => accounts.iter().map(|qualified_payable|&qualified_payable.account).collect(), Either::Right(weighted_accounts) => weighted_accounts .iter() .map(|weighted_account| &weighted_account.account) diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 6f6b2acec..9d1bae6e8 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -11,7 +11,7 @@ use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::Scanner; -use crate::accountant::ResponseSkeleton; +use crate::accountant::{QualifiedPayableAccount, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use actix::Message; use itertools::Either; @@ -40,7 +40,7 @@ pub trait SolvencySensitivePaymentInstructor { } pub struct PreparedAdjustment { - pub qualified_payables: Vec, + pub qualified_payables: Vec, pub agent: Box, pub response_skeleton_opt: Option, pub adjustment: Adjustment, @@ -48,7 +48,7 @@ pub struct PreparedAdjustment { impl PreparedAdjustment { pub fn new( - qualified_payables: Vec, + qualified_payables: Vec, agent: Box, response_skeleton_opt: Option, adjustment: Adjustment, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index aa5244468..42f1c4129 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -23,7 +23,7 @@ use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils:: PendingPayableScanReport, }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; -use crate::accountant::PendingPayableId; +use crate::accountant::{PendingPayableId, QualifiedPayableAccount}; use crate::accountant::{ comma_joined_stringifiable, gwei_to_wei, Accountant, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, @@ -267,12 +267,22 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { .borrow() .search_for_indispensable_adjustment(&unprotected, &*msg.agent) { - Ok(adjustment_opt) => Some(Self::handle_adjustment_opt( - adjustment_opt, - msg.agent, - msg.response_skeleton_opt, - unprotected, - )), + Ok(adjustment_opt) => + { + let either = match adjustment_opt { + None => Either::Left(OutboundPaymentsInstructions::new( + Either::Left(unprotected), + msg.agent, + msg.response_skeleton_opt, + )), + Some(adjustment) => { + let prepared_adjustment = + PreparedAdjustment::new(unprotected, msg.agent, msg.response_skeleton_opt, adjustment); + Either::Right(prepared_adjustment) + } + }; + + Some(either)}, Err(e) => { warning!( logger, @@ -337,26 +347,19 @@ impl PayableScanner { &self, non_pending_payables: Vec, logger: &Logger, - ) -> Vec { - fn pass_payables_and_drop_points( - qp_tp: impl Iterator, - ) -> Vec { - let (payables, _) = qp_tp.unzip::<_, _, Vec, Vec<_>>(); - payables - } - - let qualified_payables_and_points_uncollected = + ) -> Vec { + let qualified_payables = non_pending_payables.into_iter().flat_map(|account| { - self.payable_exceeded_threshold(&account, SystemTime::now()) - .map(|threshold_point| (account, threshold_point)) - }); + // TODO this SystemTime::now() isn't tested probably + self.payable_exceeded_threshold(&account, SystemTime::now()) + .map(|payment_threshold_intercept| QualifiedPayableAccount {account, + payment_threshold_intercept}) + }).collect(); match logger.debug_enabled() { - false => pass_payables_and_drop_points(qualified_payables_and_points_uncollected), + false => qualified_payables, true => { - let qualified_and_points_collected = - qualified_payables_and_points_uncollected.collect_vec(); - payables_debug_summary(&qualified_and_points_collected, logger); - pass_payables_and_drop_points(qualified_and_points_collected.into_iter()) + payables_debug_summary(&qualified_payables, logger); + qualified_payables } } } @@ -388,6 +391,7 @@ impl PayableScanner { let threshold = self .payable_threshold_gauge .calculate_payout_threshold_in_gwei(&self.common.payment_thresholds, debt_age); + if payable.balance_wei > threshold { Some(threshold) } else { @@ -395,6 +399,7 @@ impl PayableScanner { } } + fn separate_existent_and_nonexistent_fingerprints<'a>( &'a self, sent_payables: &'a [&'a PendingPayable], @@ -569,33 +574,13 @@ impl PayableScanner { }; } - fn protect_payables(&self, payables: Vec) -> Obfuscated { + fn protect_payables(&self, payables: Vec) -> Obfuscated { Obfuscated::obfuscate_vector(payables) } - fn expose_payables(&self, obfuscated: Obfuscated) -> Vec { + fn expose_payables(&self, obfuscated: Obfuscated) -> Vec { obfuscated.expose_vector() } - - fn handle_adjustment_opt( - adjustment_opt: Option, - agent: Box, - response_skeleton_opt: Option, - unprotected: Vec, - ) -> Either { - match adjustment_opt { - None => Either::Left(OutboundPaymentsInstructions::new( - unprotected, - agent, - response_skeleton_opt, - )), - Some(adjustment) => { - let prepared_adjustment = - PreparedAdjustment::new(unprotected, agent, response_skeleton_opt, adjustment); - Either::Right(prepared_adjustment) - } - } - } } pub struct PendingPayableScanner { @@ -631,7 +616,7 @@ impl Scanner for PendingP filtered_pending_payable.len() ); Ok(RequestTransactionReceipts { - pending_payable: filtered_pending_payable, + pending_payables: filtered_pending_payable, response_skeleton_opt, }) } @@ -1129,10 +1114,7 @@ mod tests { PendingPayableScannerBuilder, ReceivableDaoFactoryMock, ReceivableDaoMock, ReceivableScannerBuilder, }; - use crate::accountant::{ - gwei_to_wei, PendingPayableId, ReceivedPayments, ReportTransactionReceipts, - RequestTransactionReceipts, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, - }; + use crate::accountant::{gwei_to_wei, PendingPayableId, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, QualifiedPayableAccount}; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; use crate::blockchain::blockchain_interface::data_structures::{ @@ -1251,11 +1233,11 @@ mod tests { #[test] fn protected_payables_can_be_cast_from_and_back_to_vec_of_payable_accounts_by_payable_scanner() { - let initial_unprotected = vec![make_payable_account(123), make_payable_account(456)]; + let initial_unprotected = vec![QualifiedPayableAccount{account: make_payable_account(123), payment_threshold_intercept: 123456789}, QualifiedPayableAccount{account: make_payable_account(456), payment_threshold_intercept: 987654321}]; let subject = PayableScannerBuilder::new().build(); let protected = subject.protect_payables(initial_unprotected.clone()); - let again_unprotected: Vec = subject.expose_payables(protected); + let again_unprotected: Vec = subject.expose_payables(protected); assert_eq!(initial_unprotected, again_unprotected) } @@ -2110,7 +2092,7 @@ mod tests { let time = (payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec - 1) as i64; - let qualified_payable = PayableAccount { + let qualifiable_payable = PayableAccount { wallet: make_wallet("wallet0"), balance_wei: debt, last_paid_timestamp: from_time_t(time), @@ -2123,11 +2105,14 @@ mod tests { let logger = Logger::new(test_name); let result = subject.sniff_out_alarming_payables_and_maybe_log_them( - vec![qualified_payable.clone()], + vec![qualifiable_payable.clone()], &logger, ); - assert_eq!(result, vec![qualified_payable]); + assert_eq!(result.len(),1); + assert_eq!(&result[0].account, &qualifiable_payable); + todo!("see if you can assert against exactly computed value"); + //assert!(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei) <= result[0].payment_threshold_intercept && result[0].payment_threshold_intercept <= gwei_to_wei((payment_thresholds.permanent_debt_allowed_gwei * 100001) / 100000)); TestLogHandler::new().exists_log_matching(&format!( "DEBUG: {}: Paying qualified debts:\n999,999,999,000,000,\ 000 wei owed for \\d+ sec exceeds threshold: 500,000,000,000,000,000 wei; creditor: \ @@ -2199,7 +2184,7 @@ mod tests { assert_eq!( result, Ok(RequestTransactionReceipts { - pending_payable: fingerprints, + pending_payables: fingerprints, response_skeleton_opt: None }) ); diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 0c7d139e7..fc00fdf86 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -6,7 +6,7 @@ pub mod payable_scanner_utils { use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; - use crate::accountant::{comma_joined_stringifiable, ProcessedPayableFallible, SentPayables}; + use crate::accountant::{comma_joined_stringifiable, ProcessedPayableFallible, QualifiedPayableAccount, SentPayables}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; @@ -168,7 +168,7 @@ pub mod payable_scanner_utils { } } - pub fn payables_debug_summary(qualified_accounts: &[(PayableAccount, u128)], logger: &Logger) { + pub fn payables_debug_summary(qualified_accounts: &[QualifiedPayableAccount], logger: &Logger) { if qualified_accounts.is_empty() { return; } @@ -176,16 +176,17 @@ pub mod payable_scanner_utils { let now = SystemTime::now(); qualified_accounts .iter() - .map(|(payable, threshold_point)| { + .map(|qualified_account| { + let account = &qualified_account.account; let p_age = now - .duration_since(payable.last_paid_timestamp) + .duration_since(account.last_paid_timestamp) .expect("Payable time is corrupt"); format!( "{} wei owed for {} sec exceeds threshold: {} wei; creditor: {}", - payable.balance_wei.separate_with_commas(), + account.balance_wei.separate_with_commas(), p_age.as_secs(), - threshold_point.separate_with_commas(), - payable.wallet + qualified_account.payment_threshold_intercept.separate_with_commas(), + account.wallet ) }) .join("\n") diff --git a/node/src/accountant/scanners/test_utils.rs b/node/src/accountant/scanners/test_utils.rs index c43d6f71b..c7423e7a8 100644 --- a/node/src/accountant/scanners/test_utils.rs +++ b/node/src/accountant/scanners/test_utils.rs @@ -2,9 +2,9 @@ #![cfg(test)] -use crate::accountant::db_access_objects::payable_dao::PayableAccount; use masq_lib::type_obfuscation::Obfuscated; +use crate::accountant::QualifiedPayableAccount; -pub fn protect_payables_in_test(payables: Vec) -> Obfuscated { +pub fn protect_payables_in_test(payables: Vec) -> Obfuscated { Obfuscated::obfuscate_vector(payables) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 7823c87df..a4dfc53a5 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -26,9 +26,7 @@ use crate::accountant::scanners::{ BeginScanError, PayableScanner, PendingPayableScanner, PeriodicalScanScheduler, ReceivableScanner, ScanSchedulers, Scanner, }; -use crate::accountant::{ - gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, -}; +use crate::accountant::{gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, QualifiedPayableAccount}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; use crate::blockchain::test_utils::make_tx_hash; @@ -1402,7 +1400,7 @@ impl PayableThresholdsGaugeMock { #[derive(Default)] pub struct PaymentAdjusterMock { search_for_indispensable_adjustment_params: - Arc, ArbitraryIdStamp)>>>, + Arc, ArbitraryIdStamp)>>>, search_for_indispensable_adjustment_results: RefCell, PaymentAdjusterError>>>, adjust_payments_params: Arc>>, @@ -1413,7 +1411,7 @@ pub struct PaymentAdjusterMock { impl PaymentAdjuster for PaymentAdjusterMock { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[PayableAccount], + qualified_payables: &[QualifiedPayableAccount], agent: &dyn BlockchainAgent, ) -> Result, PaymentAdjusterError> { self.search_for_indispensable_adjustment_params @@ -1668,3 +1666,8 @@ impl ScanSchedulers { } } } + +pub fn make_qualified_payables(payment_thresholds: PaymentThresholds, payables: Vec)->Vec{ + sort this out + payables.into_iter().map(||) +} diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 57f02e5be..b0f87d1ba 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -400,7 +400,7 @@ impl BlockchainBridge { Vec>, Option<(BlockchainError, H256)>, ) = (vec![], None); - let (vector_of_results, error_opt) = msg.pending_payable.iter().fold( + let (vector_of_results, error_opt) = msg.pending_payables.iter().fold( init, |(mut ok_receipts, err_opt), current_fingerprint| match err_opt { None => match self @@ -418,7 +418,7 @@ impl BlockchainBridge { ); let pairs = vector_of_results .into_iter() - .zip(msg.pending_payable.into_iter()) + .zip(msg.pending_payables.into_iter()) .collect_vec(); self.pending_payable_confirmation .report_transaction_receipts_sub_opt @@ -1077,7 +1077,7 @@ mod tests { let peer_actors = peer_actors_builder().accountant(accountant).build(); send_bind_message!(subject_subs, peer_actors); let msg = RequestTransactionReceipts { - pending_payable: vec![ + pending_payables: vec![ pending_payable_fingerprint_1.clone(), pending_payable_fingerprint_2.clone(), ], @@ -1231,7 +1231,7 @@ mod tests { .report_transaction_receipts_sub_opt = Some(report_transaction_receipt_recipient); subject.scan_error_subs_opt = Some(scan_error_recipient); let msg = RequestTransactionReceipts { - pending_payable: vec![ + pending_payables: vec![ fingerprint_1.clone(), fingerprint_2.clone(), fingerprint_3, @@ -1293,7 +1293,7 @@ mod tests { .pending_payable_confirmation .report_transaction_receipts_sub_opt = Some(recipient); let msg = RequestTransactionReceipts { - pending_payable: vec![], + pending_payables: vec![], response_skeleton_opt: None, }; let system = System::new( @@ -1356,7 +1356,7 @@ mod tests { .report_transaction_receipts_sub_opt = Some(report_transaction_recipient); subject.scan_error_subs_opt = Some(scan_error_recipient); let msg = RequestTransactionReceipts { - pending_payable: vec![fingerprint_1, fingerprint_2], + pending_payables: vec![fingerprint_1, fingerprint_2], response_skeleton_opt: None, }; let system = System::new("test"); diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index bd8f62d4a..e59392fc6 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -673,9 +673,9 @@ impl ProxyServer { accountant_sub .try_send(ReportServicesConsumedMessage { timestamp, - exit, + exit_service: exit, routing_payload_size: pkg.payload.len(), - routing, + routing_services: routing, }) .expect("Accountant is dead"); } @@ -810,9 +810,9 @@ impl ProxyServer { .collect::>(); let report_message = ReportServicesConsumedMessage { timestamp: SystemTime::now(), - exit: exit_service_report, + exit_service: exit_service_report, routing_payload_size: routing_size, - routing: routing_service_reports, + routing_services: routing_service_reports, }; self.subs .as_ref() @@ -2340,14 +2340,14 @@ mod tests { record, &ReportServicesConsumedMessage { timestamp: now, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: exit_earning_wallet, payload_size: exit_payload_size, service_rate: exit_node_rate_pack.exit_service_rate, byte_rate: exit_node_rate_pack.exit_byte_rate }, routing_payload_size: payload_enc_length, - routing: vec![ + routing_services: vec![ RoutingServiceConsumed { earning_wallet: route_1_earning_wallet, service_rate: routing_node_1_rate_pack.routing_service_rate, @@ -3392,14 +3392,14 @@ mod tests { first_report, &ReportServicesConsumedMessage { timestamp: first_report_timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: incoming_route_d_wallet, payload_size: first_exit_size, service_rate: rate_pack_d.exit_service_rate, byte_rate: rate_pack_d.exit_byte_rate }, routing_payload_size: routing_size, - routing: vec![ + routing_services: vec![ RoutingServiceConsumed { earning_wallet: incoming_route_e_wallet, service_rate: rate_pack_e.routing_service_rate, @@ -3421,14 +3421,14 @@ mod tests { second_report, &ReportServicesConsumedMessage { timestamp: second_report_timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: incoming_route_g_wallet, payload_size: second_exit_size, service_rate: rate_pack_g.exit_service_rate, byte_rate: rate_pack_g.exit_byte_rate }, routing_payload_size: routing_size, - routing: vec![ + routing_services: vec![ RoutingServiceConsumed { earning_wallet: incoming_route_h_wallet, service_rate: rate_pack_h.routing_service_rate, @@ -3530,14 +3530,14 @@ mod tests { services_consumed_report, &ReportServicesConsumedMessage { timestamp: returned_timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: incoming_route_d_wallet, payload_size: exit_size, service_rate: rate_pack_d.exit_service_rate, byte_rate: rate_pack_d.exit_byte_rate }, routing_payload_size: routing_size, - routing: vec![RoutingServiceConsumed { + routing_services: vec![RoutingServiceConsumed { earning_wallet: incoming_route_e_wallet, service_rate: rate_pack_e.routing_service_rate, byte_rate: rate_pack_e.routing_byte_rate @@ -3696,14 +3696,14 @@ mod tests { services_consumed_message, &ReportServicesConsumedMessage { timestamp: returned_timestamp, - exit: ExitServiceConsumed { + exit_service: ExitServiceConsumed { earning_wallet: incoming_route_d_wallet, payload_size: 0, service_rate: rate_pack_d.exit_service_rate, byte_rate: rate_pack_d.exit_byte_rate }, routing_payload_size: routing_size, - routing: vec![ + routing_services: vec![ RoutingServiceConsumed { earning_wallet: incoming_route_e_wallet, service_rate: rate_pack_e.routing_service_rate, diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 15ceb0613..50430b88a 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -139,9 +139,9 @@ pub struct ReportExitServiceProvidedMessage { #[derive(Clone, PartialEq, Eq, Debug, Message)] pub struct ReportServicesConsumedMessage { pub timestamp: SystemTime, - pub exit: ExitServiceConsumed, + pub exit_service: ExitServiceConsumed, pub routing_payload_size: usize, - pub routing: Vec, + pub routing_services: Vec, } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 65e59a68e..a435a6f7a 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -3,7 +3,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; -use crate::accountant::{RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder}; +use crate::accountant::{QualifiedPayableAccount, RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder}; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::sub_lib::peer_actors::BindMessage; use actix::Message; @@ -12,6 +12,7 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::ui_gateway::NodeFromUiMessage; use std::fmt; use std::fmt::{Debug, Formatter}; +use itertools::Either; use web3::types::U256; #[derive(Clone, PartialEq, Eq, Debug, Default)] @@ -48,10 +49,15 @@ pub struct OutboundPaymentsInstructions { impl OutboundPaymentsInstructions { pub fn new( - affordable_accounts: Vec, + accounts: Either, Vec>, agent: Box, response_skeleton_opt: Option, ) -> Self { + let affordable_accounts = match accounts { + Either::Left(qualified_account) => todo!(), + Either::Right(adjusted_account) => todo!() + }; + Self { affordable_accounts, agent, From 788eb42ab5de9dc23dceabc81912524e37cb6946 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 16 Mar 2024 14:00:38 +0100 Subject: [PATCH 138/250] GH-711-b: Implemented QualifiedPayableAccount structure --- node/src/accountant/mod.rs | 179 +++++--- .../payment_adjuster/adjustment_runners.rs | 103 +++-- .../criteria_calculators/mod.rs | 58 ++- .../payment_adjuster/diagnostics.rs | 32 +- .../accountant/payment_adjuster/log_fns.rs | 8 +- .../miscellaneous/data_structures.rs | 29 +- .../miscellaneous/helper_functions.rs | 186 ++++++--- node/src/accountant/payment_adjuster/mod.rs | 395 +++++++++++------- .../pre_adjustment_analyzer.rs | 9 +- .../accountant/payment_adjuster/test_utils.rs | 16 +- .../payable_scanner/mod.rs | 1 - node/src/accountant/scanners/mod.rs | 236 ++++++----- .../src/accountant/scanners/scanners_utils.rs | 73 +++- node/src/accountant/scanners/test_utils.rs | 4 +- node/src/accountant/test_utils.rs | 56 ++- node/src/blockchain/blockchain_bridge.rs | 53 +-- node/src/sub_lib/blockchain_bridge.rs | 11 +- 17 files changed, 932 insertions(+), 517 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 5c95871f4..7b91db361 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -13,7 +13,9 @@ use core::fmt::Debug; use masq_lib::constants::{SCAN_ERROR, WEIS_IN_GWEI}; use std::cell::{Ref, RefCell}; -use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDao, PayableDaoError}; +use crate::accountant::db_access_objects::payable_dao::{ + PayableAccount, PayableDao, PayableDaoError, +}; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayableDao; use crate::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoError}; use crate::accountant::db_access_objects::utils::{ @@ -127,10 +129,19 @@ pub struct ReceivedPayments { pub response_skeleton_opt: Option, } -#[derive(Debug, PartialEq, Clone)] -pub struct QualifiedPayableAccount{ - pub account: PayableAccount, - pub payment_threshold_intercept: u128 +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct QualifiedPayableAccount { + pub payable: PayableAccount, + pub payment_threshold_intercept: u128, +} + +impl QualifiedPayableAccount { + pub fn new( + payable_account: PayableAccount, + payment_threshold_intercept: u128, + ) -> QualifiedPayableAccount { + todo!() + } } #[derive(Debug, Message, PartialEq)] @@ -1023,16 +1034,18 @@ mod tests { use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterError}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::accountant::scanners::test_utils::protect_payables_in_test; + use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_payable_account, make_payables, - BannedDaoFactoryMock, MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, - PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, - PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, + bc_from_earning_wallet, bc_from_wallets, make_guaranteed_qualified_payables, + make_non_guaranteed_qualified_payable, make_payable_account, + make_unqualified_and_qualified_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, + NullScanner, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, + PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, + ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; @@ -1315,16 +1328,16 @@ mod tests { #[test] fn scan_payables_request() { let config = bc_from_earning_wallet(make_wallet("some_wallet_address")); - let payable_account = PayableAccount { + let now = SystemTime::now(); + let payable = PayableAccount { wallet: make_wallet("wallet"), balance_wei: gwei_to_wei(DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 1), - last_paid_timestamp: SystemTime::now().sub(Duration::from_secs( + last_paid_timestamp: now.sub(Duration::from_secs( (DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 1) as u64, )), pending_payable_opt: None, }; - let payable_dao = - PayableDaoMock::new().non_pending_payables_result(vec![payable_account.clone()]); + let payable_dao = PayableDaoMock::new().non_pending_payables_result(vec![payable.clone()]); let subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_daos(vec![ForPayableScanner(payable_dao)]) @@ -1349,10 +1362,14 @@ mod tests { System::current().stop(); system.run(); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + let expected_qualified_payables = + make_guaranteed_qualified_payables(vec![payable], &DEFAULT_PAYMENT_THRESHOLDS, now); assert_eq!( blockchain_bridge_recording.get_record::(0), &QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(vec![payable_account]), + protected_qualified_payables: protect_qualified_payables_in_test( + expected_qualified_payables + ), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, context_id: 4321, @@ -1405,9 +1422,9 @@ mod tests { #[test] fn received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge( ) { - // the numbers for balances don't do real math, they need not to match either the condition for - // the payment adjustment or the actual values that come from the payable size reducing algorithm; - // all that is mocked in this test + // the numbers for balances don't do real math, they need not match either the condition for + // the payment adjustment or the actual values that come from the payable size reducing + // algorithm: all that is mocked in this test init_test_logging(); let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; let search_for_indispensable_adjustment_params_arc = Arc::new(Mutex::new(vec![])); @@ -1434,9 +1451,20 @@ mod tests { let system = System::new("test"); let expected_agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); - let accounts = vec![account_1, account_2]; + let qualified_payables = vec![ + QualifiedPayableAccount { + payable: account_1.clone(), + payment_threshold_intercept: 2345, + }, + QualifiedPayableAccount { + payable: account_2.clone(), + payment_threshold_intercept: 6789, + }, + ]; let msg = BlockchainAgentWithContextMessage { - protected_qualified_payables: protect_payables_in_test(accounts.clone()), + protected_qualified_payables: protect_qualified_payables_in_test( + qualified_payables.clone(), + ), agent: Box::new(agent), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1453,13 +1481,16 @@ mod tests { .unwrap(); let (actual_qualified_payables, actual_agent_id_stamp) = search_for_indispensable_adjustment_params.remove(0); - assert_eq!(actual_qualified_payables, accounts.clone()); + assert_eq!(actual_qualified_payables, qualified_payables); assert_eq!(actual_agent_id_stamp, expected_agent_id_stamp); assert!(search_for_indispensable_adjustment_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); let payments_instructions = blockchain_bridge_recording.get_record::(0); - assert_eq!(payments_instructions.affordable_accounts, accounts); + assert_eq!( + payments_instructions.affordable_accounts, + vec![account_1, account_2] + ); assert_eq!( payments_instructions.response_skeleton_opt, Some(ResponseSkeleton { @@ -1489,15 +1520,21 @@ mod tests { .start() .recipient(); let mut subject = AccountantBuilder::default().build(); - let unadjusted_account_1 = QualifiedPayableAccount{account: make_payable_account(111_111), payment_threshold_intercept: 1234567}; - let unadjusted_account_2 = QualifiedPayableAccount{account: make_payable_account(222_222), payment_threshold_intercept: 444555666}; + let unadjusted_account_1 = QualifiedPayableAccount { + payable: make_payable_account(111_111), + payment_threshold_intercept: 1234567, + }; + let unadjusted_account_2 = QualifiedPayableAccount { + payable: make_payable_account(222_222), + payment_threshold_intercept: 444555666, + }; let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1.account.clone() + ..unadjusted_account_1.payable.clone() }; let adjusted_account_2 = PayableAccount { balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2.account.clone() + ..unadjusted_account_2.payable.clone() }; let response_skeleton = ResponseSkeleton { client_id: 12, @@ -1508,7 +1545,9 @@ mod tests { let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp_first_phase); let payable_payments_setup_msg = BlockchainAgentWithContextMessage { - protected_qualified_payables: protect_payables_in_test(unadjusted_qualified_accounts.clone()), + protected_qualified_payables: protect_qualified_payables_in_test( + unadjusted_qualified_accounts.clone(), + ), agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; @@ -1607,10 +1646,15 @@ mod tests { let scan_started_at = SystemTime::now(); subject.scanners.payable.mark_as_started(scan_started_at); let subject_addr = subject.start(); - let account_1 = make_payable_account(111_111); + let account = make_payable_account(111_111); + let qualified_payable = QualifiedPayableAccount { + payable: account, + payment_threshold_intercept: 123456, + }; let system = System::new(test_name); let agent = BlockchainAgentMock::default(); - let protected_qualified_payables = protect_payables_in_test(vec![account_1]); + let protected_qualified_payables = + protect_qualified_payables_in_test(vec![qualified_payable]); let msg = BlockchainAgentWithContextMessage::new( protected_qualified_payables, Box::new(agent), @@ -1669,7 +1713,7 @@ mod tests { .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner could not finish. \ - If mature payables stay untreated long, your creditors may impose ban on you" + If matured payables stay untreated long, your creditors may impose a ban on you" )); } @@ -1688,20 +1732,21 @@ mod tests { log_handler.exists_log_containing(&format!( "WARN: {test_name}: Payment adjustment has not produced any executable payments. Please add funds \ into your consuming wallet in order to avoid bans from your creditors. Failure reason: The adjustment \ - algorithm had to eliminate each payable due to lack of resources" + algorithm had to eliminate each payable from the recently urged payment due to lack of resources" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If mature payables stay untreated long, your \ - creditors may impose ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated long, your \ + creditors may impose a ban on you" )); } #[test] - fn payment_adjuster_error_is_not_sent_by_exclusive_msg_to_ui_if_scan_not_manually_requested() { + fn payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested() { init_test_logging(); - let test_name = "payment_adjuster_error_is_not_sent_by_exclusive_msg_to_ui_if_scan_not_manually_requested"; + let test_name = + "payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( @@ -1716,8 +1761,8 @@ mod tests { .build(); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); - let account = make_payable_account(111_111); - let protected_payables = protect_payables_in_test(vec![account]); + let qualified_payable = make_non_guaranteed_qualified_payable(111_111); + let protected_payables = protect_qualified_payables_in_test(vec![qualified_payable]); let blockchain_agent = BlockchainAgentMock::default(); let msg = BlockchainAgentWithContextMessage::new( protected_payables, @@ -1931,7 +1976,7 @@ mod tests { let now = SystemTime::now(); let payment_thresholds = PaymentThresholds::default(); let (qualified_payables, _, all_non_pending_payables) = - make_payables(now, &payment_thresholds); + make_unqualified_and_qualified_payables(now, &payment_thresholds); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let system = System::new( @@ -1960,7 +2005,9 @@ mod tests { assert_eq!( message, &QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(qualified_payables), + protected_qualified_payables: protect_qualified_payables_in_test( + qualified_payables + ), response_skeleton_opt: None, } ); @@ -2268,9 +2315,9 @@ mod tests { .begin_scan_params(&begin_scan_params_arc) .begin_scan_result(Err(BeginScanError::NothingToProcess)) .begin_scan_result(Ok(QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(vec![make_payable_account( - 123, - )]), + protected_qualified_payables: protect_qualified_payables_in_test(vec![ + make_non_guaranteed_qualified_payable(123), + ]), response_skeleton_opt: None, })) .stop_the_system_after_last_msg(); @@ -2445,37 +2492,41 @@ mod tests { payable_scan_interval: Duration::from_secs(50_000), receivable_scan_interval: Duration::from_secs(50_000), }); - let now = to_time_t(SystemTime::now()); - let qualified_payables = vec![ - // slightly above minimum balance, to the right of the curve (time intersection) + let now = SystemTime::now(); + let now_t = to_time_t(now); + let payables = vec![ + // Slightly above the minimum balance, to the right of the curve (time intersection) PayableAccount { wallet: make_wallet("wallet0"), balance_wei: gwei_to_wei( DEFAULT_PAYMENT_THRESHOLDS.permanent_debt_allowed_gwei + 1, ), last_paid_timestamp: from_time_t( - now - checked_conversion::( - DEFAULT_PAYMENT_THRESHOLDS.threshold_interval_sec - + DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec - + 10, - ), + now_t + - checked_conversion::( + DEFAULT_PAYMENT_THRESHOLDS.threshold_interval_sec + + DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + + 10, + ), ), pending_payable_opt: None, }, - // slightly above the curve (balance intersection), to the right of minimum time + // Slightly above the curve (balance intersection), to the right of the minimum time PayableAccount { wallet: make_wallet("wallet1"), balance_wei: gwei_to_wei(DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 1), last_paid_timestamp: from_time_t( - now - checked_conversion::( - DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 10, - ), + now_t + - checked_conversion::( + DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 10, + ), ), pending_payable_opt: None, }, ]; - let payable_dao = - PayableDaoMock::default().non_pending_payables_result(qualified_payables.clone()); + let qualified_payables = + make_guaranteed_qualified_payables(payables.clone(), &DEFAULT_PAYMENT_THRESHOLDS, now); + let payable_dao = PayableDaoMock::default().non_pending_payables_result(payables); let (blockchain_bridge, _, blockchain_bridge_recordings_arc) = make_recorder(); let blockchain_bridge = blockchain_bridge .system_stop_conditions(match_every_type_id!(QualifiedPayablesMessage)); @@ -2496,13 +2547,15 @@ mod tests { send_start_message!(accountant_subs); - system.run(); + assert_eq!(system.run(), 0); let blockchain_bridge_recordings = blockchain_bridge_recordings_arc.lock().unwrap(); let message = blockchain_bridge_recordings.get_record::(0); assert_eq!( message, &QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(qualified_payables), + protected_qualified_payables: protect_qualified_payables_in_test( + qualified_payables + ), response_skeleton_opt: None, } ); @@ -3371,7 +3424,7 @@ mod tests { last_paid_timestamp: past_payable_timestamp_2, pending_payable_opt: None, }; - let pending_payable_scan_interval = 200; // should be slightly less than 1/5 of the time until shutting the system + let pending_payable_scan_interval = 200; // Should be slightly less than 1/5 of the time until shutting the system let payable_dao_for_payable_scanner = PayableDaoMock::new() .non_pending_payables_params(&non_pending_payables_params_arc) .non_pending_payables_result(vec![account_1, account_2]) @@ -3452,10 +3505,10 @@ mod tests { .increment_scan_attempts_result(Ok(())) .increment_scan_attempts_result(Ok(())) .mark_failures_params(&mark_failure_params_arc) - // we don't have a better solution yet, so we mark this down + // We don't have a better solution yet, so we just mark the error down in the database .mark_failures_result(Ok(())) .delete_fingerprints_params(&delete_record_params_arc) - // this is used during confirmation of the successful one + // This is used during confirmation of the successful transaction .delete_fingerprints_result(Ok(())); pending_payable_dao_for_pending_payable_scanner .have_return_all_errorless_fingerprints_shut_down_the_system = true; @@ -3518,7 +3571,7 @@ mod tests { assert_eq!(second_payable.1, rowid_for_account_2); let return_all_errorless_fingerprints_params = return_all_errorless_fingerprints_params_arc.lock().unwrap(); - // it varies with machines and sometimes we manage more cycles than necessary + // It varies with machines, and sometimes we manage more cycles than necessary assert!(return_all_errorless_fingerprints_params.len() >= 5); let non_pending_payables_params = non_pending_payables_params_arc.lock().unwrap(); assert_eq!(*non_pending_payables_params, vec![()]); // because we disabled further scanning for payables @@ -3568,7 +3621,7 @@ mod tests { notify_later_scan_for_pending_payable_params_arc .lock() .unwrap(); - // it varies with machines and sometimes we manage more cycles than necessary + // It varies with machines, and sometimes we manage more cycles than necessary let vector_of_first_five_cycles = notify_later_check_for_confirmation .drain(0..=4) .collect_vec(); diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 512163e4d..6f0d1255c 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -7,9 +7,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; +use crate::accountant::QualifiedPayableAccount; use itertools::Either; use std::vec; -use crate::accountant::QualifiedPayableAccount; // There are just two runners. Different by the adjustment they can perform, either adjusting by // both the transaction fee and service fee, or exclusively by the transaction fee. The idea is @@ -30,7 +30,7 @@ pub trait AdjustmentRunner { fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, - last_account: PayableAccount, + last_account: QualifiedPayableAccount, ) -> Self::ReturnType; fn adjust_multiple( @@ -51,7 +51,7 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, - last_account: PayableAccount, + last_account: QualifiedPayableAccount, ) -> Self::ReturnType { Ok(Either::Left(empty_or_single_element_vector( adjust_last_one_opt(payment_adjuster, last_account), @@ -88,7 +88,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { fn adjust_last_one( &self, payment_adjuster: &PaymentAdjusterReal, - last_account: PayableAccount, + last_account: QualifiedPayableAccount, ) -> Self::ReturnType { empty_or_single_element_vector(adjust_last_one_opt(payment_adjuster, last_account)) } @@ -105,24 +105,25 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { fn adjust_last_one_opt( payment_adjuster: &PaymentAdjusterReal, - last_account: PayableAccount, + last_payable: QualifiedPayableAccount, ) -> Option { let cw_balance = payment_adjuster .inner .unallocated_cw_service_fee_balance_minor(); - let proposed_adjusted_balance = if last_account.balance_wei.checked_sub(cw_balance) == None { - last_account.balance_wei - } else { - diagnostics!( - "LAST REMAINING ACCOUNT", - "Balance adjusted to {} by exhausting the cw balance fully", - cw_balance - ); + let proposed_adjusted_balance = + if last_payable.payable.balance_wei.checked_sub(cw_balance) == None { + last_payable.payable.balance_wei + } else { + diagnostics!( + "LAST REMAINING ACCOUNT", + "Balance adjusted to {} by exhausting the cw balance fully", + cw_balance + ); - cw_balance - }; + cw_balance + }; let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - WeightedAccount::new(last_account, u128::MAX), // The weight doesn't matter really and is made up + WeightedAccount::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up proposed_adjusted_balance, )]; @@ -152,14 +153,17 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; + use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; - use crate::accountant::test_utils::make_payable_account; + use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + }; + use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; use std::fmt::Debug; use std::time::{Duration, SystemTime}; - use crate::accountant::QualifiedPayableAccount; fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { let adjustment = Adjustment::ByServiceFee; @@ -176,22 +180,28 @@ mod tests { RT: Debug + PartialEq, { let now = SystemTime::now(); - let wallet_1 = make_wallet("abc"); - let account_1 = PayableAccount { - wallet: wallet_1.clone(), + let wallet = make_wallet("abc"); + let account = PayableAccount { + wallet, balance_wei: 9_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; + let qualified_payable = make_guaranteed_qualified_payables( + vec![account], + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + ) + .remove(0); let cw_balance = 8_645_123_505; let mut payment_adjuster = prepare_payment_adjuster(cw_balance, now); - let result = subject.adjust_last_one(&mut payment_adjuster, account_1.clone()); + let result = subject.adjust_last_one(&mut payment_adjuster, qualified_payable.clone()); assert_eq!( result, expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { - original_account: account_1, + original_qualified_account: qualified_payable, proposed_adjusted_balance: cw_balance, }]) ) @@ -217,36 +227,48 @@ mod tests { let now = SystemTime::now(); let account_balance = 4_500_600; let cw_balance = calculate_disqualification_edge(account_balance) + 1; - let account = PayableAccount { + let payable = PayableAccount { wallet: make_wallet("abc"), balance_wei: account_balance, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; + let qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: 111222333, + }; let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - let result = adjust_last_one_opt(&payment_adjuster, account.clone()); + let result = adjust_last_one_opt(&payment_adjuster, qualified_payable.clone()); assert_eq!( result, Some(AdjustedAccountBeforeFinalization { - original_account: account, + original_qualified_account: qualified_payable, proposed_adjusted_balance: cw_balance, }) ) } - fn test_adjust_last_one_when_disqualified(cw_balance: u128, account_balance: u128) { + fn test_adjust_last_one_when_disqualified( + cw_balance: u128, + account_balance: u128, + threshold_intercept: u128, + ) { let now = SystemTime::now(); - let account = PayableAccount { + let payable = PayableAccount { wallet: make_wallet("abc"), balance_wei: account_balance, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; + let qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: threshold_intercept, + }; let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - let result = adjust_last_one_opt(&payment_adjuster, account.clone()); + let result = adjust_last_one_opt(&payment_adjuster, qualified_payable); assert_eq!(result, None) } @@ -256,8 +278,13 @@ mod tests { { let account_balance = 4_000_444; let cw_balance = calculate_disqualification_edge(account_balance); + let payment_threshold_intercept = 3_000_000_000; - test_adjust_last_one_when_disqualified(cw_balance, account_balance) + test_adjust_last_one_when_disqualified( + cw_balance, + account_balance, + payment_threshold_intercept, + ) } #[test] @@ -265,8 +292,13 @@ mod tests { { let account_balance = 4_000_444; let cw_balance = calculate_disqualification_edge(account_balance) - 1; + let payment_threshold_intercept = 3_000_000_000; - test_adjust_last_one_when_disqualified(cw_balance, account_balance) + test_adjust_last_one_when_disqualified( + cw_balance, + account_balance, + payment_threshold_intercept, + ) } #[test] @@ -283,6 +315,8 @@ mod tests { let mut account_2 = account_1.clone(); account_2.wallet = wallet_2.clone(); let accounts = vec![account_1, account_2]; + let qualified_payables = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; @@ -290,13 +324,14 @@ mod tests { let cw_balance = 9_000_000; payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = ServiceFeeOnlyAdjustmentRunner {}; - let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(accounts); + let criteria_and_accounts = + payment_adjuster.calculate_weights_for_accounts(qualified_payables); let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); let returned_accounts = result .into_iter() - .map(|account| account.original_account.wallet) + .map(|account| account.original_qualified_account.payable.wallet) .collect::>(); assert_eq!(returned_accounts, vec![wallet_1, wallet_2]) // If the transaction fee adjustment had been available to perform, only one account would've been @@ -313,7 +348,7 @@ mod tests { #[test] fn empty_or_single_element_vector_for_some() { let account_info = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(123), + original_qualified_account: make_non_guaranteed_qualified_payable(123), proposed_adjusted_balance: 123_456_789, }; let result = empty_or_single_element_vector(Some(account_info.clone())); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 18d7cc7f7..1d4c24e83 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -3,7 +3,7 @@ pub mod age_criterion_calculator; pub mod balance_criterion_calculator; -use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::QualifiedPayableAccount; use std::fmt::{Debug, Display, Formatter}; use std::time::SystemTime; use variant_count::VariantCount; @@ -57,12 +57,16 @@ impl CalculatorInputHolder { } } -impl<'a> From<(CalculatorType, &'a PayableAccount)> for CalculatorInputHolder { - fn from((calculator_type, account): (CalculatorType, &'a PayableAccount)) -> Self { +impl<'account> From<(CalculatorType, &'account QualifiedPayableAccount)> for CalculatorInputHolder { + fn from( + (calculator_type, qualified_payable): (CalculatorType, &'account QualifiedPayableAccount), + ) -> Self { match calculator_type { - CalculatorType::DebtBalance => CalculatorInputHolder::DebtBalance(account.balance_wei), + CalculatorType::DebtBalance => { + CalculatorInputHolder::DebtBalance(qualified_payable.payable.balance_wei) + } CalculatorType::DebtAge => CalculatorInputHolder::DebtAge { - last_paid_timestamp: account.last_paid_timestamp, + last_paid_timestamp: qualified_payable.payable.last_paid_timestamp, }, } } @@ -83,6 +87,8 @@ mod tests { use crate::accountant::payment_adjuster::criteria_calculators::{ CalculatorInputHolder, CalculatorType, }; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use crate::accountant::QualifiedPayableAccount; use crate::test_utils::make_wallet; use std::panic::{catch_unwind, RefUnwindSafe}; use std::time::{Duration, SystemTime}; @@ -93,23 +99,29 @@ mod tests { let last_paid_timestamp = SystemTime::now() .checked_sub(Duration::from_secs(3)) .unwrap(); - let account = PayableAccount { + let payable = PayableAccount { wallet: make_wallet("abc"), balance_wei, last_paid_timestamp, pending_payable_opt: None, }; - + let payment_threshold_intercept = 65432; + let qualified_account = QualifiedPayableAccount { + payable, + payment_threshold_intercept, + }; let result = [CalculatorType::DebtAge, CalculatorType::DebtBalance] .into_iter() - .map(|calculator_type| CalculatorInputHolder::from((calculator_type, &account))) + .map(|calculator_type| { + CalculatorInputHolder::from((calculator_type, &qualified_account)) + }) .collect::>(); let expected = vec![ CalculatorInputHolder::DebtAge { last_paid_timestamp, }, - CalculatorInputHolder::DebtBalance(balance_wei), + CalculatorInputHolder::DebtBalance(balance_wei - payment_threshold_intercept), ]; assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); assert_eq!(result, expected); @@ -175,20 +187,23 @@ mod tests { the_only_correct_type: CalculatorType, tested_function_call_for_panics: F, ) where - F: Fn(CalculatorType, &PayableAccount) + RefUnwindSafe, + F: Fn(CalculatorType, &QualifiedPayableAccount) + RefUnwindSafe, { - let account = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 12345, - last_paid_timestamp: SystemTime::UNIX_EPOCH, - pending_payable_opt: None, - }; + let mut qualified_payable = make_non_guaranteed_qualified_payable(12345); + qualified_payable.payable.balance_wei = 2_000_000; + qualified_payable.payment_threshold_intercept = 778_899; + let difference = + qualified_payable.payable.balance_wei - qualified_payable.payment_threshold_intercept; let all_types = vec![ - (CalculatorType::DebtBalance, "balance", "DebtBalance(12345)"), + ( + CalculatorType::DebtBalance, + "balance", + format!("DebtBalance({difference})"), + ), ( CalculatorType::DebtAge, "age", - "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }", + "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }".to_string(), ), ]; @@ -204,9 +219,10 @@ mod tests { .filter(|(calculator_type, _, _)| calculator_type != &the_only_correct_type) .for_each( |(calculator_type, _, debug_rendering_of_the_corresponding_input_holder)| { - let result = - catch_unwind(|| tested_function_call_for_panics(calculator_type, &account)) - .unwrap_err(); + let result = catch_unwind(|| { + tested_function_call_for_panics(calculator_type, &qualified_payable) + }) + .unwrap_err(); let panic_msg = result.downcast_ref::().unwrap(); assert_eq!( panic_msg, diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index a8ad10a7f..e28c842c1 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -95,12 +95,12 @@ pub fn collection_diagnostics( } pub mod ordinary_diagnostic_functions { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, }; + use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use thousands::Separable; @@ -108,11 +108,12 @@ pub mod ordinary_diagnostic_functions { account_info: &AdjustedAccountBeforeFinalization, ) { diagnostics!( - &account_info.original_account.wallet, + &account_info.original_qualified_account.payable.wallet, "OUTWEIGHED ACCOUNT FOUND", "Original balance: {}, proposed balance: {}", account_info - .original_account + .original_qualified_account + .payable .balance_wei .separate_with_commas(), account_info @@ -127,7 +128,11 @@ pub mod ordinary_diagnostic_functions { disqualification_edge: u128, ) { diagnostics!( - account_info.non_finalized_account.original_account.wallet, + account_info + .non_finalized_account + .original_qualified_account + .payable + .wallet, "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", "Proposed: {}, disqualification limit: {}", proposed_adjusted_balance.separate_with_commas(), @@ -142,7 +147,10 @@ pub mod ordinary_diagnostic_functions { diagnostics!( "EXHAUSTING CW ON PAYMENT", "For account {} from proposed {} to the possible maximum of {}", - non_finalized_account_info.original_account.wallet, + non_finalized_account_info + .original_qualified_account + .payable + .wallet, non_finalized_account_info.proposed_adjusted_balance, non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition ); @@ -154,18 +162,24 @@ pub mod ordinary_diagnostic_functions { diagnostics!( "FULLY EXHAUSTED CW, PASSING ACCOUNT OVER", "Account {} with original balance {} must be finalized with proposed {}", - non_finalized_account_info.original_account.wallet, - non_finalized_account_info.original_account.balance_wei, + non_finalized_account_info + .original_qualified_account + .payable + .wallet, + non_finalized_account_info + .original_qualified_account + .payable + .balance_wei, non_finalized_account_info.proposed_adjusted_balance ); } pub fn proposed_adjusted_balance_diagnostics( - account: &PayableAccount, + account: &QualifiedPayableAccount, proposed_adjusted_balance: u128, ) { diagnostics!( - &account.wallet, + &account.payable.wallet, "PROPOSED ADJUSTED BALANCE", "{}", proposed_adjusted_balance.separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index a7337db6f..a23421110 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -127,9 +127,13 @@ pub fn info_log_for_disqualified_account( "Shortage of MASQ in your consuming wallet impacts on payable {}, ruled out from this \ round of payments. The proposed adjustment {} wei was less than half of the recorded \ debt, {} wei", - account.original_account.wallet, + account.original_qualified_account.payable.wallet, account.proposed_adjusted_balance.separate_with_commas(), - account.original_account.balance_wei.separate_with_commas() + account + .original_qualified_account + .payable + .balance_wei + .separate_with_commas() ) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index c9b6a2b4e..d3aecb04f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,16 +1,19 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::QualifiedPayableAccount; #[derive(Clone)] pub struct WeightedAccount { - pub account: PayableAccount, + pub qualified_account: QualifiedPayableAccount, pub weight: u128, } impl WeightedAccount { - pub fn new(account: PayableAccount, weight: u128) -> Self { - Self { account, weight } + pub fn new(qualified_account: QualifiedPayableAccount, weight: u128) -> Self { + Self { + qualified_account, + weight, + } } } @@ -19,7 +22,7 @@ pub enum AdjustmentIterationResult { AllAccountsProcessed(Vec), SpecialTreatmentRequired { case: RequiredSpecialTreatment, - remaining_undecided_accounts: Vec, + remaining_undecided_accounts: Vec, }, } @@ -55,14 +58,14 @@ pub enum RequiredSpecialTreatment { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { - pub original_account: PayableAccount, + pub original_qualified_account: QualifiedPayableAccount, pub proposed_adjusted_balance: u128, } impl AdjustedAccountBeforeFinalization { - pub fn new(original_account: PayableAccount, proposed_adjusted_balance: u128) -> Self { + pub fn new(original_account: QualifiedPayableAccount, proposed_adjusted_balance: u128) -> Self { Self { - original_account, + original_qualified_account: original_account, proposed_adjusted_balance, } } @@ -78,7 +81,7 @@ impl UnconfirmedAdjustment { pub fn new(weighted_account: WeightedAccount, proposed_adjusted_balance: u128) -> Self { Self { non_finalized_account: AdjustedAccountBeforeFinalization::new( - weighted_account.account, + weighted_account.qualified_account, proposed_adjusted_balance, ), weight: weighted_account.weight, @@ -138,20 +141,20 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, }; - use crate::accountant::test_utils::make_payable_account; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; #[test] fn merging_results_from_recursion_works() { let non_finalized_account_1 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(111), + original_qualified_account: make_non_guaranteed_qualified_payable(111), proposed_adjusted_balance: 1234, }; let non_finalized_account_2 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(222), + original_qualified_account: make_non_guaranteed_qualified_payable(222), proposed_adjusted_balance: 5555, }; let non_finalized_account_3 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(333), + original_qualified_account: make_non_guaranteed_qualified_payable(333), proposed_adjusted_balance: 6789, }; let subject = RecursionResults { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index fc3d00076..363e6f4d6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -12,6 +12,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, PercentageAccountInsignificance, UnconfirmedAdjustment, WeightedAccount, }; +use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; @@ -28,7 +29,7 @@ pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance divisor: 2, }; -pub fn found_zero_affordable_accounts( +pub fn zero_affordable_accounts_found( accounts: &Either, Vec>, ) -> bool { match accounts { @@ -92,7 +93,8 @@ pub fn resolve_possibly_outweighed_account( .proposed_adjusted_balance > current_adjustment_info .non_finalized_account - .original_account + .original_qualified_account + .payable .balance_wei { possibly_outweighed_accounts_diagnostics(¤t_adjustment_info.non_finalized_account); @@ -101,7 +103,8 @@ pub fn resolve_possibly_outweighed_account( .non_finalized_account .proposed_adjusted_balance = current_adjustment_info .non_finalized_account - .original_account + .original_qualified_account + .payable .balance_wei; outweighed.push(current_adjustment_info); @@ -154,7 +157,8 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( ) -> ConsumingWalletExhaustingStatus { if status.remainder != 0 { let balance_gap_minor = non_finalized_account - .original_account + .original_qualified_account + .payable .balance_wei .checked_sub(non_finalized_account.proposed_adjusted_balance) .unwrap_or_else(|| { @@ -162,7 +166,10 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( "Proposed balance should never be bigger than the original one. Proposed: \ {}, original: {}", non_finalized_account.proposed_adjusted_balance, - non_finalized_account.original_account.balance_wei + non_finalized_account + .original_qualified_account + .payable + .balance_wei ) }); let possible_extra_addition = if balance_gap_minor < status.remainder { @@ -192,7 +199,11 @@ pub fn try_finding_an_account_to_disqualify_in_this_iteration( let account_to_disqualify = find_account_with_smallest_weight(&disqualification_suspected_accounts); - let wallet = account_to_disqualify.original_account.wallet.clone(); + let wallet = account_to_disqualify + .original_qualified_account + .payable + .wallet + .clone(); try_finding_an_account_to_disqualify_diagnostics( &disqualification_suspected_accounts, @@ -283,12 +294,12 @@ pub fn sort_in_descendant_order_by_weights( .collect() } -pub fn isolate_accounts_from_weights( +pub fn drop_no_longer_needed_weights_away_from_accounts( weights_and_accounts: Vec, ) -> Vec { weights_and_accounts .into_iter() - .map(|weighted_account| weighted_account.account) + .map(|weighted_account| weighted_account.qualified_account.payable) .collect() } @@ -301,7 +312,8 @@ fn list_accounts_nominated_for_disqualification( let disqualification_edge = calculate_disqualification_edge( adjustment_info .non_finalized_account - .original_account + .original_qualified_account + .payable .balance_wei, ); let proposed_adjusted_balance = adjustment_info @@ -352,9 +364,15 @@ pub fn nonzero_positive(x: u128) -> u128 { } } -impl From for PayableAccount { +impl From for PayableAccount { + fn from(qualified_payable: QualifiedPayableAccount) -> Self { + qualified_payable.payable + } +} + +impl From for QualifiedPayableAccount { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).original_account + AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).original_qualified_account } } @@ -371,10 +389,16 @@ impl From for PayableAccount { balance_wei: resolution_info .non_finalized_adjustment .proposed_adjusted_balance, - ..resolution_info.non_finalized_adjustment.original_account + ..resolution_info + .non_finalized_adjustment + .original_qualified_account + .payable }, AdjustmentResolution::Revert => { - resolution_info.non_finalized_adjustment.original_account + resolution_info + .non_finalized_adjustment + .original_qualified_account + .payable } } } @@ -390,16 +414,22 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, find_account_with_smallest_weight, - found_zero_affordable_accounts, list_accounts_nominated_for_disqualification, log_10, - log_2, resolve_possibly_outweighed_account, + list_accounts_nominated_for_disqualification, log_10, log_2, + resolve_possibly_outweighed_account, try_finding_an_account_to_disqualify_in_this_iteration, weights_total, - ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, + zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, + ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, + MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, + make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, + PRESERVED_TEST_PAYMENT_THRESHOLDS, }; - use crate::accountant::test_utils::make_payable_account; + use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + make_payable_account, + }; + use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; @@ -422,16 +452,19 @@ mod tests { } #[test] - fn found_zero_affordable_accounts_returns_true_for_non_finalized_accounts() { - let result = found_zero_affordable_accounts(&Either::Left(vec![])); + fn found_zero_affordable_accounts_found_returns_true_for_non_finalized_accounts() { + let result = zero_affordable_accounts_found(&Either::Left(vec![])); assert_eq!(result, true) } #[test] - fn found_zero_affordable_accounts_returns_false_for_non_finalized_accounts() { - let result = found_zero_affordable_accounts(&Either::Left(vec![ - AdjustedAccountBeforeFinalization::new(make_payable_account(456), 1234), + fn zero_affordable_accounts_found_returns_false_for_non_finalized_accounts() { + let result = zero_affordable_accounts_found(&Either::Left(vec![ + AdjustedAccountBeforeFinalization::new( + make_non_guaranteed_qualified_payable(456), + 1234, + ), ])); assert_eq!(result, false) @@ -439,7 +472,7 @@ mod tests { #[test] fn found_zero_affordable_accounts_returns_true_for_finalized_accounts() { - let result = found_zero_affordable_accounts(&Either::Right(vec![])); + let result = zero_affordable_accounts_found(&Either::Right(vec![])); assert_eq!(result, true) } @@ -447,7 +480,7 @@ mod tests { #[test] fn found_zero_affordable_accounts_returns_false_for_finalized_accounts() { let result = - found_zero_affordable_accounts(&Either::Right(vec![make_payable_account(123)])); + zero_affordable_accounts_found(&Either::Right(vec![make_payable_account(123)])); assert_eq!(result, false) } @@ -549,7 +582,7 @@ mod tests { ); ( computed_coefficient, - weighted_account.account.wallet, + weighted_account.qualified_account.payable.wallet, weighted_account.weight, ) }) @@ -637,14 +670,9 @@ mod tests { }) } - enum ExpectedResultIdx { - First = 0, - Second = 1, - } - - fn make_unconfirmed_adjustments_by_weights_and_select_expected_result( + fn make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( weights: Vec, - idx_of_expected_result: ExpectedResultIdx, + idx_of_expected_result: usize, ) -> ( Vec, AdjustedAccountBeforeFinalization, @@ -653,22 +681,27 @@ mod tests { Vec, Option, ) = (vec![], None); - let usize_expected_idx = idx_of_expected_result as usize; let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( init, |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { let original_account = make_payable_account(actual_idx as u64); - let garbage_proposed_balance = 1_000_000_000; // Unimportant for the usages in the tests this is for; + let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; + let qualified_account = QualifiedPayableAccount { + payable: original_account, + payment_threshold_intercept: garbage_intercept, + }; + let garbage_proposed_balance = 1_000_000_000; // Same here let new_adjustment_to_be_added = UnconfirmedAdjustment::new( - WeightedAccount::new(original_account, weight), + WeightedAccount::new(qualified_account, weight), garbage_proposed_balance, ); - let expected_result_opt = - if expected_result_opt_so_far.is_none() && actual_idx == usize_expected_idx { - Some(new_adjustment_to_be_added.non_finalized_account.clone()) - } else { - expected_result_opt_so_far - }; + let expected_result_opt = if expected_result_opt_so_far.is_none() + && actual_idx == idx_of_expected_result + { + Some(new_adjustment_to_be_added.non_finalized_account.clone()) + } else { + expected_result_opt_so_far + }; adjustments_so_far.push(new_adjustment_to_be_added); (adjustments_so_far, expected_result_opt) }, @@ -692,10 +725,11 @@ mod tests { #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { + let idx_of_expected_result = 1; let (adjustments, expected_result) = - make_unconfirmed_adjustments_by_weights_and_select_expected_result( + make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( vec![1004, 1000, 1002, 1001], - ExpectedResultIdx::Second, + idx_of_expected_result, ); let referenced_unconfirmed_adjustments = by_reference(&adjustments); @@ -705,11 +739,12 @@ mod tests { } #[test] - fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_it_bumped_into() { + fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { + let idx_of_expected_result = 0; let (adjustments, expected_result) = - make_unconfirmed_adjustments_by_weights_and_select_expected_result( + make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( vec![1111, 1113, 1111], - ExpectedResultIdx::First, + idx_of_expected_result, ); let referenced_non_finalized_accounts = by_reference(&adjustments); @@ -720,14 +755,19 @@ mod tests { #[test] fn accounts_with_original_balances_equal_to_the_proposed_ones_are_not_outweighed() { + let payable = PayableAccount { + wallet: make_wallet("blah"), + balance_wei: 9_000_000_000, + last_paid_timestamp: SystemTime::now(), + pending_payable_opt: None, + }; + let qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: 1234567, + }; let unconfirmed_adjustment = UnconfirmedAdjustment { non_finalized_account: AdjustedAccountBeforeFinalization { - original_account: PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 9_000_000_000, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }, + original_qualified_account: qualified_payable, proposed_adjusted_balance: 9_000_000_000, }, weight: 123456, @@ -779,8 +819,10 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), pending_payable_opt: None, }; - let weights_and_accounts = subject - .calculate_weights_for_accounts(vec![account_1, account_2, account_3, account_4]); + let accounts = vec![account_1, account_2, account_3, account_4]; + let qualified_payables = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); @@ -798,13 +840,17 @@ mod tests { original_balance: u128, proposed_adjusted_balance: u128, ) -> AdjustedAccountBeforeFinalization { - AdjustedAccountBeforeFinalization { - original_account: PayableAccount { + let qualified_payable = QualifiedPayableAccount { + payable: PayableAccount { wallet: wallet.clone(), balance_wei: original_balance, last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, }, + payment_threshold_intercept: u128::MAX, // Doesn't play any importance + }; + AdjustedAccountBeforeFinalization { + original_qualified_account: qualified_payable, proposed_adjusted_balance, } } @@ -846,7 +892,7 @@ mod tests { // A seemingly irrational situation, this can happen when some of those // originally qualified payables could get disqualified. Those would free some // means that could be used for the other accounts. - // In the end, we have a final set with sub-optimal balances, despite + // In the end, we have a final set with suboptimal balances, despite // the unallocated cw balance is larger than the entire sum of the original balances // for this few resulting accounts. // We can pay every account fully, so, why did we need to call the PaymentAdjuster @@ -948,6 +994,7 @@ mod tests { fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { let account_balance = 1_000_000; let garbage_weight = 22222222; // it plays no role + let garbage_thresholds_intercept = 456789; // also no role let prepare_account = |n: u64| { let mut account = make_payable_account(n); account.balance_wei = account_balance; @@ -959,17 +1006,26 @@ mod tests { let edge = calculate_disqualification_edge(account_balance); let proposed_ok_balance = edge + 1; let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( - WeightedAccount::new(payable_account_1, garbage_weight), + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_1, garbage_thresholds_intercept), + garbage_weight, + ), proposed_ok_balance, ); let proposed_bad_balance_because_equal = edge; let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( - WeightedAccount::new(payable_account_2, garbage_weight), + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_2, garbage_thresholds_intercept), + garbage_weight, + ), proposed_bad_balance_because_equal, ); let proposed_bad_balance_because_smaller = edge - 1; let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( - WeightedAccount::new(payable_account_3, garbage_weight), + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_3, garbage_thresholds_intercept), + garbage_weight, + ), proposed_bad_balance_because_smaller, ); let unconfirmed_adjustments = vec![ @@ -989,15 +1045,17 @@ mod tests { months_of_debt_and_balances: Vec<(usize, u128)>, ) -> (Vec, Vec) { let now = SystemTime::now(); - let accounts = make_extreme_accounts(Either::Right(months_of_debt_and_balances), now); + let accounts = make_extreme_payables(Either::Right(months_of_debt_and_balances), now); let wallets_in_order = accounts .iter() .map(|account| account.wallet.clone()) .collect(); + let qualified_accounts = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let subject = make_initialized_subject(now, None, None); // The initial order is remembered because when the weight are applied the collection // also gets sorted and will not necessarily have to match the initial order - let weights_and_accounts = subject.calculate_weights_for_accounts(accounts); + let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_accounts); (weights_and_accounts, wallets_in_order) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bc57c606b..f9a6843c9 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -28,8 +28,8 @@ use crate::accountant::payment_adjuster::log_fns::{ use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, isolate_accounts_from_weights, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, found_zero_affordable_accounts}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, drop_no_longer_needed_weights_away_from_accounts, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -183,7 +183,7 @@ impl PaymentAdjusterReal { TransactionAndServiceFeeAdjustmentRunner {}, )?; - if found_zero_affordable_accounts(&processed_accounts) { + if zero_affordable_accounts_found(&processed_accounts) { return Err(PaymentAdjusterError::AllAccountsEliminated); } @@ -224,7 +224,7 @@ impl PaymentAdjusterReal { .into_iter() .next() .expect("previous if stmt must be wrong"); - return adjustment_runner.adjust_last_one(self, last_one.account); + return adjustment_runner.adjust_last_one(self, last_one); } let weights_and_accounts_sorted = @@ -274,10 +274,11 @@ impl PaymentAdjusterReal { Ok(Either::Left(adjustment_result_before_verification)) } false => { - let finalized_accounts = isolate_accounts_from_weights( - accounts_with_criteria_affordable_by_transaction_fee, - ); - Ok(Either::Right(finalized_accounts)) + let accounts_not_needing_adjustment = + drop_no_longer_needed_weights_away_from_accounts( + accounts_with_criteria_affordable_by_transaction_fee, + ); + Ok(Either::Right(accounts_not_needing_adjustment)) } } } @@ -362,22 +363,22 @@ impl PaymentAdjusterReal { fn apply_criteria( &self, criteria_calculators: Vec>, - qualified_accounts: Vec, + qualified_accounts: Vec, ) -> Vec { - let weighted_accounts = qualified_accounts.into_iter().map(|account| { + let weighted_accounts = qualified_accounts.into_iter().map(|payable| { let weight = criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { let new_criterion = Self::have_calculator_calculate_its_criterion( &**criterion_calculator, - &account, + &payable, ); let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( - &account.wallet, + &payable.payable.wallet, &**criterion_calculator, new_criterion, summed_up, @@ -386,7 +387,7 @@ impl PaymentAdjusterReal { summed_up }); - WeightedAccount::new(account, weight) + WeightedAccount::new(payable, weight) }); sort_in_descendant_order_by_weights(weighted_accounts) @@ -394,7 +395,7 @@ impl PaymentAdjusterReal { fn have_calculator_calculate_its_criterion( criterion_calculator: &dyn CriterionCalculator, - account: &PayableAccount, + account: &QualifiedPayableAccount, ) -> u128 { let calculator_type = criterion_calculator.calculator_type(); let input_holder = CalculatorInputHolder::from((calculator_type, account)); @@ -459,7 +460,7 @@ impl PaymentAdjusterReal { compute_proposed_adjusted_balance(weighted_account.weight); proposed_adjusted_balance_diagnostics( - &weighted_account.account, + &weighted_account.qualified_account, proposed_adjusted_balance, ); @@ -496,16 +497,24 @@ impl PaymentAdjusterReal { try_finding_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, logger) { let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { - account_info.non_finalized_account.original_account.wallet + account_info + .non_finalized_account + .original_qualified_account + .payable + .wallet != disqualified_account_wallet }); let remaining_reverted = remaining .map(|account_info| { - PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( - account_info.non_finalized_account, - AdjustmentResolution::Revert, - )) + //TODO maybe implement from like before + account_info + .non_finalized_account + .original_qualified_account + // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( + // account_info.non_finalized_account, + // AdjustmentResolution::Revert, + // )) }) .collect(); @@ -534,7 +543,7 @@ impl PaymentAdjusterReal { if outweighed.is_empty() { Either::Left(properly_adjusted_accounts) } else { - let remaining_undecided_accounts: Vec = + let remaining_undecided_accounts: Vec = convert_collection(properly_adjusted_accounts); let pre_processed_outweighed: Vec = convert_collection(outweighed); @@ -570,7 +579,7 @@ impl PaymentAdjusterReal { self.logger.debug_enabled().then(|| { qualified_payables .iter() - .map(|account| (account.wallet.clone(), account.balance_wei)) + .map(|payable| (payable.payable.wallet.clone(), payable.payable.balance_wei)) .collect::>() }) } @@ -640,8 +649,8 @@ impl Display for PaymentAdjusterError { ), PaymentAdjusterError::AllAccountsEliminated => write!( f, - "The adjustment algorithm had to eliminate each payable from payments \ - due to luck of resources." + "The adjustment algorithm had to eliminate each payable from the recently urged payment \ + due to lack of resources." ), } } @@ -654,14 +663,12 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustmentIterationResult, RequiredSpecialTreatment}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, weights_total}; - use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_accounts, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, - }; + use crate::accountant::payment_adjuster::test_utils::{make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; - use crate::accountant::test_utils::make_payable_account; - use crate::accountant::{gwei_to_wei, ResponseSkeleton}; + use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; + use crate::accountant::{gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; @@ -903,8 +910,8 @@ mod tests { ), ( PaymentAdjusterError::AllAccountsEliminated, - "The adjustment algorithm had to eliminate each payable from payments \ - due to luck of resources.", + "The adjustment algorithm had to eliminate each payable from the recently urged \ + payment due to lack of resources.", ), ] .into_iter() @@ -933,7 +940,11 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let qualified_payables = make_guaranteed_qualified_payables( + vec![account_1.clone(), account_2.clone(), account_3.clone()], + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + ); let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); @@ -948,7 +959,7 @@ mod tests { weighted_account.weight ); previous_weight = weighted_account.weight; - weighted_account.account + weighted_account.qualified_account.payable }) .collect::>(); assert_eq!(accounts_alone, vec![account_3, account_1, account_2]) @@ -974,7 +985,11 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1.clone(), account_2.clone()]; + let qualified_payables = make_guaranteed_qualified_payables( + vec![account_1.clone(), account_2.clone()], + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + ); let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( @@ -986,7 +1001,8 @@ mod tests { .unwrap(); // First, let's have an example of why this test is important - let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + let criteria_and_accounts = + subject.calculate_weights_for_accounts(qualified_payables.clone()); let weights_total = weights_total(&criteria_and_accounts); let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(criteria_and_accounts, weights_total); @@ -1006,10 +1022,16 @@ mod tests { // So the assertion above shows the concern true. let first_returned_account = result.remove(0); // Outweighed accounts always take the first places - assert_eq!(first_returned_account.original_account, account_2); + assert_eq!( + &first_returned_account.original_qualified_account, + &qualified_payables[1] + ); assert_eq!(first_returned_account.proposed_adjusted_balance, balance_2); let second_returned_account = result.remove(0); - assert_eq!(second_returned_account.original_account, account_1); + assert_eq!( + &second_returned_account.original_qualified_account, + &qualified_payables[0] + ); let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; let lower_limit = (upper_limit * 9) / 10; assert!( @@ -1061,14 +1083,16 @@ mod tests { pending_payable_opt: None, }; let accounts = vec![account_1.clone(), account_2]; + let mut qualified_payables = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let subject = make_initialized_subject( now, Some(consuming_wallet_balance), Some(Logger::new(test_name)), ); - let weighted_accounts = subject.calculate_weights_for_accounts(accounts); + let weighted_accounts = subject.calculate_weights_for_accounts(qualified_payables.clone()); - let result = subject.perform_adjustment_by_service_fee(weighted_accounts.clone()); + let result = subject.perform_adjustment_by_service_fee(weighted_accounts); let remaining = match result { AdjustmentIterationResult::SpecialTreatmentRequired { @@ -1078,7 +1102,8 @@ mod tests { x => panic!("we expected to see a disqualified account but got: {:?}", x), }; // We eliminated (disqualified) the other account than which was going to qualify as outweighed - assert_eq!(remaining, vec![account_1]); + let expected_account = qualified_payables.remove(0); + assert_eq!(remaining, vec![expected_account]); } #[test] @@ -1103,7 +1128,9 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2, account_3]; + let payables = vec![account_1, account_2, account_3]; + let qualified_payables = + make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1174,7 +1201,9 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(160_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let payables = vec![account_1, account_2.clone(), account_3.clone()]; + let qualified_payables = + make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1213,9 +1242,9 @@ mod tests { let now = SystemTime::now(); // Each of the 3 accounts refers to a debt sized as the entire masq token supply and being 10 years old which // generates enormously large numbers in the criteria - let qualified_payables = { + let extreme_payables = { let debt_age_in_months = vec![120, 120, 120]; - make_extreme_accounts( + make_extreme_payables( Either::Left(( debt_age_in_months, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -1223,6 +1252,11 @@ mod tests { now, ) }; + let qualified_payables = make_guaranteed_qualified_payables( + extreme_payables, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + ); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); // In turn, extremely small cw balance @@ -1279,27 +1313,40 @@ mod tests { let test_name = "qualified_accounts_count_before_equals_the_payments_count_after"; let now = SystemTime::now(); let balance_1 = 4_444_444_444_444_444_444; - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), - pending_payable_opt: None, + let qualified_account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: balance_1, + last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let balance_2 = 6_000_000_000_000_000_000; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), - pending_payable_opt: None, + let qualified_account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("def"), + balance_wei: balance_2, + last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let balance_3 = 6_666_666_666_000_000_000; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), - pending_payable_opt: None, + let qualified_account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3.clone()]; + let qualified_payables = vec![ + qualified_account_1.clone(), + qualified_account_2.clone(), + qualified_account_3.clone(), + ]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1326,15 +1373,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, - ..account_1 + ..qualified_account_1.payable }; let account_2_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_2, - ..account_2 + ..qualified_account_2.payable }; let account_3_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_3, - ..account_3 + ..qualified_account_3.payable }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] }; @@ -1373,23 +1420,32 @@ mod tests { let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, + let account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, + let account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, + let account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); @@ -1414,7 +1470,10 @@ mod tests { // The account 3 takes the first place for its age // (it weights more if the balance is so small) - assert_eq!(result.affordable_accounts, vec![account_3, account_2]); + assert_eq!( + result.affordable_accounts, + vec![account_3.payable, account_2.payable] + ); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); let log_msg = format!( @@ -1444,23 +1503,32 @@ mod tests { // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, + let account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, + let account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, + let account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("ghk"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); @@ -1491,9 +1559,9 @@ mod tests { let expected_accounts = { let account_2_adjusted = PayableAccount { balance_wei: 222_000_000_000_000, - ..account_2 + ..account_2.payable }; - vec![account_3, account_2_adjusted] + vec![account_3.payable, account_2_adjusted] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1507,28 +1575,37 @@ mod tests { let now = SystemTime::now(); let wallet_1 = make_wallet("def"); // Account to be adjusted to keep as much as how much is left in the cw balance - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 333_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), - pending_payable_opt: None, + let account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 333_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; // Account to be outweighed and fully preserved let wallet_2 = make_wallet("abc"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 111_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), - pending_payable_opt: None, + let account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 111_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; // Account to be disqualified let wallet_3 = make_wallet("ghk"); let balance_3 = 600_000_000_000; - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), - pending_payable_opt: None, + let account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: wallet_3.clone(), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); @@ -1558,9 +1635,9 @@ mod tests { let expected_accounts = { let account_1_adjusted = PayableAccount { balance_wei: 272_000_000_000, - ..account_1 + ..account_1.payable }; - vec![account_1_adjusted, account_2] + vec![account_1_adjusted, account_2.payable] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1625,7 +1702,9 @@ mod tests { .unwrap(), pending_payable_opt: None, }; - let qualified_payables = vec![account_1, account_2]; + let payables = vec![account_1, account_2]; + let qualified_payables = + make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let mut subject = PaymentAdjusterReal::new(); let agent = { let mock = BlockchainAgentMock::default() @@ -1737,27 +1816,36 @@ mod tests { // Thrown away as the second one due to shortage of service fee, // for the proposed adjusted balance insignificance (the third account withdraws // most of the available balance from the consuming wallet for itself) - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 10_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, + let account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 10_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; // Thrown away as the first one due to shortage of transaction fee, // as it is the least significant by criteria at the moment - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 55_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, + let account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("def"), + balance_wei: 55_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let wallet_3 = make_wallet("ghi"); let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, + let account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: last_paid_timestamp_3, + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let qualified_payables = vec![account_1, account_2, account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); @@ -1817,23 +1905,32 @@ mod tests { let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); // This account is eliminated in the transaction fee cut - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, + let account_1 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 111_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, + let account_2 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("def"), + balance_wei: 333_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, + let account_3 = QualifiedPayableAccount { + payable: PayableAccount { + wallet: make_wallet("ghi"), + balance_wei: 222_000_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept: todo!(), }; let qualified_payables = vec![account_1, account_2, account_3]; let mut subject = PaymentAdjusterReal::new(); @@ -1890,7 +1987,7 @@ mod tests { fn make_test_input_for_initial_check( service_fee_balances_config_opt: Option, transaction_fee_config_opt: Option, - ) -> (Vec, Box) { + ) -> (Vec, Box) { let service_fee_balances_setup = match service_fee_balances_config_opt { Some(config) => config, None => TestConfigForServiceFeeBalances { @@ -1924,7 +2021,7 @@ mod tests { None => (120, accounts_count_from_sf_config, 55_000, u64::MAX), }; - let qualified_payables: Vec<_> = + let payable_accounts: Vec<_> = if accounts_count_from_tf_config != accounts_count_from_sf_config { (0..accounts_count_from_tf_config) .map(|idx| make_payable_account(idx as u64)) @@ -1940,6 +2037,12 @@ mod tests { }) .collect() }; + let now = SystemTime::now(); + let qualified_payables = make_guaranteed_qualified_payables( + payable_accounts, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + ); let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( estimated_limit_for_transaction_fee_units_per_transaction diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs index 899260c07..6315e6661 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs @@ -12,10 +12,10 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ }; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::QualifiedPayableAccount; use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; -use crate::accountant::QualifiedPayableAccount; pub struct PreAdjustmentAnalyzer {} @@ -92,10 +92,13 @@ impl PreAdjustmentAnalyzer { cw_service_fee_balance_minor: u128, ) -> Result { let qualified_payables: Vec<&PayableAccount> = match payables { - Either::Left(accounts) => accounts.iter().map(|qualified_payable|&qualified_payable.account).collect(), + Either::Left(accounts) => accounts + .iter() + .map(|qualified_payable| &qualified_payable.payable) + .collect(), Either::Right(weighted_accounts) => weighted_accounts .iter() - .map(|weighted_account| &weighted_account.account) + .map(|weighted_account| &weighted_account.qualified_account.payable) .collect(), }; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 3aeea3605..0d0d94820 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -3,9 +3,13 @@ #![cfg(test)] use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::criteria_calculators::{ + CalculatorInputHolder, CalculatorType, CriterionCalculator, +}; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::PreAdjustmentAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use itertools::Either; use lazy_static::lazy_static; @@ -37,7 +41,7 @@ pub fn make_initialized_subject( } } -pub fn make_extreme_accounts( +pub fn make_extreme_payables( months_of_debt_and_balance_minor: Either<(Vec, u128), Vec<(usize, u128)>>, now: SystemTime, ) -> Vec { @@ -64,6 +68,16 @@ pub fn make_extreme_accounts( .collect() } +pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHOLDS: + PaymentThresholds = PaymentThresholds { + debt_threshold_gwei: 2_000_000, + maturity_threshold_sec: 1_000, + payment_grace_period_sec: 1_000, + permanent_debt_allowed_gwei: 1_000_000, + threshold_interval_sec: 1_000_000, + unban_below_gwei: 1_000_000, +}; + pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( constants_and_expected_values: &[(i128, i128)], expected_num_sum: i128, diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 9d1bae6e8..68db68e08 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -6,7 +6,6 @@ pub mod blockchain_agent; pub mod msgs; pub mod test_utils; -use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::Adjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 42f1c4129..e40aec3b2 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -7,17 +7,11 @@ pub mod test_utils; use crate::accountant::db_access_objects::payable_dao::{PayableAccount, PayableDao}; use crate::accountant::db_access_objects::pending_payable_dao::{PendingPayable, PendingPayableDao}; use crate::accountant::db_access_objects::receivable_dao::ReceivableDao; -use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterReal}; +use crate::accountant::payment_adjuster::{PaymentAdjuster, PaymentAdjusterReal}; use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; -use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ - debugging_summary_after_error_separation, err_msg_if_failed_without_existing_fingerprints, - investigate_debt_extremes, mark_pending_payable_fatal_error, payables_debug_summary, - separate_errors, separate_rowids_and_hashes, PayableThresholdsGauge, - PayableThresholdsGaugeReal, PayableTransactingErrorEnum, PendingPayableMetadata, - VecOfRowidOptAndHash, -}; +use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{debugging_summary_after_error_separation, err_msg_if_failed_without_existing_fingerprints, investigate_debt_extremes, mark_pending_payable_fatal_error, payables_debug_summary, separate_errors, separate_rowids_and_hashes, PayableThresholdsGaugeReal, PayableTransactingErrorEnum, PendingPayableMetadata, VecOfRowidOptAndHash, PayableInspector}; use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::{ elapsed_in_ms, handle_none_status, handle_status_with_failure, handle_status_with_success, PendingPayableScanReport, @@ -25,7 +19,7 @@ use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils:: use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; use crate::accountant::{PendingPayableId, QualifiedPayableAccount}; use crate::accountant::{ - comma_joined_stringifiable, gwei_to_wei, Accountant, ReceivedPayments, + comma_joined_stringifiable, Accountant, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayables, }; @@ -57,7 +51,6 @@ use time::OffsetDateTime; use web3::types::{TransactionReceipt, H256}; use masq_lib::type_obfuscation::Obfuscated; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::{PreparedAdjustment, MultistagePayableScanner, SolvencySensitivePaymentInstructor}; -use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{BlockchainAgentWithContextMessage, QualifiedPayablesMessage}; use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; @@ -80,6 +73,7 @@ impl Scanners { dao_factories.payable_dao_factory.make(), dao_factories.pending_payable_dao_factory.make(), Rc::clone(&payment_thresholds), + PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())), Box::new(PaymentAdjusterReal::new()), )), pending_payable: Box::new(PendingPayableScanner::new( @@ -121,6 +115,8 @@ where pub struct ScannerCommon { initiated_at_opt: Option, + // TODO The thresholds probably shouldn't be in common because + // the PendingPayableScanner does not need it pub payment_thresholds: Rc, } @@ -179,7 +175,7 @@ pub struct PayableScanner { pub common: ScannerCommon, pub payable_dao: Box, pub pending_payable_dao: Box, - pub payable_threshold_gauge: Box, + pub payable_inspector: PayableInspector, pub payment_adjuster: RefCell>, } @@ -267,22 +263,26 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { .borrow() .search_for_indispensable_adjustment(&unprotected, &*msg.agent) { - Ok(adjustment_opt) => - { - let either = match adjustment_opt { - None => Either::Left(OutboundPaymentsInstructions::new( - Either::Left(unprotected), + Ok(adjustment_opt) => { + let either = match adjustment_opt { + None => Either::Left(OutboundPaymentsInstructions::new( + Either::Left(unprotected), + msg.agent, + msg.response_skeleton_opt, + )), + Some(adjustment) => { + let prepared_adjustment = PreparedAdjustment::new( + unprotected, msg.agent, msg.response_skeleton_opt, - )), - Some(adjustment) => { - let prepared_adjustment = - PreparedAdjustment::new(unprotected, msg.agent, msg.response_skeleton_opt, adjustment); - Either::Right(prepared_adjustment) - } - }; - - Some(either)}, + adjustment, + ); + Either::Right(prepared_adjustment) + } + }; + + Some(either) + } Err(e) => { warning!( logger, @@ -332,13 +332,14 @@ impl PayableScanner { payable_dao: Box, pending_payable_dao: Box, payment_thresholds: Rc, + payable_inspector: PayableInspector, payment_adjuster: Box, ) -> Self { Self { common: ScannerCommon::new(payment_thresholds), payable_dao, pending_payable_dao, - payable_threshold_gauge: Box::new(PayableThresholdsGaugeReal::default()), + payable_inspector, payment_adjuster: RefCell::new(payment_adjuster), } } @@ -348,13 +349,16 @@ impl PayableScanner { non_pending_payables: Vec, logger: &Logger, ) -> Vec { - let qualified_payables = - non_pending_payables.into_iter().flat_map(|account| { - // TODO this SystemTime::now() isn't tested probably - self.payable_exceeded_threshold(&account, SystemTime::now()) - .map(|payment_threshold_intercept| QualifiedPayableAccount {account, - payment_threshold_intercept}) - }).collect(); + let qualified_payables = non_pending_payables + .into_iter() + .flat_map(|account| { + self.payable_exceeded_threshold(&account, SystemTime::now()) + .map(|payment_threshold_intercept| QualifiedPayableAccount { + payable: account, + payment_threshold_intercept, + }) + }) + .collect(); match logger.debug_enabled() { false => qualified_payables, true => { @@ -366,40 +370,16 @@ impl PayableScanner { fn payable_exceeded_threshold( &self, - payable: &PayableAccount, + account: &PayableAccount, now: SystemTime, ) -> Option { - let debt_age = now - .duration_since(payable.last_paid_timestamp) - .expect("Internal error") - .as_secs(); - - if self.payable_threshold_gauge.is_innocent_age( - debt_age, - self.common.payment_thresholds.maturity_threshold_sec, - ) { - return None; - } - - if self.payable_threshold_gauge.is_innocent_balance( - payable.balance_wei, - gwei_to_wei(self.common.payment_thresholds.permanent_debt_allowed_gwei), - ) { - return None; - } - - let threshold = self - .payable_threshold_gauge - .calculate_payout_threshold_in_gwei(&self.common.payment_thresholds, debt_age); - - if payable.balance_wei > threshold { - Some(threshold) - } else { - None - } + self.payable_inspector.payable_exceeded_threshold( + account, + &self.common.payment_thresholds, + now, + ) } - fn separate_existent_and_nonexistent_fingerprints<'a>( &'a self, sent_payables: &'a [&'a PendingPayable], @@ -1097,24 +1077,31 @@ mod tests { use crate::accountant::db_access_objects::pending_payable_dao::{ PendingPayable, PendingPayableDaoError, }; - use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; + use crate::accountant::db_access_objects::utils::{from_time_t, now_time_t, to_time_t}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; - use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PendingPayableMetadata; + use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ + PayableInspector, PayableThresholdsGauge, PayableThresholdsGaugeReal, + PendingPayableMetadata, + }; use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils::PendingPayableScanReport; - use crate::accountant::scanners::test_utils::protect_payables_in_test; + use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::scanners::{ BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, ScanSchedulers, Scanner, ScannerCommon, Scanners, }; use crate::accountant::test_utils::{ - make_custom_payment_thresholds, make_payable_account, make_payables, - make_pending_payable_fingerprint, make_receivable_account, BannedDaoFactoryMock, + make_custom_payment_thresholds, make_payable_account, make_pending_payable_fingerprint, + make_receivable_account, make_unqualified_and_qualified_payables, BannedDaoFactoryMock, BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, PendingPayableScannerBuilder, ReceivableDaoFactoryMock, ReceivableDaoMock, ReceivableScannerBuilder, }; - use crate::accountant::{gwei_to_wei, PendingPayableId, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, QualifiedPayableAccount}; + use crate::accountant::{ + gwei_to_wei, PendingPayableId, QualifiedPayableAccount, ReceivedPayments, + ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, + DEFAULT_PENDING_TOO_LONG_SEC, + }; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; use crate::blockchain::blockchain_interface::data_structures::errors::PayableTransactionError; use crate::blockchain::blockchain_interface::data_structures::{ @@ -1138,6 +1125,7 @@ mod tests { use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; + use sysinfo::Signal::Sys; use web3::types::{TransactionReceipt, H256}; use web3::Error; @@ -1233,7 +1221,16 @@ mod tests { #[test] fn protected_payables_can_be_cast_from_and_back_to_vec_of_payable_accounts_by_payable_scanner() { - let initial_unprotected = vec![QualifiedPayableAccount{account: make_payable_account(123), payment_threshold_intercept: 123456789}, QualifiedPayableAccount{account: make_payable_account(456), payment_threshold_intercept: 987654321}]; + let initial_unprotected = vec![ + QualifiedPayableAccount { + payable: make_payable_account(123), + payment_threshold_intercept: 123456789, + }, + QualifiedPayableAccount { + payable: make_payable_account(456), + payment_threshold_intercept: 987654321, + }, + ]; let subject = PayableScannerBuilder::new().build(); let protected = subject.protect_payables(initial_unprotected.clone()); @@ -1248,7 +1245,7 @@ mod tests { let test_name = "payable_scanner_can_initiate_a_scan"; let now = SystemTime::now(); let (qualified_payable_accounts, _, all_non_pending_payables) = - make_payables(now, &PaymentThresholds::default()); + make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() @@ -1262,7 +1259,7 @@ mod tests { assert_eq!( result, Ok(QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test( + protected_qualified_payables: protect_qualified_payables_in_test( qualified_payable_accounts.clone() ), response_skeleton_opt: None, @@ -1280,7 +1277,8 @@ mod tests { #[test] fn payable_scanner_throws_error_when_a_scan_is_already_running() { let now = SystemTime::now(); - let (_, _, all_non_pending_payables) = make_payables(now, &PaymentThresholds::default()); + let (_, _, all_non_pending_payables) = + make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() @@ -1302,7 +1300,7 @@ mod tests { fn payable_scanner_throws_error_in_case_no_qualified_payable_is_found() { let now = SystemTime::now(); let (_, unqualified_payable_accounts, _) = - make_payables(now, &PaymentThresholds::default()); + make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); let mut subject = PayableScannerBuilder::new() @@ -1932,7 +1930,7 @@ mod tests { .is_innocent_age_params(&is_innocent_age_params_arc) .is_innocent_age_result(true); let mut subject = PayableScannerBuilder::new().build(); - subject.payable_threshold_gauge = Box::new(payable_thresholds_gauge); + subject.payable_inspector = PayableInspector::new(Box::new(payable_thresholds_gauge)); let now = SystemTime::now(); let debt_age_s = 111_222; let last_paid_timestamp = now.checked_sub(Duration::from_secs(debt_age_s)).unwrap(); @@ -1963,7 +1961,7 @@ mod tests { .is_innocent_balance_params(&is_innocent_balance_params_arc) .is_innocent_balance_result(true); let mut subject = PayableScannerBuilder::new().build(); - subject.payable_threshold_gauge = Box::new(payable_thresholds_gauge); + subject.payable_inspector = PayableInspector::new(Box::new(payable_thresholds_gauge)); let now = SystemTime::now(); let debt_age_s = 3_456; let last_paid_timestamp = now.checked_sub(Duration::from_secs(debt_age_s)).unwrap(); @@ -2014,19 +2012,17 @@ mod tests { }; let payable_thresholds_gauge = PayableThresholdsGaugeMock::default() .is_innocent_age_params(&is_innocent_age_params_arc) - .is_innocent_age_result( - debt_age_s <= custom_payment_thresholds.maturity_threshold_sec as u64, - ) + .is_innocent_age_result(debt_age_s <= custom_payment_thresholds.maturity_threshold_sec) .is_innocent_balance_params(&is_innocent_balance_params_arc) .is_innocent_balance_result( balance <= gwei_to_wei(custom_payment_thresholds.permanent_debt_allowed_gwei), ) .calculate_payout_threshold_in_gwei_params(&calculate_payable_threshold_params_arc) - .calculate_payout_threshold_in_gwei_result(4567898); //made up value + .calculate_payout_threshold_in_gwei_result(4567898); // Made up value let mut subject = PayableScannerBuilder::new() .payment_thresholds(custom_payment_thresholds) .build(); - subject.payable_threshold_gauge = Box::new(payable_thresholds_gauge); + subject.payable_inspector = PayableInspector::new(Box::new(payable_thresholds_gauge)); let result = subject.payable_exceeded_threshold(&payable_account, now); @@ -2037,7 +2033,7 @@ mod tests { assert_eq!(debt_age_returned_innocent, debt_age_s); assert_eq!( curve_derived_time, - custom_payment_thresholds.maturity_threshold_sec as u64 + custom_payment_thresholds.maturity_threshold_sec ); let is_innocent_balance_params = is_innocent_balance_params_arc.lock().unwrap(); assert_eq!( @@ -2088,14 +2084,15 @@ mod tests { fn payable_with_debt_above_the_slope_is_qualified() { init_test_logging(); let payment_thresholds = PaymentThresholds::default(); + let now = SystemTime::now(); let debt = gwei_to_wei(payment_thresholds.debt_threshold_gwei - 1); - let time = (payment_thresholds.maturity_threshold_sec + let debt_age = (payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec - 1) as i64; - let qualifiable_payable = PayableAccount { + let payable = PayableAccount { wallet: make_wallet("wallet0"), balance_wei: debt, - last_paid_timestamp: from_time_t(time), + last_paid_timestamp: from_time_t(to_time_t(now) - debt_age), pending_payable_opt: None, }; let subject = PayableScannerBuilder::new() @@ -2104,19 +2101,21 @@ mod tests { let test_name = "payable_with_debt_above_the_slope_is_qualified"; let logger = Logger::new(test_name); - let result = subject.sniff_out_alarming_payables_and_maybe_log_them( - vec![qualifiable_payable.clone()], - &logger, - ); - - assert_eq!(result.len(),1); - assert_eq!(&result[0].account, &qualifiable_payable); - todo!("see if you can assert against exactly computed value"); - //assert!(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei) <= result[0].payment_threshold_intercept && result[0].payment_threshold_intercept <= gwei_to_wei((payment_thresholds.permanent_debt_allowed_gwei * 100001) / 100000)); + let result = + subject.sniff_out_alarming_payables_and_maybe_log_them(vec![payable.clone()], &logger); + + assert_eq!(result.len(), 1); + let expected_intercept = PayableThresholdsGaugeReal::default() + .calculate_payout_threshold_in_gwei(&payment_thresholds, debt_age as u64); + let expected_qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: expected_intercept, + }; + assert_eq!(&result[0], &expected_qualified_payable); TestLogHandler::new().exists_log_matching(&format!( "DEBUG: {}: Paying qualified debts:\n999,999,999,000,000,\ - 000 wei owed for \\d+ sec exceeds threshold: 500,000,000,000,000,000 wei; creditor: \ - 0x0000000000000000000000000077616c6c657430", + 000 wei owed for \\d+ sec exceeds threshold: 500,023,148,148,151,\\d{{3}} wei; \ + creditor: 0x0000000000000000000000000077616c6c657430", test_name )); } @@ -2148,6 +2147,51 @@ mod tests { .exists_no_log_containing(&format!("DEBUG: {test_name}: Paying qualified debts")); } + #[test] + fn sniff_out_alarming_payables_and_maybe_log_them_generates_and_uses_correct_timestamp() { + let payment_thresholds = PaymentThresholds { + debt_threshold_gwei: 10_000_000_000, + maturity_threshold_sec: 100, + payment_grace_period_sec: 0, + permanent_debt_allowed_gwei: 1_000_000_000, + threshold_interval_sec: 1_000, + unban_below_gwei: 0, + }; + let wallet = make_wallet("abc"); + // It is important to have a payable matching the declining part of the thresholds, + // also it will be more believable if the slope is steep because then one second can make + // the bigger difference in the intercept value, which is the value this test compare to + // conclude a pass + let debt_age = payment_thresholds.maturity_threshold_sec + + (payment_thresholds.threshold_interval_sec / 2); + let payable = PayableAccount { + wallet: wallet.clone(), + balance_wei: gwei_to_wei(12_000_000_000_u64), + last_paid_timestamp: from_time_t(now_time_t() - debt_age as i64), + pending_payable_opt: None, + }; + let subject = PayableScannerBuilder::new() + .payment_thresholds(payment_thresholds) + .build(); + let intercept_before = subject + .payable_exceeded_threshold(&payable, SystemTime::now()) + .unwrap(); + + let result = subject.sniff_out_alarming_payables_and_maybe_log_them( + vec![payable.clone()], + &Logger::new("test"), + ); + + let intercept_after = subject + .payable_exceeded_threshold(&payable, SystemTime::now()) + .unwrap(); + assert_eq!(result.len(), 1); + assert_eq!(&result[0].payable.wallet, &wallet); + assert!(intercept_before >= result[0].payment_threshold_intercept && result[0].payment_threshold_intercept >= intercept_after, + "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept, intercept_before, intercept_after + ) + } + #[test] fn pending_payable_scanner_can_initiate_a_scan() { init_test_logging(); diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index fc00fdf86..ca3ae463c 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -6,7 +6,7 @@ pub mod payable_scanner_utils { use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableTransactingErrorEnum::{ LocallyCausedError, RemotelyCausedErrors, }; - use crate::accountant::{comma_joined_stringifiable, ProcessedPayableFallible, QualifiedPayableAccount, SentPayables}; + use crate::accountant::{comma_joined_stringifiable, gwei_to_wei, ProcessedPayableFallible, QualifiedPayableAccount, SentPayables}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; @@ -177,7 +177,7 @@ pub mod payable_scanner_utils { qualified_accounts .iter() .map(|qualified_account| { - let account = &qualified_account.account; + let account = &qualified_account.payable; let p_age = now .duration_since(account.last_paid_timestamp) .expect("Payable time is corrupt"); @@ -185,7 +185,9 @@ pub mod payable_scanner_utils { "{} wei owed for {} sec exceeds threshold: {} wei; creditor: {}", account.balance_wei.separate_with_commas(), p_age.as_secs(), - qualified_account.payment_threshold_intercept.separate_with_commas(), + qualified_account + .payment_threshold_intercept + .separate_with_commas(), account.wallet ) }) @@ -284,6 +286,53 @@ pub mod payable_scanner_utils { .unzip() } + pub struct PayableInspector { + payable_threshold_gauge: Box, + } + + impl PayableInspector { + pub fn new(payable_threshold_gauge: Box) -> Self { + Self { + payable_threshold_gauge, + } + } + pub fn payable_exceeded_threshold( + &self, + payable: &PayableAccount, + payment_thresholds: &PaymentThresholds, + now: SystemTime, + ) -> Option { + let debt_age = now + .duration_since(payable.last_paid_timestamp) + .expect("Internal error") + .as_secs(); + + if self + .payable_threshold_gauge + .is_innocent_age(debt_age, payment_thresholds.maturity_threshold_sec) + { + return None; + } + + if self.payable_threshold_gauge.is_innocent_balance( + payable.balance_wei, + gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei), + ) { + return None; + } + + let threshold = self + .payable_threshold_gauge + .calculate_payout_threshold_in_gwei(payment_thresholds, debt_age); + + if payable.balance_wei > threshold { + Some(threshold) + } else { + None + } + } + } + pub trait PayableThresholdsGauge { fn is_innocent_age(&self, age: u64, limit: u64) -> bool; fn is_innocent_balance(&self, balance: u128, limit: u128) -> bool; @@ -440,7 +489,7 @@ mod tests { PayableThresholdsGaugeReal, }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; - use crate::accountant::{checked_conversion, gwei_to_wei, SentPayables}; + use crate::accountant::{checked_conversion, gwei_to_wei, QualifiedPayableAccount, SentPayables}; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; @@ -609,8 +658,8 @@ mod tests { unban_below_gwei: 10_000_000, }; let qualified_payables_and_threshold_points = vec![ - ( - PayableAccount { + QualifiedPayableAccount { + payable: PayableAccount { wallet: make_wallet("wallet0"), balance_wei: gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei + 2000), last_paid_timestamp: from_time_t( @@ -621,10 +670,10 @@ mod tests { ), pending_payable_opt: None, }, - 10_000_000_001_152_000_u128, - ), - ( - PayableAccount { + payment_threshold_intercept: 10_000_000_001_152_000_u128, + }, + QualifiedPayableAccount { + payable: PayableAccount { wallet: make_wallet("wallet1"), balance_wei: gwei_to_wei(payment_thresholds.debt_threshold_gwei - 1), last_paid_timestamp: from_time_t( @@ -634,8 +683,8 @@ mod tests { ), pending_payable_opt: None, }, - 999_978_993_055_555_580, - ), + payment_threshold_intercept: 999_978_993_055_555_580, + }, ]; let logger = Logger::new("test"); diff --git a/node/src/accountant/scanners/test_utils.rs b/node/src/accountant/scanners/test_utils.rs index c7423e7a8..e9f9a3194 100644 --- a/node/src/accountant/scanners/test_utils.rs +++ b/node/src/accountant/scanners/test_utils.rs @@ -2,9 +2,9 @@ #![cfg(test)] -use masq_lib::type_obfuscation::Obfuscated; use crate::accountant::QualifiedPayableAccount; +use masq_lib::type_obfuscation::Obfuscated; -pub fn protect_payables_in_test(payables: Vec) -> Obfuscated { +pub fn protect_qualified_payables_in_test(payables: Vec) -> Obfuscated { Obfuscated::obfuscate_vector(payables) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index a4dfc53a5..b2203a0f9 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -21,12 +21,17 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::{ MultistagePayableScanner, PreparedAdjustment, SolvencySensitivePaymentInstructor, }; -use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGauge; +use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ + PayableInspector, PayableThresholdsGauge, PayableThresholdsGaugeReal, +}; use crate::accountant::scanners::{ BeginScanError, PayableScanner, PendingPayableScanner, PeriodicalScanScheduler, ReceivableScanner, ScanSchedulers, Scanner, }; -use crate::accountant::{gwei_to_wei, Accountant, ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, QualifiedPayableAccount}; +use crate::accountant::{ + gwei_to_wei, Accountant, QualifiedPayableAccount, ResponseSkeleton, SentPayables, + DEFAULT_PENDING_TOO_LONG_SEC, +}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; use crate::blockchain::test_utils::make_tx_hash; @@ -1064,6 +1069,7 @@ pub struct PayableScannerBuilder { payable_dao: PayableDaoMock, pending_payable_dao: PendingPayableDaoMock, payment_thresholds: PaymentThresholds, + payable_inspector: PayableInspector, payment_adjuster: PaymentAdjusterMock, } @@ -1073,6 +1079,9 @@ impl PayableScannerBuilder { payable_dao: PayableDaoMock::new(), pending_payable_dao: PendingPayableDaoMock::new(), payment_thresholds: PaymentThresholds::default(), + payable_inspector: PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + )), payment_adjuster: PaymentAdjusterMock::default(), } } @@ -1095,6 +1104,11 @@ impl PayableScannerBuilder { self } + pub fn payable_inspector(mut self, payable_inspector: PayableInspector) -> Self { + self.payable_inspector = payable_inspector; + self + } + pub fn pending_payable_dao( mut self, pending_payable_dao: PendingPayableDaoMock, @@ -1108,6 +1122,7 @@ impl PayableScannerBuilder { Box::new(self.payable_dao), Box::new(self.pending_payable_dao), Rc::new(self.payment_thresholds), + self.payable_inspector, Box::new(self.payment_adjuster), ) } @@ -1230,11 +1245,11 @@ pub fn make_pending_payable_fingerprint() -> PendingPayableFingerprint { } } -pub fn make_payables( +pub fn make_unqualified_and_qualified_payables( now: SystemTime, payment_thresholds: &PaymentThresholds, ) -> ( - Vec, + Vec, Vec, Vec, ) { @@ -1246,7 +1261,7 @@ pub fn make_payables( ), pending_payable_opt: None, }]; - let qualified_payable_accounts = vec![ + let payable_accounts_to_qualify = vec![ PayableAccount { wallet: make_wallet("wallet2"), balance_wei: gwei_to_wei( @@ -1268,9 +1283,14 @@ pub fn make_payables( pending_payable_opt: None, }, ]; + let qualified_payable_accounts = make_guaranteed_qualified_payables( + payable_accounts_to_qualify.clone(), + payment_thresholds, + now, + ); let mut all_non_pending_payables = Vec::new(); - all_non_pending_payables.extend(qualified_payable_accounts.clone()); + all_non_pending_payables.extend(payable_accounts_to_qualify); all_non_pending_payables.extend(unqualified_payable_accounts.clone()); ( @@ -1439,7 +1459,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { impl PaymentAdjusterMock { pub fn search_for_indispensable_adjustment_params( mut self, - params: &Arc, ArbitraryIdStamp)>>>, + params: &Arc, ArbitraryIdStamp)>>>, ) -> Self { self.search_for_indispensable_adjustment_params = params.clone(); self @@ -1667,7 +1687,23 @@ impl ScanSchedulers { } } -pub fn make_qualified_payables(payment_thresholds: PaymentThresholds, payables: Vec)->Vec{ - sort this out - payables.into_iter().map(||) +pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount { + // Without guarantee that the generated payable would cross the given thresholds + QualifiedPayableAccount { + payable: make_payable_account(n), + payment_threshold_intercept: n as u128 * 12345, + } +} + +pub fn make_guaranteed_qualified_payables( + payables: Vec, + payment_thresholds: &PaymentThresholds, + now: SystemTime, +) -> Vec { + let payable_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); + payables.into_iter().map(|payable|{ + let payment_threshold_intercept = payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) + .unwrap_or_else(||panic!("You intend to create qualified payables but their paramters not always make them qualify as in: {:?}", payable)); + QualifiedPayableAccount{ payable, payment_threshold_intercept } + }).collect() } diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index b0f87d1ba..852bb2ffd 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -528,8 +528,10 @@ mod tests { use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; use crate::accountant::db_access_objects::utils::from_time_t; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::accountant::scanners::test_utils::protect_payables_in_test; - use crate::accountant::test_utils::make_pending_payable_fingerprint; + use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; + use crate::accountant::test_utils::{ + make_non_guaranteed_qualified_payable, make_pending_payable_fingerprint, + }; use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::blockchain_interface::blockchain_interface_null::BlockchainInterfaceNull; use crate::blockchain::blockchain_interface::data_structures::errors::{ @@ -565,7 +567,7 @@ mod tests { use std::any::TypeId; use std::path::Path; use std::sync::{Arc, Mutex}; - use std::time::{Duration, SystemTime}; + use std::time::SystemTime; use web3::types::{TransactionReceipt, H160, H256}; impl Handler> for BlockchainBridge { @@ -670,25 +672,9 @@ mod tests { let persistent_config_id_stamp = ArbitraryIdStamp::new(); let persistent_configuration = PersistentConfigurationMock::default() .set_arbitrary_id_stamp(persistent_config_id_stamp); - let wallet_1 = make_wallet("booga"); - let wallet_2 = make_wallet("gulp"); let qualified_payables = vec![ - PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 78_654_321_124, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(1000)) - .unwrap(), - pending_payable_opt: None, - }, - PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 60_457_111_003, - last_paid_timestamp: SystemTime::now() - .checked_sub(Duration::from_secs(500)) - .unwrap(), - pending_payable_opt: None, - }, + make_non_guaranteed_qualified_payable(111), + make_non_guaranteed_qualified_payable(222), ]; let subject = BlockchainBridge::new( Box::new(blockchain_interface), @@ -699,7 +685,7 @@ mod tests { let addr = subject.start(); let subject_subs = BlockchainBridge::make_subs_from(&addr); let peer_actors = peer_actors_builder().accountant(accountant).build(); - let qualified_payables = protect_payables_in_test(qualified_payables.clone()); + let qualified_payables = protect_qualified_payables_in_test(qualified_payables.clone()); let qualified_payables_msg = QualifiedPayablesMessage { protected_qualified_payables: qualified_payables.clone(), response_skeleton_opt: Some(ResponseSkeleton { @@ -736,10 +722,11 @@ mod tests { } #[test] - fn build_of_blockchain_agent_throws_err_out_and_ends_handling_qualified_payables_message() { + fn build_of_blockchain_agent_throws_err_out_and_terminates_handling_qualified_payables_message() + { init_test_logging(); let test_name = - "build_of_blockchain_agent_throws_err_out_and_ends_handling_qualified_payables_message"; + "build_of_blockchain_agent_throws_err_out_and_terminates_handling_qualified_payables_message"; let (accountant, _, accountant_recording_arc) = make_recorder(); let scan_error_recipient: Recipient = accountant .system_stop_conditions(match_every_type_id!(ScanError)) @@ -760,12 +747,9 @@ mod tests { subject.logger = Logger::new(test_name); subject.scan_error_subs_opt = Some(scan_error_recipient); let request = QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 42, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }]), + protected_qualified_payables: protect_qualified_payables_in_test(vec![ + make_non_guaranteed_qualified_payable(1234), + ]), response_skeleton_opt: Some(ResponseSkeleton { client_id: 11, context_id: 2323, @@ -810,12 +794,9 @@ mod tests { None, ); let request = QualifiedPayablesMessage { - protected_qualified_payables: protect_payables_in_test(vec![PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 4254, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }]), + protected_qualified_payables: protect_qualified_payables_in_test(vec![ + make_non_guaranteed_qualified_payable(12345), + ]), response_skeleton_opt: None, }; diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index a435a6f7a..ade747f6a 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -3,16 +3,19 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::QualifiedPayablesMessage; -use crate::accountant::{QualifiedPayableAccount, RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder}; +use crate::accountant::{ + QualifiedPayableAccount, RequestTransactionReceipts, ResponseSkeleton, SkeletonOptHolder, +}; use crate::blockchain::blockchain_bridge::RetrieveTransactions; use crate::sub_lib::peer_actors::BindMessage; use actix::Message; use actix::Recipient; +use itertools::Either; use masq_lib::blockchains::chains::Chain; use masq_lib::ui_gateway::NodeFromUiMessage; +use masq_lib::utils::convert_collection; use std::fmt; use std::fmt::{Debug, Formatter}; -use itertools::Either; use web3::types::U256; #[derive(Clone, PartialEq, Eq, Debug, Default)] @@ -54,8 +57,8 @@ impl OutboundPaymentsInstructions { response_skeleton_opt: Option, ) -> Self { let affordable_accounts = match accounts { - Either::Left(qualified_account) => todo!(), - Either::Right(adjusted_account) => todo!() + Either::Left(qualified_account) => convert_collection(qualified_account), + Either::Right(adjusted_account) => todo!(), }; Self { From 1cc6af44985aac29fcf781a46fb875eff9480c85 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 16 Mar 2024 17:59:40 +0100 Subject: [PATCH 139/250] GH-711-b: Ripped off the old calculators --- .../age_criterion_calculator.rs | 253 ---------- .../balance_and_age_calculator.rs | 91 ++++ .../balance_criterion_calculator.rs | 150 ------ .../criteria_calculators/mod.rs | 446 +++++++++--------- .../payment_adjuster/diagnostics.rs | 325 +------------ .../miscellaneous/helper_functions.rs | 76 +-- node/src/accountant/payment_adjuster/mod.rs | 30 +- .../accountant/payment_adjuster/test_utils.rs | 17 +- node/src/accountant/scanners/mod.rs | 1 - 9 files changed, 350 insertions(+), 1039 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs create mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs delete mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs deleted file mode 100644 index 62aa596d4..000000000 --- a/node/src/accountant/payment_adjuster/criteria_calculators/age_criterion_calculator.rs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, CriterionCalculator, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::nonzero_positive; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use std::time::SystemTime; - -// Base is the main body of the growing value -const AGE_EXPONENT_FOR_BASE: u32 = 3; - -// Descending multiplier is the parameter that slows down the growth of the base. We start with a huge multiplier that -// diminishes as the age of the debt stretches -const AGE_DESC_MULTIPLIER_ARG_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_EXP: u32 = 2; -const AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER: u128 = 42; -const AGE_DESC_MULTIPLIER_DIVISOR_EXP: u32 = 4; - -pub struct AgeCriterionCalculator { - formula: Box u128>, -} - -impl CriterionCalculator for AgeCriterionCalculator { - fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128 { - &self.formula - } - - fn calculator_type(&self) -> CalculatorType { - CalculatorType::DebtAge - } -} - -impl AgeCriterionCalculator { - pub fn new(payment_adjuster: &PaymentAdjusterReal) -> Self { - let now = payment_adjuster.inner.now(); - - let formula = Box::new( - move |last_paid_timestamp_in_holder: CalculatorInputHolder| { - let last_paid_timestamp = last_paid_timestamp_in_holder.age_input(); - let elapsed_secs: u64 = Self::nonzero_elapsed(now, last_paid_timestamp); - - // This argument slows down the growth of the power of 3 in the base value, but unlike to the descending - // multiplier below, this happens consistently - let divisor = Self::compute_divisor(elapsed_secs); - - // This argument impacts the curve so that it progresses slower the further in time we get - // (The idea is: it's not already such a difference if it is weeks or months. Put differently, while - // the age of the debt can overrule easily smaller balances, because it progresses more aggressively - // than metrics for the debt size, in contrary, we don't want it to shade the large ones) - let log_multiplier = Self::compute_descending_multiplier(elapsed_secs, divisor); - - (elapsed_secs as u128) - .checked_pow(AGE_EXPONENT_FOR_BASE) - .expect("pow overflow") - .checked_div(divisor) - .expect("div overflow") - .checked_mul(log_multiplier) - .expect("mul overflow") - }, - ); - Self { formula } - } - - fn nonzero_elapsed(now: SystemTime, previous_timestamp: SystemTime) -> u64 { - let elapsed = now - .duration_since(previous_timestamp) - .expect("time traveller") - .as_secs(); - if elapsed > 0 { - elapsed - } else { - 1 - } - } - - fn compute_divisor(elapsed_sec: u64) -> u128 { - (elapsed_sec as f64).sqrt().ceil() as u128 - } - - fn nonzero_log2_value(num: f64) -> u128 { - if num < 2.0 { - 1 - } else { - num.log2() as u128 - } - } - - // This multiplier is meant to push against the growth of the age criterion, slowing it down more and more as - // the time goes on. The reason is that balances grow huge soon despite staying completely realistic and possible. - // For the different manner of time, the age formula is designed to up more steeply in rather smaller amounts of - // seconds, but while shifting towards days, weeks, months and so on, the inflating tendency diminishes - fn compute_descending_multiplier(elapsed_secs: u64, divisor: u128) -> u128 { - let too_fast_growing_argument = (elapsed_secs as u128) - .checked_pow(AGE_DESC_MULTIPLIER_ARG_EXP) - .expect("pow blew up") as f64; - - let log_value = Self::nonzero_log2_value(too_fast_growing_argument); - - let log_stressed = log_value.pow(AGE_DESC_MULTIPLIER_LOG_STRESS_EXP) - * AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER; - - let final_log_multiplier = (log_stressed / divisor).pow(AGE_DESC_MULTIPLIER_DIVISOR_EXP); - - nonzero_positive(final_log_multiplier) - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::{ - AgeCriterionCalculator, AGE_DESC_MULTIPLIER_ARG_EXP, AGE_DESC_MULTIPLIER_DIVISOR_EXP, - AGE_DESC_MULTIPLIER_LOG_STRESS_EXP, AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER, - AGE_EXPONENT_FOR_BASE, - }; - use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, CriterionCalculator, - }; - use crate::accountant::payment_adjuster::test_utils::{ - assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes, - make_initialized_subject, - }; - use std::time::{Duration, SystemTime}; - - #[test] - fn constants_are_correct() { - let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ - (AGE_EXPONENT_FOR_BASE.try_into().unwrap(), 3), - (AGE_DESC_MULTIPLIER_ARG_EXP.try_into().unwrap(), 2), - (AGE_DESC_MULTIPLIER_LOG_STRESS_EXP.try_into().unwrap(), 2), - ( - AGE_DESC_MULTIPLIER_LOG_STRESS_MULTIPLIER - .try_into() - .unwrap(), - 42, - ), - (AGE_DESC_MULTIPLIER_DIVISOR_EXP.try_into().unwrap(), 4), - ]; - - assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( - &constants_and_their_expected_values, - 53, - ) - } - - #[test] - fn nonzero_compute_divisor_works() { - let result: Vec<_> = [1, 100, 81, 82, 80] - .into_iter() - .map(|secs| AgeCriterionCalculator::compute_divisor(secs)) - .collect(); - - assert_eq!(result, vec![1, 10, 9, 10, 9]) - } - - #[test] - fn nonzero_elapsed_works() { - let now = SystemTime::now(); - let result: Vec<_> = [ - // The first entry is normally considered 0 s - now.checked_sub(Duration::from_nanos(55)).unwrap(), - now.checked_sub(Duration::from_secs(1)).unwrap(), - now.checked_sub(Duration::from_secs(2)).unwrap(), - ] - .into_iter() - .map(|timestamp| AgeCriterionCalculator::nonzero_elapsed(now, timestamp)) - .collect(); - - assert_eq!(result, vec![1, 1, 2]) - } - - #[test] - fn compute_descending_multiplier_works() { - let result: Vec<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 18] - .into_iter() - .map(|exp| 10_u64.pow(exp)) - .map(|seconds_elapsed| { - let divisor = AgeCriterionCalculator::compute_divisor(seconds_elapsed); - AgeCriterionCalculator::compute_descending_multiplier(seconds_elapsed, divisor) - }) - .collect(); - - assert_eq!( - result, - vec![ - 20415837456, - // Harmless irregularity at the beginning - 252688187761, - 50054665441, - 6414247921, - 429981696, - 15752961, - 614656, - 14641, - 256, - 1, - 1, - 1, - 1 - ] - ) - } - - #[test] - fn nonzero_log_works() { - let result = vec![0.0, 0.6, 1.3, 1.99999, 2.0, 2.1, 5.0, 9.0] - .into_iter() - .map(|num| AgeCriterionCalculator::nonzero_log2_value(num)) - .collect::>(); - - assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 2, 3]) - } - - #[test] - fn calculator_knows_its_type() { - let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); - let subject = AgeCriterionCalculator::new(&payment_adjuster); - - let result = subject.calculator_type(); - - assert_eq!(result, CalculatorType::DebtAge) - } - - #[test] - fn age_criteria_calculation_works() { - let now = SystemTime::now(); - let payment_adjuster = make_initialized_subject(now, None, None); - let subject = AgeCriterionCalculator::new(&payment_adjuster); - let last_paid_timestamp = SystemTime::now() - .checked_sub(Duration::from_secs(1500)) - .unwrap(); - let last_paid_timestamp_holder = CalculatorInputHolder::DebtAge { - last_paid_timestamp, - }; - - let result = subject.formula()(last_paid_timestamp_holder); - - let expected_criterion = { - let elapsed_secs: u64 = now.duration_since(last_paid_timestamp).unwrap().as_secs(); - let divisor = AgeCriterionCalculator::compute_divisor(elapsed_secs); - let log_multiplier = - AgeCriterionCalculator::compute_descending_multiplier(elapsed_secs, divisor); - (elapsed_secs as u128) - .checked_pow(AGE_EXPONENT_FOR_BASE) - .unwrap() - .checked_div(divisor) - .unwrap() - .checked_mul(log_multiplier) - .unwrap() - }; - assert_eq!(result, expected_criterion) - } -} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs new file mode 100644 index 000000000..b32804ebd --- /dev/null +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs @@ -0,0 +1,91 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::QualifiedPayableAccount; + +// This parameter affects the steepness inversely, but just slowly. +// +// Don't worry to joggle with this number; it's not as scientific as it looks like. True, I arrived at it after many +// attempts when I finally aligned myself with the tuning. The issue is it needs to be carefully compared to the values +// the Age criterion calculator yields. +// (If you are preparing to follow my steps you'll need to enable the rendering from the 'diagnostics' module, getting +// back a chance for a close look into each characteristic of the calculators formulas and therefore also how sync +// they are. See and think about the effects of your new settings) +const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; +// This parameter affects the steepness analogously, but energetically +const BALANCE_FINAL_MULTIPLIER: u128 = 2; + +pub struct BalanceAndAgeCriterionCalculator {} + +impl CriterionCalculator for BalanceAndAgeCriterionCalculator { + fn calculate(&self, account: &QualifiedPayableAccount) -> u128 { + todo!() + } + + fn parameter_name(&self) -> &'static str { + todo!() + } +} + +impl BalanceAndAgeCriterionCalculator { + pub fn new() -> Self { + todo!() + // let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { + // let balance_minor = balance_minor_holder.balance_input(); + // let argument_for_log = Self::calculate_binary_argument(balance_minor); + // let binary_weight = Self::nonzero_log2(argument_for_log); + // balance_minor + // .checked_mul(binary_weight as u128) + // .expect("mul overflow") + // * BALANCE_FINAL_MULTIPLIER + // }); + // Self { formula } + } + + // fn nonzero_log2(input: u128) -> u32 { + // let log = log_2(input); + // if log > 0 { + // log + // } else { + // 1 + // } + // } + // + // fn calculate_binary_argument(balance_minor: u128) -> u128 { + // balance_minor / BALANCE_LOG_2_ARG_DIVISOR + // } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::{ + BalanceAndAgeCriterionCalculator, BALANCE_FINAL_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, + }; + use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::test_utils::assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes; + + #[test] + fn constants_are_correct() { + let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ + (BALANCE_LOG_2_ARG_DIVISOR.try_into().unwrap(), 18_490_000), + (BALANCE_FINAL_MULTIPLIER.try_into().unwrap(), 2), + ]; + + assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( + &constants_and_their_expected_values, + 18490002, + ) + } + + #[test] + fn balance_criteria_calculation_works() { + let subject = BalanceAndAgeCriterionCalculator::new(); + let balance_wei = 111_333_555_777_u128; + let account = todo!(); + + let result = subject.calculate(account); + + let expected_result = todo!(); + assert_eq!(result, expected_result) + } +} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs deleted file mode 100644 index 8d3e7eafb..000000000 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_criterion_calculator.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, CriterionCalculator, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_2; - -// This parameter affects the steepness inversely, but just slowly. -// -// Don't worry to joggle with this number; it's not as scientific as it looks like. True, I arrived at it after many -// attempts when I finally aligned myself with the tuning. The issue is it needs to be carefully compared to the values -// the Age criterion calculator yields. -// (If you are preparing to follow my steps you'll need to enable the rendering from the 'diagnostics' module, getting -// back a chance for a close look into each characteristics of the calculators formulas and therefore also how sync -// they are. See and think about the effects of your new settings) -const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; -// This parameter affects the steepness analogously, but energetically -const BALANCE_FINAL_MULTIPLIER: u128 = 2; - -pub struct BalanceCriterionCalculator { - formula: Box u128>, -} - -impl CriterionCalculator for BalanceCriterionCalculator { - fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128 { - &self.formula - } - - fn calculator_type(&self) -> CalculatorType { - CalculatorType::DebtBalance - } -} - -impl BalanceCriterionCalculator { - pub fn new() -> Self { - let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { - let balance_minor = balance_minor_holder.balance_input(); - let argument_for_log = Self::calculate_binary_argument(balance_minor); - let binary_weight = Self::nonzero_log2(argument_for_log); - balance_minor - .checked_mul(binary_weight as u128) - .expect("mul overflow") - * BALANCE_FINAL_MULTIPLIER - }); - Self { formula } - } - - fn nonzero_log2(input: u128) -> u32 { - let log = log_2(input); - if log > 0 { - log - } else { - 1 - } - } - - fn calculate_binary_argument(balance_minor: u128) -> u128 { - balance_minor / BALANCE_LOG_2_ARG_DIVISOR - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::{ - BalanceCriterionCalculator, BALANCE_FINAL_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, - }; - use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, CriterionCalculator, - }; - use crate::accountant::payment_adjuster::test_utils::assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes; - - #[test] - fn constants_are_correct() { - let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ - (BALANCE_LOG_2_ARG_DIVISOR.try_into().unwrap(), 18_490_000), - (BALANCE_FINAL_MULTIPLIER.try_into().unwrap(), 2), - ]; - - assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( - &constants_and_their_expected_values, - 18490002, - ) - } - - #[test] - fn compute_binary_argument_works() { - let arg_values = [ - 1, - BALANCE_LOG_2_ARG_DIVISOR - 1, - BALANCE_LOG_2_ARG_DIVISOR, - BALANCE_LOG_2_ARG_DIVISOR + 1, - BALANCE_LOG_2_ARG_DIVISOR + 1000, - ]; - - let result: Vec<_> = arg_values - .into_iter() - .map(|arg| BalanceCriterionCalculator::calculate_binary_argument(arg)) - .collect(); - - assert_eq!( - result, - vec![ - 0, - 0, - 1, - 1, - (BALANCE_LOG_2_ARG_DIVISOR + 1000) / BALANCE_LOG_2_ARG_DIVISOR - ] - ) - } - - #[test] - fn nonzero_log2_works() { - let result: Vec<_> = [0, 1, 2, 5, 66, 100, 131, 132, u64::MAX as u128 + 1] - .into_iter() - .map(|balance| BalanceCriterionCalculator::nonzero_log2(balance)) - .collect(); - - assert_eq!(result, vec![1, 1, 1, 2, 6, 6, 7, 7, 64]) - } - - #[test] - fn balance_criterion_calculator_knows_its_type() { - let subject = BalanceCriterionCalculator::new(); - - let result = subject.calculator_type(); - - assert_eq!(result, CalculatorType::DebtBalance) - } - - #[test] - fn balance_criteria_calculation_works() { - let subject = BalanceCriterionCalculator::new(); - let balance_wei = 111_333_555_777; - let balance_wei_inside_input_holder = CalculatorInputHolder::DebtBalance(balance_wei); - - let result = subject.formula()(balance_wei_inside_input_holder); - - let expected_result = { - let binary_weight = BalanceCriterionCalculator::nonzero_log2( - BalanceCriterionCalculator::calculate_binary_argument(balance_wei), - ); - balance_wei - .checked_mul(binary_weight as u128) - .expect("mul overflow") - * BALANCE_FINAL_MULTIPLIER - }; - assert_eq!(result, expected_result) - } -} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs index 1d4c24e83..49830f66c 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs @@ -1,238 +1,226 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod age_criterion_calculator; -pub mod balance_criterion_calculator; +pub mod balance_and_age_calculator; use crate::accountant::QualifiedPayableAccount; -use std::fmt::{Debug, Display, Formatter}; -use std::time::SystemTime; -use variant_count::VariantCount; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { - // Reference to the formula that is meant by design to be stored inside the calculator. - // The formula can keep its own context if required - fn formula(&self) -> &dyn Fn(CalculatorInputHolder) -> u128; + fn calculate(&self, account: &QualifiedPayableAccount) -> u128; - fn calculator_type(&self) -> CalculatorType; -} - -#[derive(PartialEq, Debug, VariantCount, Clone, Copy)] -pub enum CalculatorType { - DebtBalance, - DebtAge, -} - -#[derive(PartialEq, Debug, VariantCount)] -pub enum CalculatorInputHolder { - DebtBalance(u128), - DebtAge { last_paid_timestamp: SystemTime }, -} - -impl CalculatorInputHolder { - fn age_input(self) -> SystemTime { - if let CalculatorInputHolder::DebtAge { - last_paid_timestamp, - } = self - { - last_paid_timestamp - } else { - self.mismatched_call_panic("age") - } - } - - fn balance_input(self) -> u128 { - if let CalculatorInputHolder::DebtBalance(balance_wei) = self { - balance_wei - } else { - self.mismatched_call_panic("balance") - } - } - - fn mismatched_call_panic(&self, param_name: &str) -> ! { - panic!( - "Call for {} while the underlying enum variant is {:?}", - param_name, self - ) - } -} - -impl<'account> From<(CalculatorType, &'account QualifiedPayableAccount)> for CalculatorInputHolder { - fn from( - (calculator_type, qualified_payable): (CalculatorType, &'account QualifiedPayableAccount), - ) -> Self { - match calculator_type { - CalculatorType::DebtBalance => { - CalculatorInputHolder::DebtBalance(qualified_payable.payable.balance_wei) - } - CalculatorType::DebtAge => CalculatorInputHolder::DebtAge { - last_paid_timestamp: qualified_payable.payable.last_paid_timestamp, - }, - } - } -} - -impl Display for CalculatorType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - CalculatorType::DebtBalance => write!(f, "BALANCE"), - CalculatorType::DebtAge => write!(f, "AGE"), - } - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, - }; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use crate::accountant::QualifiedPayableAccount; - use crate::test_utils::make_wallet; - use std::panic::{catch_unwind, RefUnwindSafe}; - use std::time::{Duration, SystemTime}; - - #[test] - fn input_holders_can_be_derived_from_calculator_type_and_payable_account() { - let balance_wei = 135_792_468; - let last_paid_timestamp = SystemTime::now() - .checked_sub(Duration::from_secs(3)) - .unwrap(); - let payable = PayableAccount { - wallet: make_wallet("abc"), - balance_wei, - last_paid_timestamp, - pending_payable_opt: None, - }; - let payment_threshold_intercept = 65432; - let qualified_account = QualifiedPayableAccount { - payable, - payment_threshold_intercept, - }; - let result = [CalculatorType::DebtAge, CalculatorType::DebtBalance] - .into_iter() - .map(|calculator_type| { - CalculatorInputHolder::from((calculator_type, &qualified_account)) - }) - .collect::>(); - - let expected = vec![ - CalculatorInputHolder::DebtAge { - last_paid_timestamp, - }, - CalculatorInputHolder::DebtBalance(balance_wei - payment_threshold_intercept), - ]; - assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); - assert_eq!(result, expected); - } - - #[test] - fn calculator_type_implements_display() { - assert_eq!(CalculatorType::DebtBalance.to_string(), "BALANCE"); - assert_eq!(CalculatorType::DebtAge.to_string(), "AGE") - } - - #[test] - fn input_values_can_be_fetched_from_input_holder() { - let last_paid_timestamp = SystemTime::now() - .checked_sub(Duration::from_millis(1234)) - .unwrap(); - let age_input_holder = CalculatorInputHolder::DebtAge { - last_paid_timestamp, - }; - let balance = 333_444_555_666; - let balance_input_holder = CalculatorInputHolder::DebtBalance(balance); - - let result = vec![age_input_holder, balance_input_holder]; - - assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); - let mut result = result.into_iter(); - assert_eq!(result.next().unwrap().age_input(), last_paid_timestamp); - assert_eq!(result.next().unwrap().balance_input(), balance); - assert_eq!(result.next(), None) - } - - #[test] - fn there_is_same_amount_of_calculator_types_as_calculator_input_holders() { - assert_eq!( - CalculatorType::VARIANT_COUNT, - CalculatorInputHolder::VARIANT_COUNT - ) - } - - #[test] - fn panics_for_age_input_when_the_enum_is_not_age_input_holder() { - test_panics_for_mismatched_input_holder( - CalculatorType::DebtAge, - |calculator_type, account| { - CalculatorInputHolder::from((calculator_type, account)).age_input(); - // should cause panic - }, - ) - } - - #[test] - fn panics_for_balance_input_when_the_enum_is_not_balance_input_holder() { - test_panics_for_mismatched_input_holder( - CalculatorType::DebtBalance, - |calculator_type, account| { - CalculatorInputHolder::from((calculator_type, account)).balance_input(); - // should cause panic - }, - ) - } - - fn test_panics_for_mismatched_input_holder( - the_only_correct_type: CalculatorType, - tested_function_call_for_panics: F, - ) where - F: Fn(CalculatorType, &QualifiedPayableAccount) + RefUnwindSafe, - { - let mut qualified_payable = make_non_guaranteed_qualified_payable(12345); - qualified_payable.payable.balance_wei = 2_000_000; - qualified_payable.payment_threshold_intercept = 778_899; - let difference = - qualified_payable.payable.balance_wei - qualified_payable.payment_threshold_intercept; - let all_types = vec![ - ( - CalculatorType::DebtBalance, - "balance", - format!("DebtBalance({difference})"), - ), - ( - CalculatorType::DebtAge, - "age", - "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }".to_string(), - ), - ]; - - assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); - let (_, the_right_param_literal_name, _) = all_types - .iter() - .find(|(calculator_type, _, _)| calculator_type == &the_only_correct_type) - .unwrap(); - // To be able to shut the reference before we consume the vec - let the_right_param_literal_name = the_right_param_literal_name.to_string(); - all_types - .into_iter() - .filter(|(calculator_type, _, _)| calculator_type != &the_only_correct_type) - .for_each( - |(calculator_type, _, debug_rendering_of_the_corresponding_input_holder)| { - let result = catch_unwind(|| { - tested_function_call_for_panics(calculator_type, &qualified_payable) - }) - .unwrap_err(); - let panic_msg = result.downcast_ref::().unwrap(); - assert_eq!( - panic_msg, - &format!( - "Call for {} while the underlying enum variant is {}", - the_right_param_literal_name, - debug_rendering_of_the_corresponding_input_holder - ) - ); - }, - ) - } + fn parameter_name(&self) -> &'static str; } +// +// #[derive(PartialEq, Debug, VariantCount)] +// pub enum CalculatorInputHolder { +// DebtBalance(u128), +// DebtAge { last_paid_timestamp: SystemTime }, +// } +// +// impl CalculatorInputHolder { +// fn age_input(self) -> SystemTime { +// if let CalculatorInputHolder::DebtAge { +// last_paid_timestamp, +// } = self +// { +// last_paid_timestamp +// } else { +// self.mismatched_call_panic("age") +// } +// } +// +// fn balance_input(self) -> u128 { +// if let CalculatorInputHolder::DebtBalance(balance_wei) = self { +// balance_wei +// } else { +// self.mismatched_call_panic("balance") +// } +// } +// +// fn mismatched_call_panic(&self, param_name: &str) -> ! { +// panic!( +// "Call for {} while the underlying enum variant is {:?}", +// param_name, self +// ) +// } +// } +// +// impl<'account> From<(CalculatorType, &'account QualifiedPayableAccount)> for CalculatorInputHolder { +// fn from( +// (calculator_type, qualified_payable): (CalculatorType, &'account QualifiedPayableAccount), +// ) -> Self { +// match calculator_type { +// CalculatorType::DebtBalance => { +// CalculatorInputHolder::DebtBalance(qualified_payable.payable.balance_wei) +// } +// CalculatorType::DebtAge => CalculatorInputHolder::DebtAge { +// last_paid_timestamp: qualified_payable.payable.last_paid_timestamp, +// }, +// } +// } +// } +// +// impl Display for CalculatorType { +// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +// match self { +// CalculatorType::DebtBalance => write!(f, "BALANCE"), +// CalculatorType::DebtAge => write!(f, "AGE"), +// } +// } +// } +// +// #[cfg(test)] +// mod tests { +// use crate::accountant::db_access_objects::payable_dao::PayableAccount; +// use crate::accountant::payment_adjuster::criteria_calculators::{ +// CalculatorInputHolder, CalculatorType, +// }; +// use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; +// use crate::accountant::QualifiedPayableAccount; +// use crate::test_utils::make_wallet; +// use std::panic::{catch_unwind, RefUnwindSafe}; +// use std::time::{Duration, SystemTime}; +// +// #[test] +// fn input_holders_can_be_derived_from_calculator_type_and_payable_account() { +// let balance_wei = 135_792_468; +// let last_paid_timestamp = SystemTime::now() +// .checked_sub(Duration::from_secs(3)) +// .unwrap(); +// let payable = PayableAccount { +// wallet: make_wallet("abc"), +// balance_wei, +// last_paid_timestamp, +// pending_payable_opt: None, +// }; +// let payment_threshold_intercept = 65432; +// let qualified_account = QualifiedPayableAccount { +// payable, +// payment_threshold_intercept, +// }; +// let result = [CalculatorType::DebtAge, CalculatorType::DebtBalance] +// .into_iter() +// .map(|calculator_type| { +// CalculatorInputHolder::from((calculator_type, &qualified_account)) +// }) +// .collect::>(); +// +// let expected = vec![ +// CalculatorInputHolder::DebtAge { +// last_paid_timestamp, +// }, +// CalculatorInputHolder::DebtBalance(balance_wei - payment_threshold_intercept), +// ]; +// assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); +// assert_eq!(result, expected); +// } +// +// #[test] +// fn calculator_type_implements_display() { +// assert_eq!(CalculatorType::DebtBalance.to_string(), "BALANCE"); +// assert_eq!(CalculatorType::DebtAge.to_string(), "AGE") +// } +// +// #[test] +// fn input_values_can_be_fetched_from_input_holder() { +// let last_paid_timestamp = SystemTime::now() +// .checked_sub(Duration::from_millis(1234)) +// .unwrap(); +// let age_input_holder = CalculatorInputHolder::DebtAge { +// last_paid_timestamp, +// }; +// let balance = 333_444_555_666; +// let balance_input_holder = CalculatorInputHolder::DebtBalance(balance); +// +// let result = vec![age_input_holder, balance_input_holder]; +// +// assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); +// let mut result = result.into_iter(); +// assert_eq!(result.next().unwrap().age_input(), last_paid_timestamp); +// assert_eq!(result.next().unwrap().balance_input(), balance); +// assert_eq!(result.next(), None) +// } +// +// #[test] +// fn there_is_same_amount_of_calculator_types_as_calculator_input_holders() { +// assert_eq!( +// CalculatorType::VARIANT_COUNT, +// CalculatorInputHolder::VARIANT_COUNT +// ) +// } +// +// #[test] +// fn panics_for_age_input_when_the_enum_is_not_age_input_holder() { +// test_panics_for_mismatched_input_holder( +// CalculatorType::DebtAge, +// |calculator_type, account| { +// CalculatorInputHolder::from((calculator_type, account)).age_input(); +// // should cause panic +// }, +// ) +// } +// +// #[test] +// fn panics_for_balance_input_when_the_enum_is_not_balance_input_holder() { +// test_panics_for_mismatched_input_holder( +// CalculatorType::DebtBalance, +// |calculator_type, account| { +// CalculatorInputHolder::from((calculator_type, account)).balance_input(); +// // should cause panic +// }, +// ) +// } +// +// fn test_panics_for_mismatched_input_holder( +// the_only_correct_type: CalculatorType, +// tested_function_call_for_panics: F, +// ) where +// F: Fn(CalculatorType, &QualifiedPayableAccount) + RefUnwindSafe, +// { +// let mut qualified_payable = make_non_guaranteed_qualified_payable(12345); +// qualified_payable.payable.balance_wei = 2_000_000; +// qualified_payable.payment_threshold_intercept = 778_899; +// let difference = +// qualified_payable.payable.balance_wei - qualified_payable.payment_threshold_intercept; +// let all_types = vec![ +// ( +// CalculatorType::DebtBalance, +// "balance", +// format!("DebtBalance({difference})"), +// ), +// ( +// CalculatorType::DebtAge, +// "age", +// "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }".to_string(), +// ), +// ]; +// +// assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); +// let (_, the_right_param_literal_name, _) = all_types +// .iter() +// .find(|(calculator_type, _, _)| calculator_type == &the_only_correct_type) +// .unwrap(); +// // To be able to shut the reference before we consume the vec +// let the_right_param_literal_name = the_right_param_literal_name.to_string(); +// all_types +// .into_iter() +// .filter(|(calculator_type, _, _)| calculator_type != &the_only_correct_type) +// .for_each( +// |(calculator_type, _, debug_rendering_of_the_corresponding_input_holder)| { +// let result = catch_unwind(|| { +// tested_function_call_for_panics(calculator_type, &qualified_payable) +// }) +// .unwrap_err(); +// let panic_msg = result.downcast_ref::().unwrap(); +// assert_eq!( +// panic_msg, +// &format!( +// "Call for {} while the underlying enum variant is {}", +// the_right_param_literal_name, +// debug_rendering_of_the_corresponding_input_holder +// ) +// ); +// }, +// ) +// } +// } diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index e28c842c1..dee84579b 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -1,9 +1,5 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -#[cfg(test)] -use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::{ - render_complete_formulas_characteristics, COMPUTE_FORMULAS_CHARACTERISTICS, -}; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; @@ -205,11 +201,12 @@ pub mod ordinary_diagnostic_functions { added_in_the_sum: u128, ) { const FIRST_COLUMN_WIDTH: usize = 30; + diagnostics!( wallet_ref, "PARTIAL CRITERION CALCULATED", "For {: String { - let payment_adjuster = make_initialized_subject(SystemTime::now(), None, None); - - let rendering_params: Vec<(&'static str, Box, DiagnosticsAxisX)> = vec![ - ( - "BALANCE", - Box::new(BalanceCriterionCalculator::new()), - make_rendering_config_for_balance(), - ), - ( - "AGE", - Box::new(AgeCriterionCalculator::new(&payment_adjuster)), - make_rendering_config_for_age(), - ), - ]; - - rendering_params - .into_iter() - .map( - |(param_name, criterion_calculator, param_rendering_config)| { - let param_calculation_formula = criterion_calculator.formula(); - compute_progressive_characteristics( - param_name, - param_calculation_formula, - param_rendering_config, - ) - }, - ) - .join("\n\n") - } - - fn make_rendering_config_for_balance() -> DiagnosticsAxisX { - let file_path = file_path("input_data_for_diagnostics_of_balance_criterion_formula.txt"); - let horizontal_axis_data_supply = read_diagnostics_inputs_from_file(&file_path); - - DiagnosticsAxisX { - values: horizontal_axis_data_supply, - convertor_to_expected_formula_input: Box::new(|balance_wei| { - CalculatorInputHolder::DebtBalance(balance_wei) - }), - } - } - - fn make_rendering_config_for_age() -> DiagnosticsAxisX { - let now = SystemTime::now(); - let file_path = file_path("input_data_for_diagnostics_of_age_criterion_formula.txt"); - let horizontal_axis_data_supply = read_diagnostics_inputs_from_file(&file_path); - let convertor_to_expected_formula_input_type = - Box::new(move |secs_since_last_paid_payable: u128| { - let native_time = now - .checked_sub(Duration::from_secs(secs_since_last_paid_payable as u64)) - .expect("time travelling"); - CalculatorInputHolder::DebtAge { - last_paid_timestamp: native_time, - } - }); - - DiagnosticsAxisX { - values: horizontal_axis_data_supply, - convertor_to_expected_formula_input: convertor_to_expected_formula_input_type, - } - } - - fn file_path(file_name: &str) -> PathBuf { - standard_dir_for_test_input_data().join(file_name) - } - - #[derive(Deserialize)] - struct InputFromFile { - literals: Vec, - decimal_exponents: Vec, - marked_values: Vec, - } - - #[derive(Deserialize)] - struct MarkedValueFromFile { - value: IntegerValueAllowingUnderscores, - label: String, - } - - struct MarkedValue { - value: u128, - label: String, - } - - struct DeserializedInputValues { - non_marked_values: Vec, - marked_values: Vec, - } - - struct DiagnosticsAxisX { - values: DeserializedInputValues, - convertor_to_expected_formula_input: Box CalculatorInputHolder>, - } - - struct IntegerValueAllowingUnderscores { - numerical_value: u128, - } - - impl IntegerValueAllowingUnderscores { - fn new(value: u128) -> Self { - Self { - numerical_value: value, - } - } - } - - impl<'de> NormalImplDeserialize<'de> for IntegerValueAllowingUnderscores { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value: Value = NormalImplDeserialize::deserialize(deserializer)?; - if let Value::String(str) = value { - let underscore_less = str.chars().filter(|char| char != &'_').collect::(); - let num: u128 = underscore_less.parse().unwrap(); - Ok(IntegerValueAllowingUnderscores::new(num)) - } else { - Err(D::Error::custom(format!( - "Expected a string value but found: {:?}", - value - ))) - } - } - } - - impl From for MarkedValue { - fn from(marked_value_from_file: MarkedValueFromFile) -> Self { - MarkedValue { - value: marked_value_from_file.value.numerical_value, - label: marked_value_from_file.label, - } - } - } - - impl DiagnosticsAxisX { - fn finalize_input_with_marked_values(&self) -> Vec { - if self.values.marked_values.is_empty() { - self.values.non_marked_values.clone() - } else { - let filtered_marked_values = self - .values - .marked_values - .iter() - .map(|marked_val| &marked_val.value); - let standard_input = self.values.non_marked_values.iter(); - filtered_marked_values - .chain(standard_input) - .sorted() - .dedup() - .map(|num| *num) - .collect() - } - } - } - - fn render_notation(coordinate_value: u128, marked_vals: &[MarkedValue]) -> String { - match should_mark_be_used(coordinate_value, marked_vals) { - Some(mark) => format!("{} {}", coordinate_value.separate_with_commas(), mark), - None => coordinate_value.separate_with_commas(), - } - } - fn should_mark_be_used(coordinate_value: u128, marked_vals: &[MarkedValue]) -> Option { - if marked_vals.is_empty() { - None - } else { - marked_vals - .iter() - .find(|marked_val| marked_val.value == coordinate_value) - .map(|marked_val| marked_val.label.clone()) - } - } - - fn compute_progressive_characteristics( - param_name: &'static str, - formula: &dyn Fn(CalculatorInputHolder) -> u128, - rendering_config: DiagnosticsAxisX, - ) -> String { - let input_values = rendering_config.finalize_input_with_marked_values(); - let marked_input_values = rendering_config.values.marked_values.as_slice(); - let config_x_axis_type_formatter = rendering_config.convertor_to_expected_formula_input; - - let characteristics = input_values.into_iter().map(|single_coordinate| { - let correctly_formatted_input = config_x_axis_type_formatter(single_coordinate); - let input_with_commas = render_notation(single_coordinate, marked_input_values); - let computed_value_with_commas = - formula(correctly_formatted_input).separate_with_commas(); - format!( - "x: {: DeserializedInputValues { - let mut file = File::open(path).unwrap_or_else(|e| { - panic!("Inputs badly prepared at path: {:?}, error: {:?}", path, e) - }); - let mut buffer = String::new(); - file.read_to_string(&mut buffer).unwrap(); - let plain_json: String = buffer - .lines() - .filter(|line| !line.is_empty() && !line_is_comment(line)) - .collect(); - let processed_json_input = serde_json::from_str::(&plain_json) - .unwrap_or_else(|e| { - panic!( - "Error {:?} for file path: {:?}. Read string: {}", - e, path, plain_json - ) - }); - - let nums_declared_as_literals = processed_json_input - .literals - .into_iter() - .map(|wrapper| wrapper.numerical_value) - .collect(); - - let marked_values = convert_collection(processed_json_input.marked_values); - - DeserializedInputValues { - non_marked_values: serialize_values_on_x_axis_from_vecs( - nums_declared_as_literals, - processed_json_input.decimal_exponents, - ), - marked_values, - } - } - - fn line_is_comment(line: &str) -> bool { - let char_collection = line - .chars() - .skip_while(|char| char.is_ascii_whitespace()) - .take(1) - .collect::>(); - let first_meaningful_char_opt = char_collection.first(); - if first_meaningful_char_opt.is_none() { - panic!("Something went wrong. Empty lines should have been already tested") - } - first_meaningful_char_opt.unwrap() == &'#' - } - - pub fn serialize_values_on_x_axis_from_vecs( - nums_declared_as_literals: Vec, - nums_declared_as_decimal_exponents: Vec, - ) -> Vec { - let exponent_based_numbers = nums_declared_as_decimal_exponents - .into_iter() - .map(|exponent| 10_u128.pow(exponent)); - nums_declared_as_literals - .into_iter() - .chain(exponent_based_numbers) - .sorted() - .collect() - } -} - #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::diagnostics::formulas_progressive_characteristics::COMPUTE_FORMULAS_CHARACTERISTICS; use crate::accountant::payment_adjuster::diagnostics::PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS; #[test] fn constants_are_correct() { assert_eq!(PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS, false); - assert_eq!(COMPUTE_FORMULAS_CHARACTERISTICS, false) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 363e6f4d6..1acb14da4 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -78,10 +78,9 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( let exponent = positive_only_difference + EMPIRIC_PRECISION_COEFFICIENT; U256::from(10) .checked_pow(exponent.into()) - .expect("impossible to reach given weights total data type being u128") - // Note that reaching this limitation is highly unlikely, and even in the future, if we boosted the data type - // for account_weights_total up to U256, assuming such low inputs we would be feeding it now with real world - // scenario parameters + .expect("impossible to reach given weights data type being u128") + // Bursting this cap is so unlikely, even assuming blockchains without our own layer + // of a smart contract that might be adopted in the future } pub fn resolve_possibly_outweighed_account( @@ -211,13 +210,13 @@ pub fn try_finding_an_account_to_disqualify_in_this_iteration( ); debug!( - logger, - "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ - for disqualification. Chose the least desirable disqualified account as the one \ - with the biggest balance, which is {}. To be thrown away in this iteration.", - disqualification_suspected_accounts, - wallet - ); + logger, + "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ + for disqualification. Chose the least desirable disqualified account as the one \ + with the biggest balance, which is {}. To be thrown away in this iteration.", + disqualification_suspected_accounts, + wallet + ); info_log_for_disqualified_account(logger, account_to_disqualify); @@ -340,22 +339,12 @@ pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor } -// Replace with std lib method log10() for u128 which will be introduced by Rust 1.67.0; this was written using 1.63.0 +// Replace with std lib method log10() for u128 which will be introduced by Rust 1.67.0; this was +// written using 1.63.0 pub fn log_10(num: u128) -> usize { successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() } -const fn num_bits() -> usize { - std::mem::size_of::() * 8 -} - -pub fn log_2(x: u128) -> u32 { - if x < 1 { - return 0; - } - num_bits::() as u32 - x.leading_zeros() - 1 -} - pub fn nonzero_positive(x: u128) -> u128 { if x == 0 { 1 @@ -414,8 +403,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, find_account_with_smallest_weight, - list_accounts_nominated_for_disqualification, log_10, log_2, - resolve_possibly_outweighed_account, + list_accounts_nominated_for_disqualification, log_10, resolve_possibly_outweighed_account, try_finding_an_account_to_disqualify_in_this_iteration, weights_total, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, @@ -498,29 +486,6 @@ mod tests { .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) } - #[test] - fn log_2_works() { - [ - (1, 0), - (2, 1), - (3, 1), - (4, 2), - (8192, 13), - (18446744073709551616, 64), - (1267650600228229401496703205376, 100), - (170141183460469231731687303715884105728, 127), - ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_2(num), expected_result)) - } - - #[test] - fn log_2_for_0() { - let result = log_2(0); - - assert_eq!(result, 0) - } - #[test] fn multiplication_coefficient_can_give_numbers_preventing_fractional_numbers() { let final_weight = 5_000_000_000_000_u128; @@ -530,7 +495,7 @@ mod tests { 123_456_789, 5_555_000_000_000, 5_000_555_000_000_000, - 1_000_000_000_000_000_000, //1 MASQ + 1_000_000_000_000_000_000, // 1 MASQ ]; let result = cw_balances @@ -545,8 +510,8 @@ mod tests { 1_000_000_000_u128, 1_000_000_000_000_000, 1_000_000_000_000, - // The following values are the minimum. It turned out that it helps to reach better precision in - // the downstream computations + // The following values are the minimum. It turned out that it helps to reach better + // precision in the downstream computations 100_000_000, 100_000_000, 100_000_000, @@ -556,9 +521,8 @@ mod tests { #[test] fn multiplication_coefficient_extreme_feeding_with_possible_but_only_little_realistic_values() { - // We cannot say by heart which of the evaluated weights from - // these parameters below will be bigger than another and therefore - // we cannot line them up in an order + // We cannot say by heart which of the evaluated weights from these parameters below will + // be bigger than another, and therefore we cannot line them up in an order let accounts_as_months_and_balances = vec![ (1, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), (5, 10_u128.pow(18)), @@ -993,8 +957,8 @@ mod tests { #[test] fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { let account_balance = 1_000_000; - let garbage_weight = 22222222; // it plays no role - let garbage_thresholds_intercept = 456789; // also no role + let garbage_weight = 22222222; // It plays no role + let garbage_thresholds_intercept = 456789; // Also no role let prepare_account = |n: u64| { let mut account = make_payable_account(n); account.balance_wei = account_balance; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index f9a6843c9..8cdbbd995 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -17,7 +17,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, }; -use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics, display_formulas_characteristics_according_to_compilation_mode}; +use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; @@ -42,9 +42,8 @@ use std::time::SystemTime; use thousands::Separable; use web3::types::U256; use masq_lib::utils::convert_collection; -use crate::accountant::payment_adjuster::criteria_calculators::age_criterion_calculator::AgeCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::balance_criterion_calculator::BalanceCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::{CalculatorInputHolder, CriterionCalculator}; +use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; +use crate::accountant::payment_adjuster::criteria_calculators::{CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::QualifiedPayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -230,8 +229,6 @@ impl PaymentAdjusterReal { let weights_and_accounts_sorted = self.calculate_weights_for_accounts(unresolved_qualified_accounts); - display_formulas_characteristics_according_to_compilation_mode(); - adjustment_runner.adjust_multiple(self, weights_and_accounts_sorted) } @@ -352,10 +349,8 @@ impl PaymentAdjusterReal { &self, accounts: Vec, ) -> Vec { - let criteria_calculators: Vec> = vec![ - Box::new(BalanceCriterionCalculator::new()), - Box::new(AgeCriterionCalculator::new(self)), - ]; + let criteria_calculators: Vec> = + vec![Box::new(BalanceAndAgeCriterionCalculator::new())]; self.apply_criteria(criteria_calculators, accounts) } @@ -370,10 +365,7 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = Self::have_calculator_calculate_its_criterion( - &**criterion_calculator, - &payable, - ); + let new_criterion = criterion_calculator.calculate(&payable); let summed_up = weight + new_criterion; @@ -393,16 +385,6 @@ impl PaymentAdjusterReal { sort_in_descendant_order_by_weights(weighted_accounts) } - fn have_calculator_calculate_its_criterion( - criterion_calculator: &dyn CriterionCalculator, - account: &QualifiedPayableAccount, - ) -> u128 { - let calculator_type = criterion_calculator.calculator_type(); - let input_holder = CalculatorInputHolder::from((calculator_type, account)); - - criterion_calculator.formula()(input_holder) - } - fn perform_adjustment_by_service_fee( &self, weighted_accounts: Vec, diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 0d0d94820..d47088ae4 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -3,12 +3,11 @@ #![cfg(test)] use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::{ - CalculatorInputHolder, CalculatorType, CriterionCalculator, -}; +use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::pre_adjustment_analyzer::PreAdjustmentAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use itertools::Either; @@ -106,3 +105,15 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ rendering characteristics of the curves the calculations of these parameters are based on." ) } + +pub struct CriterionCalculatorMock {} + +impl CriterionCalculator for CriterionCalculatorMock { + fn calculate(&self, account: &QualifiedPayableAccount) -> u128 { + todo!() + } + + fn parameter_name(&self) -> &'static str { + todo!() + } +} diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index e40aec3b2..0925aa5c4 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1125,7 +1125,6 @@ mod tests { use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; - use sysinfo::Signal::Sys; use web3::types::{TransactionReceipt, H256}; use web3::Error; From 89a8aa96fe74bb92c0beeb62e8be8554d1023851 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 21 Mar 2024 10:57:11 +0100 Subject: [PATCH 140/250] GH-711-b: Another big shuffling - created the DisqualificationArbiter class --- .../db_access_objects/receivable_dao.rs | 54 +-- .../src/accountant/db_access_objects/utils.rs | 8 +- .../payment_adjuster/adjustment_runners.rs | 17 +- .../balance_and_age_calculator.rs | 17 +- .../disqualification_arbiter.rs | 327 +++++++++++++++++ .../miscellaneous/helper_functions.rs | 336 ++---------------- node/src/accountant/payment_adjuster/mod.rs | 26 +- ...nt_analyzer.rs => preparatory_analyses.rs} | 53 +-- .../accountant/payment_adjuster/test_utils.rs | 28 +- .../unprivileged_parse_args_configuration.rs | 2 +- node/src/sub_lib/accountant.rs | 10 +- 11 files changed, 468 insertions(+), 410 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/disqualification_arbiter.rs rename node/src/accountant/payment_adjuster/{pre_adjustment_analyzer.rs => preparatory_analyses.rs} (85%) diff --git a/node/src/accountant/db_access_objects/receivable_dao.rs b/node/src/accountant/db_access_objects/receivable_dao.rs index 14fe4b4e9..dd24fb91a 100644 --- a/node/src/accountant/db_access_objects/receivable_dao.rs +++ b/node/src/accountant/db_access_objects/receivable_dao.rs @@ -79,7 +79,7 @@ pub trait ReceivableDao: Send { fn total(&self) -> i128; - //test-only-like method but because of share with multi-node tests #[cfg(test)] is disallowed + // Test-only-like method but because of share with multi-node tests #[cfg(test)] is disallowed fn account_status(&self, wallet: &Wallet) -> Option; as_any_in_trait!(); @@ -159,10 +159,10 @@ impl ReceivableDao for ReceivableDaoReal { from receivable r left outer join banned b on r.wallet_address = b.wallet_address where - r.last_received_timestamp < :sugg_and_grace - and ((r.balance_high_b > slope_drop_high_bytes(:debt_threshold, :slope, :sugg_and_grace - r.last_received_timestamp)) - or ((r.balance_high_b = slope_drop_high_bytes(:debt_threshold, :slope, :sugg_and_grace - r.last_received_timestamp)) - and (r.balance_low_b > slope_drop_low_bytes(:debt_threshold, :slope, :sugg_and_grace - r.last_received_timestamp)))) + r.last_received_timestamp < :maturity_and_grace + and ((r.balance_high_b > slope_drop_high_bytes(:debt_threshold, :slope, :maturity_and_grace - r.last_received_timestamp)) + or ((r.balance_high_b = slope_drop_high_bytes(:debt_threshold, :slope, :maturity_and_grace - r.last_received_timestamp)) + and (r.balance_low_b > slope_drop_low_bytes(:debt_threshold, :slope, :maturity_and_grace - r.last_received_timestamp)))) and ((r.balance_high_b > :permanent_debt_allowed_high_b) or ((r.balance_high_b = 0) and (r.balance_low_b > :permanent_debt_allowed_low_b))) and b.wallet_address is null " @@ -174,7 +174,7 @@ impl ReceivableDao for ReceivableDaoReal { named_params! { ":debt_threshold": checked_conversion::(payment_thresholds.debt_threshold_gwei), ":slope": slope, - ":sugg_and_grace": payment_thresholds.sugg_and_grace(to_time_t(now)), + ":maturity_and_grace": payment_thresholds.maturity_and_grace(to_time_t(now)), ":permanent_debt_allowed_high_b": permanent_debt_allowed_high_b, ":permanent_debt_allowed_low_b": permanent_debt_allowed_low_b }, @@ -282,18 +282,18 @@ impl ReceivableDaoReal { timestamp: SystemTime, payments: &[BlockchainTransaction], ) -> Result<(), ReceivableDaoError> { - let xactn = self.conn.transaction()?; + let txn = self.conn.transaction()?; { for transaction in payments { - // the plus signs are correct, 'Subtraction' in the wei_change converts x of u128 to -x of i128 which leads to an integer pair - // with the high bytes integer being negative + // The plus signs are correct, 'Subtraction' in the wei_change converts x of u128 to -x of i128 which leads to an integer pair + // with the one for high bytes being negative let main_sql = "update receivable set balance_high_b = balance_high_b + :balance_high_b, \ - balance_low_b = balance_low_b + :balance_low_b, last_received_timestamp = :last_received where wallet_address = :wallet"; + balance_low_b = balance_low_b + :balance_low_b, last_received_timestamp = :last_received_timestamp where wallet_address = :wallet"; let overflow_update_clause = "update receivable set balance_high_b = :balance_high_b, balance_low_b = :balance_low_b, \ - last_received_timestamp = :last_received where wallet_address = :wallet"; + last_received_timestamp = :last_received_timestamp where wallet_address = :wallet"; self.big_int_db_processor.execute( - Either::Right(&xactn), + Either::Right(&txn), BigIntSqlConfig::new( main_sql, overflow_update_clause, @@ -301,7 +301,7 @@ impl ReceivableDaoReal { .key(WalletAddress(&transaction.from)) .wei_change(Subtraction("balance", transaction.wei_amount)) .other_params(vec![Param::new( - (":last_received", &to_time_t(timestamp)), + (":last_received_timestamp", &to_time_t(timestamp)), true, )]) .build(), @@ -309,7 +309,7 @@ impl ReceivableDaoReal { )? } } - match xactn.commit() { + match txn.commit() { // Error response is untested here, because without a mockable Transaction, it's untestable. Err(e) => Err(ReceivableDaoError::RusqliteError(format!("{:?}", e))), Ok(_) => Ok(()), @@ -849,17 +849,17 @@ mod tests { not_delinquent_inside_grace_period.balance_wei = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 1); not_delinquent_inside_grace_period.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) + 2); + from_time_t(payment_thresholds.maturity_and_grace(now) + 2); let mut not_delinquent_after_grace_below_slope = make_receivable_account(2345, false); not_delinquent_after_grace_below_slope.balance_wei = gwei_to_wei(payment_thresholds.debt_threshold_gwei - 2); not_delinquent_after_grace_below_slope.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 1); + from_time_t(payment_thresholds.maturity_and_grace(now) - 1); let mut delinquent_above_slope_after_grace = make_receivable_account(3456, true); delinquent_above_slope_after_grace.balance_wei = gwei_to_wei(payment_thresholds.debt_threshold_gwei - 1); delinquent_above_slope_after_grace.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 2); + from_time_t(payment_thresholds.maturity_and_grace(now) - 2); let mut not_delinquent_below_slope_before_stop = make_receivable_account(4567, false); not_delinquent_below_slope_before_stop.balance_wei = gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei + 1); @@ -906,11 +906,11 @@ mod tests { let mut not_delinquent = make_receivable_account(1234, false); not_delinquent.balance_wei = gwei_to_wei(105); not_delinquent.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 25); + from_time_t(payment_thresholds.maturity_and_grace(now) - 25); let mut delinquent = make_receivable_account(2345, true); delinquent.balance_wei = gwei_to_wei(105); delinquent.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 75); + from_time_t(payment_thresholds.maturity_and_grace(now) - 75); let home_dir = ensure_node_home_directory_exists("accountant", "new_delinquencies_shallow_slope"); let conn = make_connection_with_our_defined_sqlite_functions(&home_dir); @@ -938,11 +938,11 @@ mod tests { let mut not_delinquent = make_receivable_account(1234, false); not_delinquent.balance_wei = gwei_to_wei(600); not_delinquent.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 25); + from_time_t(payment_thresholds.maturity_and_grace(now) - 25); let mut delinquent = make_receivable_account(2345, true); delinquent.balance_wei = gwei_to_wei(600); delinquent.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 75); + from_time_t(payment_thresholds.maturity_and_grace(now) - 75); let home_dir = ensure_node_home_directory_exists("accountant", "new_delinquencies_steep_slope"); let conn = make_connection_with_our_defined_sqlite_functions(&home_dir); @@ -970,11 +970,11 @@ mod tests { let mut existing_delinquency = make_receivable_account(1234, true); existing_delinquency.balance_wei = gwei_to_wei(250); existing_delinquency.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 1); + from_time_t(payment_thresholds.maturity_and_grace(now) - 1); let mut new_delinquency = make_receivable_account(2345, true); new_delinquency.balance_wei = gwei_to_wei(250); new_delinquency.last_received_timestamp = - from_time_t(payment_thresholds.sugg_and_grace(now) - 1); + from_time_t(payment_thresholds.maturity_and_grace(now) - 1); let home_dir = ensure_node_home_directory_exists( "receivable_dao", "new_delinquencies_does_not_find_existing_delinquencies", @@ -1016,7 +1016,7 @@ mod tests { #[test] fn new_delinquencies_handles_too_young_debts_causing_slope_parameter_to_be_negative() { - //situation where sugg_and_grace makes more time than the age of the debt + // Situation where maturity_and_grace makes longer period of time than the debt age let home_dir = ensure_node_home_directory_exists( "receivable_dao", "new_delinquencies_handles_too_young_debts_causing_slope_parameter_to_be_negative", @@ -1030,16 +1030,16 @@ mod tests { unban_below_gwei: 0, }; let now = to_time_t(SystemTime::now()); - let sugg_and_grace = payment_thresholds.sugg_and_grace(now); + let maturity_and_grace = payment_thresholds.maturity_and_grace(now); let too_young_new_delinquency = ReceivableAccount { wallet: make_wallet("abc123"), balance_wei: 123_456_789_101_112, - last_received_timestamp: from_time_t(sugg_and_grace + 1), + last_received_timestamp: from_time_t(maturity_and_grace + 1), }; let ok_new_delinquency = ReceivableAccount { wallet: make_wallet("aaa999"), balance_wei: 123_456_789_101_112, - last_received_timestamp: from_time_t(sugg_and_grace - 1), + last_received_timestamp: from_time_t(maturity_and_grace - 1), }; let conn = make_connection_with_our_defined_sqlite_functions(&home_dir); add_receivable_account(&conn, &too_young_new_delinquency); diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index 85e5adade..613a9e2a4 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -382,10 +382,10 @@ impl ThresholdUtils { .debt_threshold_gwei, so that the numerator will be no greater than -10^9 (-gwei_to_wei(1)), and the denominator must be less than or equal to 10^9. - These restrictions do not seem over-strict, since having .permanent_debt_allowed greater - than or equal to .debt_threshold_gwei would result in chaos, and setting - .threshold_interval_sec over 10^9 would mean continuing to declare debts delinquent after - more than 31 years. + These restrictions do not seem overly strict for having .permanent_debt_allowed greater + than or equal to .debt_threshold_gwei would not make any sense and setting + .threshold_interval_sec over 10^9 would mean stretching out for debts across more than + 31 years. If payment_thresholds are ever configurable by the user, these validations should be done on the values before they are accepted. diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 6f0d1255c..7f8c2c487 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -2,10 +2,10 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::try_finding_an_account_to_disqualify_in_this_iteration; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use crate::accountant::QualifiedPayableAccount; use itertools::Either; @@ -129,7 +129,10 @@ fn adjust_last_one_opt( let logger = &payment_adjuster.logger; - match try_finding_an_account_to_disqualify_in_this_iteration(&proposed_adjustment_vec, logger) { + match DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( + &proposed_adjustment_vec, + logger, + ) { Some(_) => None, None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), } @@ -151,8 +154,8 @@ mod tests { adjust_last_one_opt, empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ @@ -226,7 +229,8 @@ mod tests { fn adjust_last_one_for_requested_balance_smaller_than_cw_but_not_needed_disqualified() { let now = SystemTime::now(); let account_balance = 4_500_600; - let cw_balance = calculate_disqualification_edge(account_balance) + 1; + let cw_balance = + DisqualificationArbiter::calculate_disqualification_edge(account_balance) + 1; let payable = PayableAccount { wallet: make_wallet("abc"), balance_wei: account_balance, @@ -277,7 +281,7 @@ mod tests { fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_evens_the_edge() { let account_balance = 4_000_444; - let cw_balance = calculate_disqualification_edge(account_balance); + let cw_balance = DisqualificationArbiter::calculate_disqualification_edge(account_balance); let payment_threshold_intercept = 3_000_000_000; test_adjust_last_one_when_disqualified( @@ -291,7 +295,8 @@ mod tests { fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() { let account_balance = 4_000_444; - let cw_balance = calculate_disqualification_edge(account_balance) - 1; + let cw_balance = + DisqualificationArbiter::calculate_disqualification_edge(account_balance) - 1; let payment_threshold_intercept = 3_000_000_000; test_adjust_last_one_when_disqualified( diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs index b32804ebd..e8a57d557 100644 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs @@ -15,6 +15,7 @@ const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; // This parameter affects the steepness analogously, but energetically const BALANCE_FINAL_MULTIPLIER: u128 = 2; +#[derive(Default)] pub struct BalanceAndAgeCriterionCalculator {} impl CriterionCalculator for BalanceAndAgeCriterionCalculator { @@ -28,20 +29,6 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { } impl BalanceAndAgeCriterionCalculator { - pub fn new() -> Self { - todo!() - // let formula = Box::new(|balance_minor_holder: CalculatorInputHolder| { - // let balance_minor = balance_minor_holder.balance_input(); - // let argument_for_log = Self::calculate_binary_argument(balance_minor); - // let binary_weight = Self::nonzero_log2(argument_for_log); - // balance_minor - // .checked_mul(binary_weight as u128) - // .expect("mul overflow") - // * BALANCE_FINAL_MULTIPLIER - // }); - // Self { formula } - } - // fn nonzero_log2(input: u128) -> u32 { // let log = log_2(input); // if log > 0 { @@ -79,7 +66,7 @@ mod tests { #[test] fn balance_criteria_calculation_works() { - let subject = BalanceAndAgeCriterionCalculator::new(); + let subject = BalanceAndAgeCriterionCalculator::default(); let balance_wei = 111_333_555_777_u128; let account = todo!(); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs new file mode 100644 index 000000000..76fbf6f62 --- /dev/null +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -0,0 +1,327 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ + account_nominated_for_disqualification_diagnostics, + try_finding_an_account_to_disqualify_diagnostics, +}; +use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; +use crate::sub_lib::wallet::Wallet; +use masq_lib::logger::Logger; +use std::cmp::Ordering; + +pub struct DisqualificationArbiter {} + +impl DisqualificationArbiter { + pub fn try_finding_an_account_to_disqualify_in_this_iteration( + unconfirmed_adjustments: &[UnconfirmedAdjustment], + logger: &Logger, + ) -> Option { + let disqualification_suspected_accounts = + Self::list_accounts_nominated_for_disqualification(unconfirmed_adjustments); + + if !disqualification_suspected_accounts.is_empty() { + let account_to_disqualify = + Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); + + let wallet = account_to_disqualify + .original_qualified_account + .payable + .wallet + .clone(); + + try_finding_an_account_to_disqualify_diagnostics( + &disqualification_suspected_accounts, + &wallet, + ); + + debug!( + logger, + "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ + for disqualification. Chose the least desirable disqualified account as the one \ + with the biggest balance, which is {}. To be thrown away in this iteration.", + disqualification_suspected_accounts, + wallet + ); + + info_log_for_disqualified_account(logger, account_to_disqualify); + + Some(wallet) + } else { + None + } + } + + pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { + (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) + / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + } + + fn list_accounts_nominated_for_disqualification( + unconfirmed_adjustments: &[UnconfirmedAdjustment], + ) -> Vec<&UnconfirmedAdjustment> { + unconfirmed_adjustments + .iter() + .flat_map(|adjustment_info| { + let disqualification_edge = Self::calculate_disqualification_edge( + adjustment_info + .non_finalized_account + .original_qualified_account + .payable + .balance_wei, + ); + let proposed_adjusted_balance = adjustment_info + .non_finalized_account + .proposed_adjusted_balance; + + if proposed_adjusted_balance <= disqualification_edge { + account_nominated_for_disqualification_diagnostics( + adjustment_info, + proposed_adjusted_balance, + disqualification_edge, + ); + + Some(adjustment_info) + } else { + None + } + }) + .collect() + } + + fn find_account_with_smallest_weight<'a>( + accounts: &'a [&'a UnconfirmedAdjustment], + ) -> &'a AdjustedAccountBeforeFinalization { + let first_account = &accounts.first().expect("collection was empty"); + &accounts + .iter() + .fold( + **first_account, + |with_smallest_weight_so_far, current| match Ord::cmp( + ¤t.weight, + &with_smallest_weight_so_far.weight, + ) { + Ordering::Less => current, + Ordering::Greater => with_smallest_weight_so_far, + Ordering::Equal => with_smallest_weight_so_far, + }, + ) + .non_finalized_account + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, + }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::test_utils::{ + make_initialized_subject, PRESERVED_TEST_PAYMENT_THRESHOLDS, + }; + use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; + use crate::accountant::QualifiedPayableAccount; + use crate::test_utils::make_wallet; + use masq_lib::logger::Logger; + use std::time::{Duration, SystemTime}; + + #[test] + fn calculate_disqualification_edge_works() { + let mut account = make_payable_account(111); + account.balance_wei = 300_000_000; + + let result = DisqualificationArbiter::calculate_disqualification_edge(account.balance_wei); + + assert_eq!(result, todo!()) + } + + #[test] + fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { + let account_balance = 1_000_000; + let garbage_weight = 22222222; // It plays no role + let garbage_thresholds_intercept = 456789; // Also no role + let prepare_account = |n: u64| { + let mut account = make_payable_account(n); + account.balance_wei = account_balance; + account + }; + let payable_account_1 = prepare_account(1); + let payable_account_2 = prepare_account(2); + let payable_account_3 = prepare_account(3); + let edge = DisqualificationArbiter::calculate_disqualification_edge(account_balance); + let proposed_ok_balance = edge + 1; + let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_1, garbage_thresholds_intercept), + garbage_weight, + ), + proposed_ok_balance, + ); + let proposed_bad_balance_because_equal = edge; + let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_2, garbage_thresholds_intercept), + garbage_weight, + ), + proposed_bad_balance_because_equal, + ); + let proposed_bad_balance_because_smaller = edge - 1; + let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( + WeightedAccount::new( + QualifiedPayableAccount::new(payable_account_3, garbage_thresholds_intercept), + garbage_weight, + ), + proposed_bad_balance_because_smaller, + ); + let unconfirmed_adjustments = vec![ + unconfirmed_adjustment_1, + unconfirmed_adjustment_2.clone(), + unconfirmed_adjustment_3.clone(), + ]; + + let result = DisqualificationArbiter::list_accounts_nominated_for_disqualification( + &unconfirmed_adjustments, + ); + + let expected_disqualified_accounts = + vec![&unconfirmed_adjustment_2, &unconfirmed_adjustment_3]; + assert_eq!(result, expected_disqualified_accounts) + } + + #[test] + fn find_account_with_smallest_weight_works_for_unequal_weights() { + let idx_of_expected_result = 1; + let (adjustments, expected_result) = + make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( + vec![1004, 1000, 1002, 1001], + idx_of_expected_result, + ); + let referenced_unconfirmed_adjustments = by_reference(&adjustments); + + let result = DisqualificationArbiter::find_account_with_smallest_weight( + &referenced_unconfirmed_adjustments, + ); + + assert_eq!(result, &expected_result) + } + + #[test] + fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { + let idx_of_expected_result = 0; + let (adjustments, expected_result) = + make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( + vec![1111, 1113, 1111], + idx_of_expected_result, + ); + let referenced_non_finalized_accounts = by_reference(&adjustments); + + let result = DisqualificationArbiter::find_account_with_smallest_weight( + &referenced_non_finalized_accounts, + ); + + assert_eq!(result, &expected_result) + } + + fn by_reference(adjusted_accounts: &[UnconfirmedAdjustment]) -> Vec<&UnconfirmedAdjustment> { + adjusted_accounts.iter().collect() + } + + #[test] + fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { + let test_name = + "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; + let now = SystemTime::now(); + let cw_masq_balance = 200_000_000_000; + let logger = Logger::new(test_name); + let subject = make_initialized_subject(now, Some(cw_masq_balance), None); + // None of these accounts would be outside the definition for disqualification + // even if any of them would be gifted by the complete balance from the cw + let wallet_1 = make_wallet("abc"); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 120_000_000_001, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 120_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 119_999_999_999, + last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), + pending_payable_opt: None, + }; + let wallet_4 = make_wallet("jkl"); + let account_4 = PayableAccount { + wallet: wallet_4.clone(), + balance_wei: 120_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), + pending_payable_opt: None, + }; + let accounts = vec![account_1, account_2, account_3, account_4]; + let qualified_payables = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + let weights_total = weights_total(&weights_and_accounts); + let unconfirmed_adjustments = + subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); + + let result = + DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + &logger, + ); + + assert_eq!(result, Some(wallet_3)); + } + + fn make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( + weights: Vec, + idx_of_expected_result: usize, + ) -> ( + Vec, + AdjustedAccountBeforeFinalization, + ) { + let init: ( + Vec, + Option, + ) = (vec![], None); + let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( + init, + |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { + let original_account = make_payable_account(actual_idx as u64); + let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; + let qualified_account = QualifiedPayableAccount { + payable: original_account, + payment_threshold_intercept: garbage_intercept, + }; + let garbage_proposed_balance = 1_000_000_000; // Same here + let new_adjustment_to_be_added = UnconfirmedAdjustment::new( + WeightedAccount::new(qualified_account, weight), + garbage_proposed_balance, + ); + let expected_result_opt = if expected_result_opt_so_far.is_none() + && actual_idx == idx_of_expected_result + { + Some(new_adjustment_to_be_added.non_finalized_account.clone()) + } else { + expected_result_opt_so_far + }; + adjustments_so_far.push(new_adjustment_to_be_added); + (adjustments_so_far, expected_result_opt) + }, + ); + (adjustments, expected_result_opt.unwrap()) + } +} diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 1acb14da4..001457352 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -79,8 +79,8 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( U256::from(10) .checked_pow(exponent.into()) .expect("impossible to reach given weights data type being u128") - // Bursting this cap is so unlikely, even assuming blockchains without our own layer - // of a smart contract that might be adopted in the future + // Bursting out of this cap is as though a fantasy. Even assuming some potential implementations + // of pure blockchains with no application-specific smart contract } pub fn resolve_possibly_outweighed_account( @@ -187,65 +187,6 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( } } -pub fn try_finding_an_account_to_disqualify_in_this_iteration( - unconfirmed_adjustments: &[UnconfirmedAdjustment], - logger: &Logger, -) -> Option { - let disqualification_suspected_accounts = - list_accounts_nominated_for_disqualification(unconfirmed_adjustments); - - if !disqualification_suspected_accounts.is_empty() { - let account_to_disqualify = - find_account_with_smallest_weight(&disqualification_suspected_accounts); - - let wallet = account_to_disqualify - .original_qualified_account - .payable - .wallet - .clone(); - - try_finding_an_account_to_disqualify_diagnostics( - &disqualification_suspected_accounts, - &wallet, - ); - - debug!( - logger, - "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ - for disqualification. Chose the least desirable disqualified account as the one \ - with the biggest balance, which is {}. To be thrown away in this iteration.", - disqualification_suspected_accounts, - wallet - ); - - info_log_for_disqualified_account(logger, account_to_disqualify); - - Some(wallet) - } else { - None - } -} - -fn find_account_with_smallest_weight<'a>( - accounts: &'a [&'a UnconfirmedAdjustment], -) -> &'a AdjustedAccountBeforeFinalization { - let first_account = &accounts.first().expect("collection was empty"); - &accounts - .iter() - .fold( - **first_account, - |with_smallest_weight_so_far, current| match Ord::cmp( - ¤t.weight, - &with_smallest_weight_so_far.weight, - ) { - Ordering::Less => current, - Ordering::Greater => with_smallest_weight_so_far, - Ordering::Equal => with_smallest_weight_so_far, - }, - ) - .non_finalized_account -} - struct ConsumingWalletExhaustingStatus { remainder: u128, accounts_finalized_so_far: Vec, @@ -302,43 +243,6 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( .collect() } -fn list_accounts_nominated_for_disqualification( - unconfirmed_adjustments: &[UnconfirmedAdjustment], -) -> Vec<&UnconfirmedAdjustment> { - unconfirmed_adjustments - .iter() - .flat_map(|adjustment_info| { - let disqualification_edge = calculate_disqualification_edge( - adjustment_info - .non_finalized_account - .original_qualified_account - .payable - .balance_wei, - ); - let proposed_adjusted_balance = adjustment_info - .non_finalized_account - .proposed_adjusted_balance; - - if proposed_adjusted_balance <= disqualification_edge { - account_nominated_for_disqualification_diagnostics( - adjustment_info, - proposed_adjusted_balance, - disqualification_edge, - ); - - Some(adjustment_info) - } else { - None - } - }) - .collect() -} - -pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor -} - // Replace with std lib method log10() for u128 which will be introduced by Rust 1.67.0; this was // written using 1.63.0 pub fn log_10(num: u128) -> usize { @@ -401,13 +305,10 @@ mod tests { WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - calculate_disqualification_edge, compute_mul_coefficient_preventing_fractional_numbers, - exhaust_cw_till_the_last_drop, find_account_with_smallest_weight, - list_accounts_nominated_for_disqualification, log_10, resolve_possibly_outweighed_account, - try_finding_an_account_to_disqualify_in_this_iteration, weights_total, - zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, EMPIRIC_PRECISION_COEFFICIENT, - MAX_EXPONENT_FOR_10_WITHIN_U128, + compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, + log_10, resolve_possibly_outweighed_account, weights_total, zero_affordable_accounts_found, + ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -523,6 +424,7 @@ mod tests { fn multiplication_coefficient_extreme_feeding_with_possible_but_only_little_realistic_values() { // We cannot say by heart which of the evaluated weights from these parameters below will // be bigger than another, and therefore we cannot line them up in an order + todo!("the comment is lie now"); let accounts_as_months_and_balances = vec![ (1, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), (5, 10_u128.pow(18)), @@ -588,6 +490,24 @@ mod tests { ); } + fn get_extreme_weights_and_initial_accounts_order( + months_of_debt_and_balances: Vec<(usize, u128)>, + ) -> (Vec, Vec) { + let now = SystemTime::now(); + let accounts = make_extreme_payables(Either::Right(months_of_debt_and_balances), now); + let wallets_in_order = accounts + .iter() + .map(|account| account.wallet.clone()) + .collect(); + let qualified_accounts = + make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let subject = make_initialized_subject(now, None, None); + // The initial order is remembered because when the weight are applied the collection + // also gets sorted and will not necessarily have to match the initial order + let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_accounts); + (weights_and_accounts, wallets_in_order) + } + fn check_relation_to_computed_weight_fairly_but_with_enough_benevolence( output: &[(U256, u128)], ) { @@ -634,89 +554,6 @@ mod tests { }) } - fn make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( - weights: Vec, - idx_of_expected_result: usize, - ) -> ( - Vec, - AdjustedAccountBeforeFinalization, - ) { - let init: ( - Vec, - Option, - ) = (vec![], None); - let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( - init, - |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { - let original_account = make_payable_account(actual_idx as u64); - let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; - let qualified_account = QualifiedPayableAccount { - payable: original_account, - payment_threshold_intercept: garbage_intercept, - }; - let garbage_proposed_balance = 1_000_000_000; // Same here - let new_adjustment_to_be_added = UnconfirmedAdjustment::new( - WeightedAccount::new(qualified_account, weight), - garbage_proposed_balance, - ); - let expected_result_opt = if expected_result_opt_so_far.is_none() - && actual_idx == idx_of_expected_result - { - Some(new_adjustment_to_be_added.non_finalized_account.clone()) - } else { - expected_result_opt_so_far - }; - adjustments_so_far.push(new_adjustment_to_be_added); - (adjustments_so_far, expected_result_opt) - }, - ); - (adjustments, expected_result_opt.unwrap()) - } - - fn by_reference(adjusted_accounts: &[UnconfirmedAdjustment]) -> Vec<&UnconfirmedAdjustment> { - adjusted_accounts.iter().collect() - } - - #[test] - fn calculate_disqualification_edge_works() { - let mut account = make_payable_account(111); - account.balance_wei = 300_000_000; - - let result = calculate_disqualification_edge(account.balance_wei); - - assert_eq!(result, calculate_disqualification_edge(account.balance_wei)) - } - - #[test] - fn find_account_with_smallest_weight_works_for_unequal_weights() { - let idx_of_expected_result = 1; - let (adjustments, expected_result) = - make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( - vec![1004, 1000, 1002, 1001], - idx_of_expected_result, - ); - let referenced_unconfirmed_adjustments = by_reference(&adjustments); - - let result = find_account_with_smallest_weight(&referenced_unconfirmed_adjustments); - - assert_eq!(result, &expected_result) - } - - #[test] - fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { - let idx_of_expected_result = 0; - let (adjustments, expected_result) = - make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( - vec![1111, 1113, 1111], - idx_of_expected_result, - ); - let referenced_non_finalized_accounts = by_reference(&adjustments); - - let result = find_account_with_smallest_weight(&referenced_non_finalized_accounts); - - assert_eq!(result, &expected_result) - } - #[test] fn accounts_with_original_balances_equal_to_the_proposed_ones_are_not_outweighed() { let payable = PayableAccount { @@ -745,60 +582,6 @@ mod tests { assert_eq!(ok, vec![unconfirmed_adjustment]) } - #[test] - fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { - let test_name = - "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; - let now = SystemTime::now(); - let cw_masq_balance = 200_000_000_000; - let logger = Logger::new(test_name); - let subject = make_initialized_subject(now, Some(cw_masq_balance), None); - // None of these accounts would be outside the definition for disqualification - // even if any of them would be gifted by the complete balance from the cw - let wallet_1 = make_wallet("abc"); - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 120_000_000_001, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), - pending_payable_opt: None, - }; - let wallet_3 = make_wallet("ghi"); - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 119_999_999_999, - last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), - pending_payable_opt: None, - }; - let wallet_4 = make_wallet("jkl"); - let account_4 = PayableAccount { - wallet: wallet_4.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), - pending_payable_opt: None, - }; - let accounts = vec![account_1, account_2, account_3, account_4]; - let qualified_payables = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let weights_total = weights_total(&weights_and_accounts); - let unconfirmed_adjustments = - subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); - - let result = try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - &logger, - ); - - assert_eq!(result, Some(wallet_3)); - } - fn make_non_finalized_adjusted_account( wallet: &Wallet, original_balance: u128, @@ -953,73 +736,4 @@ mod tests { assert_payable_accounts_after_adjustment_finalization(result, expected_resulted_balances); assert_eq!(check_sum, original_cw_balance) } - - #[test] - fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { - let account_balance = 1_000_000; - let garbage_weight = 22222222; // It plays no role - let garbage_thresholds_intercept = 456789; // Also no role - let prepare_account = |n: u64| { - let mut account = make_payable_account(n); - account.balance_wei = account_balance; - account - }; - let payable_account_1 = prepare_account(1); - let payable_account_2 = prepare_account(2); - let payable_account_3 = prepare_account(3); - let edge = calculate_disqualification_edge(account_balance); - let proposed_ok_balance = edge + 1; - let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_1, garbage_thresholds_intercept), - garbage_weight, - ), - proposed_ok_balance, - ); - let proposed_bad_balance_because_equal = edge; - let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_2, garbage_thresholds_intercept), - garbage_weight, - ), - proposed_bad_balance_because_equal, - ); - let proposed_bad_balance_because_smaller = edge - 1; - let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_3, garbage_thresholds_intercept), - garbage_weight, - ), - proposed_bad_balance_because_smaller, - ); - let unconfirmed_adjustments = vec![ - unconfirmed_adjustment_1, - unconfirmed_adjustment_2.clone(), - unconfirmed_adjustment_3.clone(), - ]; - - let result = list_accounts_nominated_for_disqualification(&unconfirmed_adjustments); - - let expected_disqualified_accounts = - vec![&unconfirmed_adjustment_2, &unconfirmed_adjustment_3]; - assert_eq!(result, expected_disqualified_accounts) - } - - fn get_extreme_weights_and_initial_accounts_order( - months_of_debt_and_balances: Vec<(usize, u128)>, - ) -> (Vec, Vec) { - let now = SystemTime::now(); - let accounts = make_extreme_payables(Either::Right(months_of_debt_and_balances), now); - let wallets_in_order = accounts - .iter() - .map(|account| account.wallet.clone()) - .collect(); - let qualified_accounts = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let subject = make_initialized_subject(now, None, None); - // The initial order is remembered because when the weight are applied the collection - // also gets sorted and will not necessarily have to match the initial order - let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_accounts); - (weights_and_accounts, wallets_in_order) - } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8cdbbd995..232b45279 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -4,12 +4,13 @@ mod adjustment_runners; mod criteria_calculators; mod diagnostics; +mod disqualification_arbiter; mod inner; #[cfg(test)] mod loading_test; mod log_fns; mod miscellaneous; -mod pre_adjustment_analyzer; +mod preparatory_analyses; #[cfg(test)] mod test_utils; @@ -29,8 +30,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::Require TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, try_finding_an_account_to_disqualify_in_this_iteration, resolve_possibly_outweighed_account, drop_no_longer_needed_weights_away_from_accounts, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; -use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{PreAdjustmentAnalyzer}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, resolve_possibly_outweighed_account, drop_no_longer_needed_weights_away_from_accounts, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; +use crate::accountant::payment_adjuster::preparatory_analyses::{PreparatoryAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -45,6 +46,7 @@ use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::QualifiedPayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -66,8 +68,9 @@ pub trait PaymentAdjuster { } pub struct PaymentAdjusterReal { - analyzer: PreAdjustmentAnalyzer, + analyzer: PreparatoryAnalyzer, inner: Box, + calculators: Vec>, logger: Logger, } @@ -145,8 +148,9 @@ impl Default for PaymentAdjusterReal { impl PaymentAdjusterReal { pub fn new() -> Self { Self { - analyzer: PreAdjustmentAnalyzer::new(), + analyzer: PreparatoryAnalyzer::new(), inner: Box::new(PaymentAdjusterInnerNull {}), + calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], logger: Logger::new("PaymentAdjuster"), } } @@ -349,15 +353,12 @@ impl PaymentAdjusterReal { &self, accounts: Vec, ) -> Vec { - let criteria_calculators: Vec> = - vec![Box::new(BalanceAndAgeCriterionCalculator::new())]; - - self.apply_criteria(criteria_calculators, accounts) + self.apply_criteria(self.calculators.as_slice(), accounts) } fn apply_criteria( &self, - criteria_calculators: Vec>, + criteria_calculators: &[Box], qualified_accounts: Vec, ) -> Vec { let weighted_accounts = qualified_accounts.into_iter().map(|payable| { @@ -476,7 +477,10 @@ impl PaymentAdjusterReal { logger: &Logger, ) -> Either, AdjustmentIterationResult> { if let Some(disqualified_account_wallet) = - try_finding_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, logger) + DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + logger, + ) { let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { account_info diff --git a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs b/node/src/accountant/payment_adjuster/preparatory_analyses.rs similarity index 85% rename from node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs rename to node/src/accountant/payment_adjuster/preparatory_analyses.rs index 6315e6661..bd66d1b68 100644 --- a/node/src/accountant/payment_adjuster/pre_adjustment_analyzer.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyses.rs @@ -1,15 +1,14 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::log_fns::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ TransactionCountsWithin16bits, WeightedAccount, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - calculate_disqualification_edge, sum_as, -}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::QualifiedPayableAccount; @@ -17,9 +16,9 @@ use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; -pub struct PreAdjustmentAnalyzer {} +pub struct PreparatoryAnalyzer {} -impl PreAdjustmentAnalyzer { +impl PreparatoryAnalyzer { pub fn new() -> Self { Self {} } @@ -110,8 +109,8 @@ impl PreAdjustmentAnalyzer { if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { - TransactionFeeAdjustmentPossibilityVerifier {} - .verify_lowest_detectable_adjustment_possibility( + ServiceFeeAdjustmentPossibilityAnalyser {} + .analyse_lowest_detectable_adjustment_possibility( &qualified_payables, cw_service_fee_balance_minor, )?; @@ -126,13 +125,14 @@ impl PreAdjustmentAnalyzer { } } -pub struct TransactionFeeAdjustmentPossibilityVerifier {} +pub struct ServiceFeeAdjustmentPossibilityAnalyser {} -impl TransactionFeeAdjustmentPossibilityVerifier { - // We cannot do much in this area, only step in if the balance can be zero or nearly zero by - // assumption we make about the smallest debt in the set and the disqualification limit applied - // on it. If so, we don't want to bother payment adjuster and so we will abort instead. - pub fn verify_lowest_detectable_adjustment_possibility( +impl ServiceFeeAdjustmentPossibilityAnalyser { + // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with + // the assumption that the smallest debt in the set of accounts fits under the disqualification + // limit. If it doesn't, we won't want to bother the payment adjuster by that work, so we're + // going to abort, no payments coming out. + pub fn analyse_lowest_detectable_adjustment_possibility( &self, accounts: &[&PayableAccount], cw_service_fee_balance_minor: u128, @@ -145,7 +145,7 @@ impl TransactionFeeAdjustmentPossibilityVerifier { .collect::>(); let smallest_account = sorted.first().expect("should be one at minimum"); - if calculate_disqualification_edge(smallest_account.balance_wei) + if DisqualificationArbiter::calculate_disqualification_edge(smallest_account.balance_wei) <= cw_service_fee_balance_minor { Ok(()) @@ -165,9 +165,9 @@ impl TransactionFeeAdjustmentPossibilityVerifier { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::calculate_disqualification_edge; - use crate::accountant::payment_adjuster::pre_adjustment_analyzer::{ - PreAdjustmentAnalyzer, TransactionFeeAdjustmentPossibilityVerifier, + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::preparatory_analyses::{ + PreparatoryAnalyzer, ServiceFeeAdjustmentPossibilityAnalyser, }; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -188,7 +188,7 @@ mod tests { estimated_transaction_fee_per_one_tx_minor, ) .transaction_fee_balance_minor_result(critical_wallet_balance); - let subject = PreAdjustmentAnalyzer::new(); + let subject = PreparatoryAnalyzer::new(); let panic_err = catch_unwind(AssertUnwindSafe(|| { subject.determine_transaction_count_limit_by_transaction_fee( @@ -215,9 +215,9 @@ mod tests { ) { let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = TransactionFeeAdjustmentPossibilityVerifier {}; + let subject = ServiceFeeAdjustmentPossibilityAnalyser {}; - let result = subject.verify_lowest_detectable_adjustment_possibility( + let result = subject.analyse_lowest_detectable_adjustment_possibility( &accounts_in_expected_format, cw_service_fee_balance, ); @@ -231,7 +231,8 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei) + 1; + let cw_service_fee_balance = + DisqualificationArbiter::calculate_disqualification_edge(account_2.balance_wei) + 1; let original_accounts = vec![account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected( @@ -246,7 +247,8 @@ mod tests { account_1.balance_wei = 2_000_000_000; let mut account_2 = make_payable_account(333); account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = calculate_disqualification_edge(account_2.balance_wei); + let cw_service_fee_balance = + DisqualificationArbiter::calculate_disqualification_edge(account_2.balance_wei); let original_accounts = vec![account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected( @@ -264,13 +266,14 @@ mod tests { account_2.balance_wei = 2_000_000_002; let mut account_3 = make_payable_account(333); account_3.balance_wei = 1_000_000_002; - let cw_service_fee_balance = calculate_disqualification_edge(account_3.balance_wei) - 1; + let cw_service_fee_balance = + DisqualificationArbiter::calculate_disqualification_edge(account_3.balance_wei) - 1; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts.iter().collect::>(); - let subject = TransactionFeeAdjustmentPossibilityVerifier {}; + let subject = ServiceFeeAdjustmentPossibilityAnalyser {}; - let result = subject.verify_lowest_detectable_adjustment_possibility( + let result = subject.analyse_lowest_detectable_adjustment_possibility( &accounts_in_expected_format, cw_service_fee_balance, ); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index d47088ae4..b35db28c4 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -3,9 +3,10 @@ #![cfg(test)] use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; -use crate::accountant::payment_adjuster::pre_adjustment_analyzer::PreAdjustmentAnalyzer; +use crate::accountant::payment_adjuster::preparatory_analyses::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::accountant::PaymentThresholds; @@ -14,6 +15,7 @@ use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; +use std::cell::RefCell; use std::time::{Duration, SystemTime}; lazy_static! { @@ -30,12 +32,13 @@ pub fn make_initialized_subject( let cw_masq_balance_minor = cw_masq_balance_minor_opt.unwrap_or(0); let logger = logger_opt.unwrap_or(Logger::new("test")); PaymentAdjusterReal { - analyzer: PreAdjustmentAnalyzer::new(), + analyzer: PreparatoryAnalyzer::new(), inner: Box::new(PaymentAdjusterInnerReal::new( now, None, cw_masq_balance_minor, )), + calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], logger, } } @@ -91,8 +94,8 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ }, ); - // This matters only if the constants participate in the calculator's formula. If that's not true, simply update - // the num sum and ignore the concern about synchronization + // This matters only if the constants participate in the calculator's formula. If that's not + // true, simply update the num sum and ignore the concern about synchronization let actual_sum: i128 = constants_and_expected_values .iter() .map(|(val, _)| *val) @@ -106,14 +109,23 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ ) } -pub struct CriterionCalculatorMock {} +pub struct CriterionCalculatorMock { + calculate_results: RefCell>, +} impl CriterionCalculator for CriterionCalculatorMock { - fn calculate(&self, account: &QualifiedPayableAccount) -> u128 { - todo!() + fn calculate(&self, _account: &QualifiedPayableAccount) -> u128 { + self.calculate_results.borrow_mut().remove(0) } fn parameter_name(&self) -> &'static str { - todo!() + "MOCKED CALCULATOR" + } +} + +impl CriterionCalculatorMock { + pub fn calculate_result(self, result: u128) -> Self { + self.calculate_results.borrow_mut().push(result); + self } } diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 4238bd8d5..613712cef 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -514,7 +514,7 @@ fn configure_accountant_config( Ok(()) } -fn check_payment_thresholds( +pub fn check_payment_thresholds( payment_thresholds: &PaymentThresholds, ) -> Result<(), ConfiguratorError> { if payment_thresholds.debt_threshold_gwei <= payment_thresholds.permanent_debt_allowed_gwei { diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 50430b88a..8bb201d43 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -61,7 +61,7 @@ impl Default for PaymentThresholds { //this code is used in tests in Accountant impl PaymentThresholds { - pub fn sugg_and_grace(&self, now: i64) -> i64 { + pub fn maturity_and_grace(&self, now: i64) -> i64 { now - checked_conversion::(self.maturity_threshold_sec) - checked_conversion::(self.payment_grace_period_sec) } @@ -191,6 +191,7 @@ impl MessageIdGenerator for MessageIdGeneratorReal { mod tests { use crate::accountant::test_utils::AccountantBuilder; use crate::accountant::{checked_conversion, Accountant}; + use crate::node_configurator::unprivileged_parse_args_configuration::check_payment_thresholds; use crate::sub_lib::accountant::{ AccountantSubsFactoryReal, MessageIdGenerator, MessageIdGeneratorReal, PaymentThresholds, ScanIntervals, SubsFactory, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, @@ -208,7 +209,8 @@ mod tests { impl PaymentThresholds { pub fn sugg_thru_decreasing(&self, now: i64) -> i64 { - self.sugg_and_grace(now) - checked_conversion::(self.threshold_interval_sec) + self.maturity_and_grace(now) + - checked_conversion::(self.threshold_interval_sec) } } @@ -233,6 +235,10 @@ mod tests { }; assert_eq!(*DEFAULT_SCAN_INTERVALS, scan_intervals_expected); assert_eq!(*DEFAULT_PAYMENT_THRESHOLDS, payment_thresholds_expected); + assert_eq!( + check_payment_thresholds(&DEFAULT_PAYMENT_THRESHOLDS), + Ok(()) + ); assert_eq!(*DEFAULT_EARNING_WALLET, default_earning_wallet_expected); assert_eq!( *TEMPORARY_CONSUMING_WALLET, From 92869e005832c78b4d2faa74a22c0132a93fa6b3 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 23 Mar 2024 17:24:22 +0100 Subject: [PATCH 141/250] GH-711-b: interim commit - still implementing new disqualification mechanism --- .../payment_adjuster/adjustment_runners.rs | 163 ++---------- .../disqualification_arbiter.rs | 243 ++++++++++++++++-- .../miscellaneous/data_structures.rs | 21 +- .../miscellaneous/helper_functions.rs | 17 +- node/src/accountant/payment_adjuster/mod.rs | 196 ++++++++++++-- ...ry_analyses.rs => preparatory_analyser.rs} | 237 ++++++++++------- .../accountant/payment_adjuster/test_utils.rs | 51 +++- 7 files changed, 611 insertions(+), 317 deletions(-) rename node/src/accountant/payment_adjuster/{preparatory_analyses.rs => preparatory_analyser.rs} (50%) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 7f8c2c487..ca9fa97d7 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -1,31 +1,27 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::diagnostics; -use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, + AdjustedAccountBeforeFinalization, WeightedAccount, }; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use crate::accountant::QualifiedPayableAccount; use itertools::Either; use std::vec; -// There are just two runners. Different by the adjustment they can perform, either adjusting by -// both the transaction fee and service fee, or exclusively by the transaction fee. The idea is -// that the adjustment by the transaction fee may ever appear in the initial iteration of -// the recursion. In any of the next iterations, if it proceed that far, this feature would be -// staying around useless. Therefor the runner with more features is used only at the beginning. -// Its speciality is that it allows also to short-circuit the weights computation for accounts, -// because it can detect that after some dropped accounts due to the transaction fee scarcity -// another adjustment, by the service fee, is not needed and therefore there is no point in going -// through any extra assessment. Mostly for the things just described each runner provides -// a different result type. +// There are only two runners. They perform adjustment either by both the transaction and service +// fee, or exclusively by the transaction fee. The idea is that the adjustment by the transaction +// fee may ever appear in the initial iteration of the recursion. In any of the other iterations, +// if it proceeded, this feature would be staying around useless. Therefor the runner with more +// features is used only at the beginning. Its benefit is that it also allows to short-circuit +// through the computation of the account weights, because it can detect that dropped accounts due +// to the transaction fee scarcity lowered demand for the service fee and this adjustment is not +// needed. For the things just described, each runner gives back a different result type. pub trait AdjustmentRunner { type ReturnType; // This specialized method: - // a) helps with writing tests that target edge cases, + // a) helps with writing tests targeting edge cases, // b) allows to avoid performing unnecessary computation for an evident result fn adjust_last_one( &self, @@ -53,9 +49,8 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: QualifiedPayableAccount, ) -> Self::ReturnType { - Ok(Either::Left(empty_or_single_element_vector( - adjust_last_one_opt(payment_adjuster, last_account), - ))) + let account_opt = payment_adjuster.adjust_last_account_opt(last_account); + Ok(Either::Left(empty_or_single_element_vector(account_opt))) } fn adjust_multiple( @@ -90,7 +85,8 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: QualifiedPayableAccount, ) -> Self::ReturnType { - empty_or_single_element_vector(adjust_last_one_opt(payment_adjuster, last_account)) + let account_opt = payment_adjuster.adjust_last_account_opt(last_account); + empty_or_single_element_vector(account_opt) } fn adjust_multiple( @@ -103,41 +99,6 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { } } -fn adjust_last_one_opt( - payment_adjuster: &PaymentAdjusterReal, - last_payable: QualifiedPayableAccount, -) -> Option { - let cw_balance = payment_adjuster - .inner - .unallocated_cw_service_fee_balance_minor(); - let proposed_adjusted_balance = - if last_payable.payable.balance_wei.checked_sub(cw_balance) == None { - last_payable.payable.balance_wei - } else { - diagnostics!( - "LAST REMAINING ACCOUNT", - "Balance adjusted to {} by exhausting the cw balance fully", - cw_balance - ); - - cw_balance - }; - let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - WeightedAccount::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up - proposed_adjusted_balance, - )]; - - let logger = &payment_adjuster.logger; - - match DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( - &proposed_adjustment_vec, - logger, - ) { - Some(_) => None, - None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), - } -} - fn empty_or_single_element_vector( adjusted_account_opt: Option, ) -> Vec { @@ -151,8 +112,8 @@ fn empty_or_single_element_vector( mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - adjust_last_one_opt, empty_or_single_element_vector, AdjustmentRunner, - ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, + empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, + TransactionAndServiceFeeAdjustmentRunner, }; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; @@ -168,13 +129,6 @@ mod tests { use std::fmt::Debug; use std::time::{Duration, SystemTime}; - fn prepare_payment_adjuster(cw_balance: u128, now: SystemTime) -> PaymentAdjusterReal { - let adjustment = Adjustment::ByServiceFee; - let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); - payment_adjuster - } - fn test_adjust_last_one( subject: AR, expected_return_type_finalizer: fn(Vec) -> RT, @@ -197,7 +151,9 @@ mod tests { ) .remove(0); let cw_balance = 8_645_123_505; - let mut payment_adjuster = prepare_payment_adjuster(cw_balance, now); + let adjustment = Adjustment::ByServiceFee; + let mut payment_adjuster = PaymentAdjusterReal::new(); + payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); let result = subject.adjust_last_one(&mut payment_adjuster, qualified_payable.clone()); @@ -225,87 +181,6 @@ mod tests { }) } - #[test] - fn adjust_last_one_for_requested_balance_smaller_than_cw_but_not_needed_disqualified() { - let now = SystemTime::now(); - let account_balance = 4_500_600; - let cw_balance = - DisqualificationArbiter::calculate_disqualification_edge(account_balance) + 1; - let payable = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: account_balance, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payable = QualifiedPayableAccount { - payable, - payment_threshold_intercept: 111222333, - }; - let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - - let result = adjust_last_one_opt(&payment_adjuster, qualified_payable.clone()); - - assert_eq!( - result, - Some(AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable, - proposed_adjusted_balance: cw_balance, - }) - ) - } - - fn test_adjust_last_one_when_disqualified( - cw_balance: u128, - account_balance: u128, - threshold_intercept: u128, - ) { - let now = SystemTime::now(); - let payable = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: account_balance, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payable = QualifiedPayableAccount { - payable, - payment_threshold_intercept: threshold_intercept, - }; - let payment_adjuster = prepare_payment_adjuster(cw_balance, now); - - let result = adjust_last_one_opt(&payment_adjuster, qualified_payable); - - assert_eq!(result, None) - } - - #[test] - fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_evens_the_edge() - { - let account_balance = 4_000_444; - let cw_balance = DisqualificationArbiter::calculate_disqualification_edge(account_balance); - let payment_threshold_intercept = 3_000_000_000; - - test_adjust_last_one_when_disqualified( - cw_balance, - account_balance, - payment_threshold_intercept, - ) - } - - #[test] - fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() - { - let account_balance = 4_000_444; - let cw_balance = - DisqualificationArbiter::calculate_disqualification_edge(account_balance) - 1; - let payment_threshold_intercept = 3_000_000_000; - - test_adjust_last_one_when_disqualified( - cw_balance, - account_balance, - payment_threshold_intercept, - ) - } - #[test] fn adjust_multiple_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 76fbf6f62..17ab4a621 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -8,20 +8,26 @@ use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_acco use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE; +use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; use std::cmp::Ordering; -pub struct DisqualificationArbiter {} +pub struct DisqualificationArbiter { + disqualification_gauge: Box, +} impl DisqualificationArbiter { + pub fn new(dsq_edge_detector: Box) -> Self { + todo!() + } pub fn try_finding_an_account_to_disqualify_in_this_iteration( + &self, unconfirmed_adjustments: &[UnconfirmedAdjustment], logger: &Logger, ) -> Option { let disqualification_suspected_accounts = - Self::list_accounts_nominated_for_disqualification(unconfirmed_adjustments); + self.list_accounts_nominated_for_disqualification(unconfirmed_adjustments); if !disqualification_suspected_accounts.is_empty() { let account_to_disqualify = @@ -55,23 +61,24 @@ impl DisqualificationArbiter { } } - pub fn calculate_disqualification_edge(account_balance: u128) -> u128 { - (ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier * account_balance) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor + pub fn calculate_disqualification_edge( + &self, + qualified_payable_account: &QualifiedPayableAccount, + ) -> u128 { + todo!() } fn list_accounts_nominated_for_disqualification( + &self, unconfirmed_adjustments: &[UnconfirmedAdjustment], ) -> Vec<&UnconfirmedAdjustment> { unconfirmed_adjustments .iter() .flat_map(|adjustment_info| { - let disqualification_edge = Self::calculate_disqualification_edge( - adjustment_info + let disqualification_edge = self.calculate_disqualification_edge( + &adjustment_info .non_finalized_account - .original_qualified_account - .payable - .balance_wei, + .original_qualified_account, ); let proposed_adjusted_balance = adjustment_info .non_finalized_account @@ -113,35 +120,222 @@ impl DisqualificationArbiter { } } +pub trait DisqualificationGauge { + fn determine_limit( + &self, + account_balance_wei: u128, + threshold_intercept_wei: u128, + permanent_debt_allowed_wei: u128, + ) -> u128; +} + +#[derive(Default)] +pub struct DisqualificationGaugeReal {} + +impl DisqualificationGauge for DisqualificationGaugeReal { + fn determine_limit( + &self, + account_balance_wei: u128, + threshold_intercept_wei: u128, + permanent_debt_allowed_wei: u128, + ) -> u128 { + todo!() + } +} + +impl DisqualificationGaugeReal { + fn qualifies_for_double_margin( + account_balance_wei: u128, + threshold_intercept_wei: u128, + permanent_debt_allowed_wei: u128, + ) -> bool { + let exceeding_part = account_balance_wei - threshold_intercept_wei; + let considered_forgiven = threshold_intercept_wei - permanent_debt_allowed_wei; + let extra_condition: bool = if considered_forgiven == 2 * permanent_debt_allowed_wei { + todo!() + } else if considered_forgiven < 2 * permanent_debt_allowed_wei { + todo!() + } else { + todo!() + }; + if exceeding_part > 2 * considered_forgiven && extra_condition { + todo!() + } else if exceeding_part == 2 * considered_forgiven && extra_condition { + todo!() + } else { + todo!() + } + } +} + #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, + }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + weights_total, EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, + }; use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, PRESERVED_TEST_PAYMENT_THRESHOLDS, + make_initialized_subject, DisqualificationGaugeMock, PRESERVED_TEST_PAYMENT_THRESHOLDS, + }; + use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + make_payable_account, }; - use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; use crate::accountant::QualifiedPayableAccount; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; use std::time::{Duration, SystemTime}; #[test] - fn calculate_disqualification_edge_works() { - let mut account = make_payable_account(111); - account.balance_wei = 300_000_000; + fn qualifies_for_double_margin_granted_on_both_conditions_returning_equals() { + let account_balance_wei = 6_000_000_000; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 3_000_000_000; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, true) + } + + #[test] + fn qualifies_for_double_margin_granted_on_first_condition_bigger_second_equal() { + let account_balance_wei = 6_000_000_001; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 3_000_000_000; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, true) + } + + #[test] + fn qualifies_for_double_margin_granted_on_first_condition_equal_second_bigger() { + let account_balance_wei = 6_000_000_002; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 3_000_000_001; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, true) + } + + fn qualifies_for_double_margin_granted_on_both_conditions_returning_bigger() { + let account_balance_wei = 6_000_000_003; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 3_000_000_001; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, true) + } + + fn qualifies_for_double_margin_declined_on_first_condition() { + let account_balance_wei = 5_999_999_999; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 3_000_000_000; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, false) + } + + fn qualifies_for_double_margin_declined_on_second_condition() { + let account_balance_wei = 6_000_000_000; + let permanent_debt_allowed_wei = 1_000_000_000; + let threshold_intercept_wei = 2_999_999_999; + + let result = DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_wei, + permanent_debt_allowed_wei, + threshold_intercept_wei, + ); + + assert_eq!(result, false) + } + + #[test] + fn calculate_disqualification_edge_in_the_horizontal_thresholds_area() { + let balance_wei = 30_000_000_000; + let threshold_intercept_wei = 4_000_000_000; + let permanent_debt_allowed_wei = 4_000_000_000; + let subject = DisqualificationGaugeReal::default(); + + todo!("finish the setup when pda can be added"); + let result = subject.determine_limit( + balance_wei, + threshold_intercept_wei, + permanent_debt_allowed_wei, + ); + + assert_eq!(result, 30_000_000_000) + } + + #[test] + fn calculate_disqualification_edge_in_the_tilted_thresholds_area_with_normal_margin() { + let balance_wei = 6_000_000_000; + let threshold_intercept_wei = 4_000_000_000; + let permanent_debt_allowed_wei = 1_000_000_000; + let subject = DisqualificationGaugeReal::default(); + + todo!("finish the setup when pda can be added"); + let result = subject.determine_limit( + balance_wei, + threshold_intercept_wei, + permanent_debt_allowed_wei, + ); - let result = DisqualificationArbiter::calculate_disqualification_edge(account.balance_wei); + assert_eq!(result, (30_000_000_000 - 4_000_000_000) + 1_000_000_000) + } - assert_eq!(result, todo!()) + #[test] + fn calculate_disqualification_edge_in_the_tilted_thresholds_area_with_double_margin() { + let balance_wei = 30_000_000_000; + let threshold_intercept_wei = 4_000_000_000; + let permanent_debt_allowed_wei = 1_000_000_000; + let subject = DisqualificationGaugeReal::default(); + + todo!("finish the setup when pda can be added"); + let result = subject.determine_limit( + balance_wei, + threshold_intercept_wei, + permanent_debt_allowed_wei, + ); + + assert_eq!( + result, + (30_000_000_000 - 4_000_000_000) + (2 * 1_000_000_000) + ) } #[test] fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { + todo!("do I still want this test??"); let account_balance = 1_000_000; let garbage_weight = 22222222; // It plays no role let garbage_thresholds_intercept = 456789; // Also no role @@ -153,7 +347,7 @@ mod tests { let payable_account_1 = prepare_account(1); let payable_account_2 = prepare_account(2); let payable_account_3 = prepare_account(3); - let edge = DisqualificationArbiter::calculate_disqualification_edge(account_balance); + let edge: u128 = todo!(); let proposed_ok_balance = edge + 1; let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( WeightedAccount::new( @@ -183,10 +377,9 @@ mod tests { unconfirmed_adjustment_2.clone(), unconfirmed_adjustment_3.clone(), ]; + let subject = DisqualificationArbiter::new(Box::new(DisqualificationGaugeReal::default())); - let result = DisqualificationArbiter::list_accounts_nominated_for_disqualification( - &unconfirmed_adjustments, - ); + let result = subject.list_accounts_nominated_for_disqualification(&unconfirmed_adjustments); let expected_disqualified_accounts = vec![&unconfirmed_adjustment_2, &unconfirmed_adjustment_3]; @@ -276,6 +469,8 @@ mod tests { let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); + let disqualification_gauge = DisqualificationGaugeMock::default(); + let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let result = DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index d3aecb04f..64bdcca0a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,6 +1,7 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::QualifiedPayableAccount; +use web3::types::U256; #[derive(Clone)] pub struct WeightedAccount { @@ -122,13 +123,19 @@ pub struct PercentageAccountInsignificance { pub divisor: u128, } +impl PercentageAccountInsignificance { + pub fn compute_reduction(&self, debt_part_above_threshold_wei: u128) -> u128 { + todo!() + } +} + pub struct TransactionCountsWithin16bits { pub affordable: u16, pub required: u16, } impl TransactionCountsWithin16bits { - pub fn new(max_possible_tx_count: u128, number_of_accounts: usize) -> Self { + pub fn new(max_possible_tx_count: U256, number_of_accounts: usize) -> Self { TransactionCountsWithin16bits { affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), @@ -142,6 +149,7 @@ mod tests { AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, }; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use ethereum_types::U256; #[test] fn merging_results_from_recursion_works() { @@ -179,9 +187,11 @@ mod tests { #[test] fn there_is_u16_ceiling_for_possible_tx_count() { - let result = [-3_i8, -1, 0, 1, 10] + let corrections_from_u16_max = [-3_i8, -1, 0, 1, 10]; + let result = corrections_from_u16_max .into_iter() - .map(|correction| plus_minus_correction_of_u16_max(correction) as u128) + .map(|correction| plus_minus_correction_of_u16_max(correction)) + .map(U256::from) .map(|max_possible_tx_count| { let detected_tx_counts = TransactionCountsWithin16bits::new(max_possible_tx_count, 123); @@ -197,12 +207,13 @@ mod tests { #[test] fn there_is_u16_ceiling_for_required_number_of_accounts() { - let result = [-9_i8, -1, 0, 1, 5] + let corrections_from_u16_max = [-9_i8, -1, 0, 1, 5]; + let result = corrections_from_u16_max .into_iter() .map(|correction| plus_minus_correction_of_u16_max(correction)) .map(|required_tx_count_usize| { let detected_tx_counts = - TransactionCountsWithin16bits::new(123, required_tx_count_usize); + TransactionCountsWithin16bits::new(U256::from(123), required_tx_count_usize); detected_tx_counts.required }) .collect::>(); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 001457352..e0a1fd41f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -23,7 +23,7 @@ use web3::types::U256; const MAX_EXPONENT_FOR_10_WITHIN_U128: u32 = 76; const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; // Represents 50% -pub const ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE: PercentageAccountInsignificance = +pub const EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO: PercentageAccountInsignificance = PercentageAccountInsignificance { multiplier: 1, divisor: 2, @@ -52,7 +52,7 @@ pub fn weights_total(weights_and_accounts: &[WeightedAccount]) -> u128 { }) } -pub fn drop_unaffordable_accounts_due_to_service_fee( +pub fn dump_unaffordable_accounts_by_txn_fee( weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, ) -> Vec { @@ -83,7 +83,7 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( // of pure blockchains with no application-specific smart contract } -pub fn resolve_possibly_outweighed_account( +pub fn adjust_account_balance_if_outweighed( (mut outweighed, mut passing_through): (Vec, Vec), mut current_adjustment_info: UnconfirmedAdjustment, ) -> (Vec, Vec) { @@ -305,10 +305,11 @@ mod tests { WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, - log_10, resolve_possibly_outweighed_account, weights_total, zero_affordable_accounts_found, - ConsumingWalletExhaustingStatus, ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, - EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, + log_10, weights_total, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, + EMPIRIC_PRECISION_COEFFICIENT, EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, + MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -332,7 +333,7 @@ mod tests { assert_eq!(MAX_EXPONENT_FOR_10_WITHIN_U128, 76); assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); assert_eq!( - ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, + EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, PercentageAccountInsignificance { multiplier: 1, divisor: 2 @@ -576,7 +577,7 @@ mod tests { let init = (vec![], vec![]); let (outweighed, ok) = - resolve_possibly_outweighed_account(init, unconfirmed_adjustment.clone()); + adjust_account_balance_if_outweighed(init, unconfirmed_adjustment.clone()); assert_eq!(outweighed, vec![]); assert_eq!(ok, vec![unconfirmed_adjustment]) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 232b45279..5239a5693 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -10,7 +10,7 @@ mod inner; mod loading_test; mod log_fns; mod miscellaneous; -mod preparatory_analyses; +mod preparatory_analyser; #[cfg(test)] mod test_utils; @@ -30,8 +30,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::Require TreatInsignificantAccount, TreatOutweighedAccounts, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, resolve_possibly_outweighed_account, drop_no_longer_needed_weights_away_from_accounts, drop_unaffordable_accounts_due_to_service_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; -use crate::accountant::payment_adjuster::preparatory_analyses::{PreparatoryAnalyzer}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, adjust_account_balance_if_outweighed, drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; +use crate::accountant::payment_adjuster::preparatory_analyser::{PreparatoryAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -46,7 +46,7 @@ use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::{CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; -use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; +use crate::accountant::payment_adjuster::disqualification_arbiter::{DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal}; use crate::accountant::QualifiedPayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -69,6 +69,7 @@ pub trait PaymentAdjuster { pub struct PaymentAdjusterReal { analyzer: PreparatoryAnalyzer, + disqualification_arbiter: DisqualificationArbiter, inner: Box, calculators: Vec>, logger: Logger, @@ -100,9 +101,10 @@ impl PaymentAdjuster for PaymentAdjusterReal { let service_fee_balance_minor = agent.service_fee_balance_minor(); match self.analyzer.check_need_of_adjustment_by_service_fee( - &self.logger, + &self.disqualification_arbiter, Either::Left(qualified_payables), service_fee_balance_minor, + &self.logger, ) { Ok(false) => Ok(None), Ok(true) => Ok(Some(Adjustment::ByServiceFee)), @@ -149,6 +151,9 @@ impl PaymentAdjusterReal { pub fn new() -> Self { Self { analyzer: PreparatoryAnalyzer::new(), + disqualification_arbiter: DisqualificationArbiter::new(Box::new( + DisqualificationGaugeReal::default(), + )), inner: Box::new(PaymentAdjusterInnerNull {}), calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], logger: Logger::new("PaymentAdjuster"), @@ -244,18 +249,19 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let accounts_with_criteria_affordable_by_transaction_fee = - drop_unaffordable_accounts_due_to_service_fee( - weighted_accounts_in_descending_order, - already_known_affordable_transaction_count, - ); + let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_txn_fee( + weighted_accounts_in_descending_order, + already_known_affordable_transaction_count, + ); + let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); let is_service_fee_adjustment_needed = match self.analyzer.check_need_of_adjustment_by_service_fee( - &self.logger, - Either::Right(&accounts_with_criteria_affordable_by_transaction_fee), + &self.disqualification_arbiter, + Either::Right(&weighted_accounts_affordable_by_transaction_fee), cw_service_fee_balance, + &self.logger, ) { Ok(answer) => answer, Err(e) => { @@ -270,14 +276,14 @@ impl PaymentAdjusterReal { let adjustment_result_before_verification = self .propose_possible_adjustment_recursively( - accounts_with_criteria_affordable_by_transaction_fee, + weighted_accounts_affordable_by_transaction_fee, ); Ok(Either::Left(adjustment_result_before_verification)) } false => { let accounts_not_needing_adjustment = drop_no_longer_needed_weights_away_from_accounts( - accounts_with_criteria_affordable_by_transaction_fee, + weighted_accounts_affordable_by_transaction_fee, ); Ok(Either::Right(accounts_not_needing_adjustment)) } @@ -400,10 +406,9 @@ impl PaymentAdjusterReal { Either::Right(with_some_outweighed) => return with_some_outweighed, }; - let verified_accounts = match Self::consider_account_disqualification( - still_unchecked_for_disqualified, - &self.logger, - ) { + let verified_accounts = match self + .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) + { Either::Left(verified_accounts) => verified_accounts, Either::Right(with_some_disqualified) => return with_some_disqualified, }; @@ -473,11 +478,13 @@ impl PaymentAdjusterReal { } fn consider_account_disqualification( + &self, unconfirmed_adjustments: Vec, logger: &Logger, ) -> Either, AdjustmentIterationResult> { - if let Some(disqualified_account_wallet) = - DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( + if let Some(disqualified_account_wallet) = self + .disqualification_arbiter + .try_finding_an_account_to_disqualify_in_this_iteration( &unconfirmed_adjustments, logger, ) @@ -524,7 +531,7 @@ impl PaymentAdjusterReal { let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments .into_iter() - .fold(init, resolve_possibly_outweighed_account); + .fold(init, adjust_account_balance_if_outweighed); if outweighed.is_empty() { Either::Left(properly_adjusted_accounts) @@ -581,6 +588,40 @@ impl PaymentAdjusterReal { accounts_before_and_after_debug(sketched_debug_info, affordable_accounts) }) } + + fn adjust_last_account_opt( + &self, + last_payable: QualifiedPayableAccount, + ) -> Option { + let cw_balance = self.inner.unallocated_cw_service_fee_balance_minor(); + let proposed_adjusted_balance = + if last_payable.payable.balance_wei.checked_sub(cw_balance) == None { + last_payable.payable.balance_wei + } else { + diagnostics!( + "LAST REMAINING ACCOUNT", + "Balance adjusted to {} by exhausting the cw balance fully", + cw_balance + ); + + cw_balance + }; + // TODO the Disqualification check really makes sense only if we assigned less than the full balance!! + let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( + WeightedAccount::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up + proposed_adjusted_balance, + )]; + + match self + .disqualification_arbiter + .try_finding_an_account_to_disqualify_in_this_iteration( + &proposed_adjustment_vec, + &self.logger, + ) { + Some(_) => None, + None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), + } + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -646,10 +687,10 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustmentIterationResult, RequiredSpecialTreatment}; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE, weights_total}; - use crate::accountant::payment_adjuster::test_utils::{make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, weights_total}; + use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; @@ -662,8 +703,10 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; + use std::sync::{Arc, Mutex}; use thousands::Separable; use web3::types::U256; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -1036,7 +1079,7 @@ mod tests { ) { // NOTE that the same is true for more outweighed accounts that would require more than // the whole cw balance together, therefore there is no such a test either. - // This test answers the question what is happening when the cw service fee balance cannot + // This test answers the question of what is happening when the cw service fee balance cannot // cover the outweighed accounts, which is just a hypothesis we can never reach in // the reality. // If there are outweighed accounts some other accounts must be also around of which some @@ -1121,8 +1164,8 @@ mod tests { subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); let service_fee_balance_in_minor_units = ((1_000_000_000_000 - * ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.multiplier) - / ACCOUNT_INSIGNIFICANCE_BY_PERCENTAGE.divisor) + * EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.multiplier) + / EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.divisor) - 1; let agent = { let mock = BlockchainAgentMock::default() @@ -1220,6 +1263,103 @@ mod tests { )); } + fn prepare_subject( + cw_balance: u128, + now: SystemTime, + disqualification_gauge: DisqualificationGaugeMock, + ) -> PaymentAdjusterReal { + let adjustment = Adjustment::ByServiceFee; + let mut payment_adjuster = PaymentAdjusterReal::new(); + todo!("is this necessary?"); + //payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); + payment_adjuster.disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + payment_adjuster + } + + #[test] + fn adjust_last_account_opt_for_balance_smaller_than_cw_but_no_need_to_disqualify() { + let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); + let now = SystemTime::now(); + let account_balance = 4_500_600; + let cw_balance = 4_500_601; + let payable = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: account_balance, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: 111222333, + }; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_params(&determine_limit_params_arc) + .determine_limit_result(4_500_600); + let payment_adjuster = prepare_subject(cw_balance, now, disqualification_gauge); + + let result = payment_adjuster.adjust_last_account_opt(qualified_payable.clone()); + + assert_eq!( + result, + Some(AdjustedAccountBeforeFinalization { + original_qualified_account: qualified_payable.clone(), + proposed_adjusted_balance: cw_balance, + }) + ); + let determine_limit_params = determine_limit_params_arc.lock().unwrap(); + assert_eq!( + *determine_limit_params, + vec![( + qualified_payable.payable.balance_wei, + qualified_payable.payment_threshold_intercept, + todo!() + )] + ) + } + + fn test_adjust_last_account_opt_when_account_disqualified( + cw_balance: u128, + disqualification_gauge: DisqualificationGaugeMock, + ) { + let now = SystemTime::now(); + let payable = PayableAccount { + wallet: make_wallet("abc"), + balance_wei: 3 * cw_balance, // Unimportant. Mock in use, + last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + pending_payable_opt: None, + }; + let qualified_payable = QualifiedPayableAccount { + payable, + payment_threshold_intercept: cw_balance / 2, // Unimportant. Mock in use + }; + let payment_adjuster = prepare_subject(cw_balance, now, disqualification_gauge); + + let result = payment_adjuster.adjust_last_account_opt(qualified_payable); + + assert_eq!(result, None) + } + + #[test] + fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_evens_the_edge() + { + let cw_balance = 1_000_111; + let disqualification_gauge = + DisqualificationGaugeMock::default().determine_limit_result(1_000_111); + + test_adjust_last_account_opt_when_account_disqualified(cw_balance, disqualification_gauge) + } + + #[test] + fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() + { + let cw_balance = 1_000_111; + let disqualification_gauge = + DisqualificationGaugeMock::default().determine_limit_result(1_000_110); + + test_adjust_last_account_opt_when_account_disqualified(cw_balance, disqualification_gauge) + } + #[test] fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { init_test_logging(); @@ -1245,7 +1385,7 @@ mod tests { ); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - // In turn, extremely small cw balance + // In turn, tiny cw balance let cw_service_fee_balance = 1_000; let agent = { let mock = BlockchainAgentMock::default() diff --git a/node/src/accountant/payment_adjuster/preparatory_analyses.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs similarity index 50% rename from node/src/accountant/payment_adjuster/preparatory_analyses.rs rename to node/src/accountant/payment_adjuster/preparatory_analyser.rs index bd66d1b68..19baf5475 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyses.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -15,6 +15,8 @@ use crate::accountant::QualifiedPayableAccount; use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; +use masq_lib::utils::type_name_of; +use std::cmp::Ordering; pub struct PreparatoryAnalyzer {} @@ -33,16 +35,14 @@ impl PreparatoryAnalyzer { let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction_minor(); - let max_possible_tx_count = Self::max_possible_tx_count( + let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, per_transaction_requirement_minor, + number_of_qualified_accounts, ); - let detected_tx_counts = - TransactionCountsWithin16bits::new(max_possible_tx_count, number_of_qualified_accounts); - - let max_tx_count_we_can_afford_u16 = detected_tx_counts.affordable; - let required_tx_count_u16 = detected_tx_counts.required; + let max_tx_count_we_can_afford_u16 = verified_tx_counts.affordable; + let required_tx_count_u16 = verified_tx_counts.required; if max_tx_count_we_can_afford_u16 == 0 { Err( @@ -65,55 +65,46 @@ impl PreparatoryAnalyzer { } } + fn transaction_counts_verification( + cw_transaction_fee_balance_minor: U256, + per_transaction_requirement_minor: u128, + number_of_qualified_accounts: usize, + ) -> TransactionCountsWithin16bits { + let max_possible_tx_count_u256 = Self::max_possible_tx_count( + cw_transaction_fee_balance_minor, + per_transaction_requirement_minor, + ); + TransactionCountsWithin16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) + } + fn max_possible_tx_count( cw_transaction_fee_balance_minor: U256, tx_fee_requirement_per_tx_minor: u128, - ) -> u128 { - let max_possible_tx_count_u256 = - cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor); - u128::try_from(max_possible_tx_count_u256).unwrap_or_else(|e| { - panic!( - "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ - transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ - respectively: \"{}\"", - cw_transaction_fee_balance_minor, - tx_fee_requirement_per_tx_minor, - max_possible_tx_count_u256, - e - ) - }) + ) -> U256 { + cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor) } pub fn check_need_of_adjustment_by_service_fee( &self, - logger: &Logger, + disqualification_arbiter: &DisqualificationArbiter, payables: Either<&[QualifiedPayableAccount], &[WeightedAccount]>, cw_service_fee_balance_minor: u128, + logger: &Logger, ) -> Result { - let qualified_payables: Vec<&PayableAccount> = match payables { - Either::Left(accounts) => accounts - .iter() - .map(|qualified_payable| &qualified_payable.payable) - .collect(), - Either::Right(weighted_accounts) => weighted_accounts - .iter() - .map(|weighted_account| &weighted_account.qualified_account.payable) - .collect(), - }; + let qualified_payables = Self::welcome_qualified_payables(payables); let required_service_fee_sum: u128 = - sum_as(&qualified_payables, |account: &&PayableAccount| { - account.balance_wei - }); + sum_as(&qualified_payables, |qa| qa.payable.balance_wei); if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { - ServiceFeeAdjustmentPossibilityAnalyser {} - .analyse_lowest_detectable_adjustment_possibility( - &qualified_payables, - cw_service_fee_balance_minor, - )?; + // TODO here you need to iterate through all and compute the edge and choose the smallest one + self.analyse_smallest_adjustment_possibility( + disqualification_arbiter, + &qualified_payables, + cw_service_fee_balance_minor, + )?; log_adjustment_by_service_fee_is_required( logger, @@ -123,37 +114,46 @@ impl PreparatoryAnalyzer { Ok(true) } } -} -pub struct ServiceFeeAdjustmentPossibilityAnalyser {} + fn welcome_qualified_payables<'payables>( + payables: Either<&'payables [QualifiedPayableAccount], &'payables [WeightedAccount]>, + ) -> Vec<&'payables QualifiedPayableAccount> { + match payables { + Either::Left(accounts) => accounts.iter().collect(), + Either::Right(weighted_accounts) => weighted_accounts + .iter() + .map(|weighted_account| &weighted_account.qualified_account) + .collect(), + } + } -impl ServiceFeeAdjustmentPossibilityAnalyser { // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with - // the assumption that the smallest debt in the set of accounts fits under the disqualification - // limit. If it doesn't, we won't want to bother the payment adjuster by that work, so we're - // going to abort, no payments coming out. - pub fn analyse_lowest_detectable_adjustment_possibility( + // the assumption that the debt with the lowest disqualification limit in the set fits in the + // available balance. If it doesn't, we won't want to bother the payment adjuster by its work, + // so we'll abort and no payments will come out. + fn analyse_smallest_adjustment_possibility( &self, - accounts: &[&PayableAccount], + disqualification_arbiter: &DisqualificationArbiter, + qualified_payables: &[&QualifiedPayableAccount], cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - let sorted = accounts + let lowest_disqualification_limit = qualified_payables .iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_a.balance_wei, &account_b.balance_wei) - }) - .collect::>(); - let smallest_account = sorted.first().expect("should be one at minimum"); + .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) + .fold(todo!(), |lowest, limit| match Ord::cmp(&lowest, &limit) { + Ordering::Less => todo!(), + Ordering::Equal => todo!(), + Ordering::Greater => todo!(), + }); - if DisqualificationArbiter::calculate_disqualification_edge(smallest_account.balance_wei) - <= cw_service_fee_balance_minor - { + if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(()) } else { - let total_amount_demanded_minor = sum_as(accounts, |account| account.balance_wei); + let total_amount_demanded_minor = + sum_as(qualified_payables, |qp| qp.payable.balance_wei); Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: accounts.len(), + number_of_accounts: qualified_payables.len(), total_amount_demanded_minor, cw_service_fee_balance_minor, }, @@ -165,16 +165,21 @@ impl ServiceFeeAdjustmentPossibilityAnalyser { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::preparatory_analyses::{ - PreparatoryAnalyzer, ServiceFeeAdjustmentPossibilityAnalyser, + use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGaugeReal, }; + use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; + use crate::accountant::payment_adjuster::test_utils::DisqualificationGaugeMock; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::accountant::test_utils::make_payable_account; + use crate::accountant::test_utils::{ + make_non_guaranteed_qualified_payable, make_payable_account, + }; + use crate::accountant::QualifiedPayableAccount; use ethereum_types::U256; use masq_lib::logger::Logger; use std::panic::{catch_unwind, AssertUnwindSafe}; + use std::sync::{Arc, Mutex}; #[test] fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() @@ -210,32 +215,61 @@ mod tests { } fn test_body_for_adjustment_possibility_nearly_rejected( - original_accounts: Vec, + disqualification_gauge: DisqualificationGaugeMock, + original_accounts: [QualifiedPayableAccount; 2], cw_service_fee_balance: u128, ) { - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - let subject = ServiceFeeAdjustmentPossibilityAnalyser {}; + let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); + let disqualification_gauge = + disqualification_gauge.determine_limit_params(&determine_limit_params_arc); + let accounts_in_expected_format = original_accounts + .iter() + .collect::>(); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let subject = PreparatoryAnalyzer {}; - let result = subject.analyse_lowest_detectable_adjustment_possibility( + let result = subject.analyse_smallest_adjustment_possibility( + &disqualification_arbiter, &accounts_in_expected_format, cw_service_fee_balance, ); - assert_eq!(result, Ok(())) + assert_eq!(result, Ok(())); + let determine_limit_params = determine_limit_params_arc.lock().unwrap(); + let account_1 = &original_accounts[0]; + let account_2 = &original_accounts[1]; + assert_eq!( + *determine_limit_params, + vec![ + ( + account_1.payable.balance_wei, + account_1.payment_threshold_intercept, + todo!() + ), + ( + account_2.payable.balance_wei, + account_2.payment_threshold_intercept, + todo!() + ) + ] + ) } #[test] - fn adjustment_possibility_nearly_rejected_when_cw_balance_one_more() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = - DisqualificationArbiter::calculate_disqualification_edge(account_2.balance_wei) + 1; - let original_accounts = vec![account_1, account_2]; + fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.payable.balance_wei = 1_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(333); + account_2.payable.balance_wei = 2_000_000_000; + let cw_service_fee_balance = 750_000_001; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(750_000_000) + .determine_limit_result(1_500_000_000); + let original_accounts = [account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected( + disqualification_gauge, original_accounts, cw_service_fee_balance, ) @@ -243,37 +277,46 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(333); - account_2.balance_wei = 1_000_000_000; - let cw_service_fee_balance = - DisqualificationArbiter::calculate_disqualification_edge(account_2.balance_wei); - let original_accounts = vec![account_1, account_2]; + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.payable.balance_wei = 2_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(333); + account_2.payable.balance_wei = 1_000_000_000; + let cw_service_fee_balance = 750_000_000; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(1_500_000_000) + .determine_limit_result(750_000_000); + let original_accounts = [account_1, account_2]; test_body_for_adjustment_possibility_nearly_rejected( + disqualification_gauge, original_accounts, cw_service_fee_balance, ) } #[test] - fn adjustment_possibility_err_from_insufficient_balance_for_at_least_single_account_adjustment() - { - let mut account_1 = make_payable_account(111); - account_1.balance_wei = 2_000_000_000; - let mut account_2 = make_payable_account(222); - account_2.balance_wei = 2_000_000_002; - let mut account_3 = make_payable_account(333); - account_3.balance_wei = 1_000_000_002; - let cw_service_fee_balance = - DisqualificationArbiter::calculate_disqualification_edge(account_3.balance_wei) - 1; + fn adjustment_possibility_err_from_insufficient_balance_for_even_the_least_demanding_account() { + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.payable.balance_wei = 2_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(222); + account_2.payable.balance_wei = 1_000_000_000; + let mut account_3 = make_non_guaranteed_qualified_payable(333); + account_3.payable.balance_wei = 1_000_100_000; + let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; - let accounts_in_expected_format = - original_accounts.iter().collect::>(); - let subject = ServiceFeeAdjustmentPossibilityAnalyser {}; + let accounts_in_expected_format = original_accounts + .iter() + .collect::>(); + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(1_500_000_000) + .determine_limit_result(1_000_000_101) + .determine_limit_result(1_000_123_000); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let subject = PreparatoryAnalyzer {}; - let result = subject.analyse_lowest_detectable_adjustment_possibility( + let result = subject.analyse_smallest_adjustment_possibility( + &disqualification_arbiter, &accounts_in_expected_format, cw_service_fee_balance, ); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index b35db28c4..ff47745e9 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -5,8 +5,9 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; -use crate::accountant::payment_adjuster::preparatory_analyses::PreparatoryAnalyzer; +use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::accountant::PaymentThresholds; @@ -16,6 +17,7 @@ use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; use std::cell::RefCell; +use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; lazy_static! { @@ -31,16 +33,14 @@ pub fn make_initialized_subject( ) -> PaymentAdjusterReal { let cw_masq_balance_minor = cw_masq_balance_minor_opt.unwrap_or(0); let logger = logger_opt.unwrap_or(Logger::new("test")); - PaymentAdjusterReal { - analyzer: PreparatoryAnalyzer::new(), - inner: Box::new(PaymentAdjusterInnerReal::new( - now, - None, - cw_masq_balance_minor, - )), - calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], - logger, - } + let mut subject = PaymentAdjusterReal::new(); + subject.logger = logger; + subject.inner = Box::new(PaymentAdjusterInnerReal::new( + now, + None, + cw_masq_balance_minor, + )); + subject } pub fn make_extreme_payables( @@ -129,3 +129,32 @@ impl CriterionCalculatorMock { self } } + +#[derive(Default)] +pub struct DisqualificationGaugeMock { + determine_limit_params: Arc>>, + determine_limit_results: RefCell>, +} + +impl DisqualificationGauge for DisqualificationGaugeMock { + fn determine_limit( + &self, + account_balance_wei: u128, + threshold_intercept_wei: u128, + permanent_debt_allowed_wei: u128, + ) -> u128 { + todo!() + } +} + +impl DisqualificationGaugeMock { + pub fn determine_limit_params(mut self, params: &Arc>>) -> Self { + self.determine_limit_params = params.clone(); + self + } + + pub fn determine_limit_result(mut self, result: u128) -> Self { + self.determine_limit_results.borrow_mut().push(result); + self + } +} From 3c5bf3b2844aa917b494f0d85374822fd7ec53a6 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 23 Mar 2024 22:44:48 +0100 Subject: [PATCH 142/250] GH-711-b: interim commit - before adding permanent_debt_allowed in QlPayable - tests still not compiling --- .../disqualification_arbiter.rs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 17ab4a621..db37ffb99 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -432,12 +432,10 @@ mod tests { let cw_masq_balance = 200_000_000_000; let logger = Logger::new(test_name); let subject = make_initialized_subject(now, Some(cw_masq_balance), None); - // None of these accounts would be outside the definition for disqualification - // even if any of them would be gifted by the complete balance from the cw let wallet_1 = make_wallet("abc"); let account_1 = PayableAccount { wallet: wallet_1.clone(), - balance_wei: 120_000_000_001, + balance_wei: 120_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), pending_payable_opt: None, }; @@ -445,21 +443,21 @@ mod tests { let account_2 = PayableAccount { wallet: wallet_2.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_001)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghi"); let account_3 = PayableAccount { wallet: wallet_3.clone(), - balance_wei: 119_999_999_999, - last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), + balance_wei: 120_000_000_000, + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_003)).unwrap(), pending_payable_opt: None, }; let wallet_4 = make_wallet("jkl"); let account_4 = PayableAccount { wallet: wallet_4.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(999_999)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_002)).unwrap(), pending_payable_opt: None, }; let accounts = vec![account_1, account_2, account_3, account_4]; @@ -469,14 +467,12 @@ mod tests { let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); - let disqualification_gauge = DisqualificationGaugeMock::default(); - let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let subject = DisqualificationArbiter::new(Box::new(DisqualificationGaugeReal::default())); - let result = - DisqualificationArbiter::try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - &logger, - ); + let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + &logger, + ); assert_eq!(result, Some(wallet_3)); } From d87e064b783edb275f7e88ad059f529dd543a3a8 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 24 Mar 2024 00:14:43 +0100 Subject: [PATCH 143/250] GH-711-b: tests compiling but failing --- node/src/accountant/mod.rs | 49 +++-- .../payment_adjuster/adjustment_runners.rs | 4 +- .../payment_adjuster/diagnostics.rs | 8 +- .../disqualification_arbiter.rs | 60 +++-- .../accountant/payment_adjuster/log_fns.rs | 4 +- .../miscellaneous/data_structures.rs | 19 +- .../miscellaneous/helper_functions.rs | 59 ++--- node/src/accountant/payment_adjuster/mod.rs | 206 ++++++++++-------- .../payment_adjuster/preparatory_analyser.rs | 8 +- node/src/accountant/scanners/mod.rs | 47 ++-- .../src/accountant/scanners/scanners_utils.rs | 45 ++-- node/src/accountant/test_utils.rs | 15 +- 12 files changed, 293 insertions(+), 231 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 7b91db361..2bcbfa262 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -132,18 +132,31 @@ pub struct ReceivedPayments { #[derive(Debug, PartialEq, Eq, Clone)] pub struct QualifiedPayableAccount { pub payable: PayableAccount, - pub payment_threshold_intercept: u128, + pub payment_threshold_intercept_minor: u128, + pub creditor_thresholds: CreditorThresholds, } impl QualifiedPayableAccount { pub fn new( payable_account: PayableAccount, payment_threshold_intercept: u128, + creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { todo!() } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct CreditorThresholds { + pub permanent_debt_allowed_wei: u128, +} + +impl CreditorThresholds { + pub fn new(permanent_debt_allowed_wei: u128) -> Self { + todo!() + } +} + #[derive(Debug, Message, PartialEq)] pub struct SentPayables { pub payment_procedure_result: Result, PayableTransactionError>, @@ -1452,14 +1465,8 @@ mod tests { let expected_agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); let qualified_payables = vec![ - QualifiedPayableAccount { - payable: account_1.clone(), - payment_threshold_intercept: 2345, - }, - QualifiedPayableAccount { - payable: account_2.clone(), - payment_threshold_intercept: 6789, - }, + QualifiedPayableAccount::new(account_1.clone(), 2345, CreditorThresholds::new(1111)), + QualifiedPayableAccount::new(account_2.clone(), 6789, CreditorThresholds::new(2222)), ]; let msg = BlockchainAgentWithContextMessage { protected_qualified_payables: protect_qualified_payables_in_test( @@ -1520,14 +1527,16 @@ mod tests { .start() .recipient(); let mut subject = AccountantBuilder::default().build(); - let unadjusted_account_1 = QualifiedPayableAccount { - payable: make_payable_account(111_111), - payment_threshold_intercept: 1234567, - }; - let unadjusted_account_2 = QualifiedPayableAccount { - payable: make_payable_account(222_222), - payment_threshold_intercept: 444555666, - }; + let unadjusted_account_1 = QualifiedPayableAccount::new( + make_payable_account(111_111), + 1234567, + CreditorThresholds::new(1111111), + ); + let unadjusted_account_2 = QualifiedPayableAccount::new( + make_payable_account(999_999), + 444555666, + CreditorThresholds::new(111111111), + ); let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), ..unadjusted_account_1.payable.clone() @@ -1647,10 +1656,8 @@ mod tests { subject.scanners.payable.mark_as_started(scan_started_at); let subject_addr = subject.start(); let account = make_payable_account(111_111); - let qualified_payable = QualifiedPayableAccount { - payable: account, - payment_threshold_intercept: 123456, - }; + let qualified_payable = + QualifiedPayableAccount::new(account, 123456, CreditorThresholds::new(111111)); let system = System::new(test_name); let agent = BlockchainAgentMock::default(); let protected_qualified_payables = diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index ca9fa97d7..92b0958b0 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -161,7 +161,7 @@ mod tests { result, expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { original_qualified_account: qualified_payable, - proposed_adjusted_balance: cw_balance, + proposed_adjusted_balance_minor: cw_balance, }]) ) } @@ -229,7 +229,7 @@ mod tests { fn empty_or_single_element_vector_for_some() { let account_info = AdjustedAccountBeforeFinalization { original_qualified_account: make_non_guaranteed_qualified_payable(123), - proposed_adjusted_balance: 123_456_789, + proposed_adjusted_balance_minor: 123_456_789, }; let result = empty_or_single_element_vector(Some(account_info.clone())); diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index dee84579b..379a11a17 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -113,7 +113,7 @@ pub mod ordinary_diagnostic_functions { .balance_wei .separate_with_commas(), account_info - .proposed_adjusted_balance + .proposed_adjusted_balance_minor .separate_with_commas() ); } @@ -147,8 +147,8 @@ pub mod ordinary_diagnostic_functions { .original_qualified_account .payable .wallet, - non_finalized_account_info.proposed_adjusted_balance, - non_finalized_account_info.proposed_adjusted_balance + possible_extra_addition + non_finalized_account_info.proposed_adjusted_balance_minor, + non_finalized_account_info.proposed_adjusted_balance_minor + possible_extra_addition ); } @@ -166,7 +166,7 @@ pub mod ordinary_diagnostic_functions { .original_qualified_account .payable .balance_wei, - non_finalized_account_info.proposed_adjusted_balance + non_finalized_account_info.proposed_adjusted_balance_minor ); } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index db37ffb99..0d41fafc5 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -68,10 +68,10 @@ impl DisqualificationArbiter { todo!() } - fn list_accounts_nominated_for_disqualification( + fn list_accounts_nominated_for_disqualification<'unconfirmed_adj>( &self, - unconfirmed_adjustments: &[UnconfirmedAdjustment], - ) -> Vec<&UnconfirmedAdjustment> { + unconfirmed_adjustments: &'unconfirmed_adj [UnconfirmedAdjustment], + ) -> Vec<&'unconfirmed_adj UnconfirmedAdjustment> { unconfirmed_adjustments .iter() .flat_map(|adjustment_info| { @@ -82,7 +82,7 @@ impl DisqualificationArbiter { ); let proposed_adjusted_balance = adjustment_info .non_finalized_account - .proposed_adjusted_balance; + .proposed_adjusted_balance_minor; if proposed_adjusted_balance <= disqualification_edge { account_nominated_for_disqualification_diagnostics( @@ -187,7 +187,7 @@ mod tests { make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account, }; - use crate::accountant::QualifiedPayableAccount; + use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; use std::time::{Duration, SystemTime}; @@ -337,8 +337,9 @@ mod tests { fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { todo!("do I still want this test??"); let account_balance = 1_000_000; - let garbage_weight = 22222222; // It plays no role - let garbage_thresholds_intercept = 456789; // Also no role + let garbage_weight = 22222222; + let garbage_thresholds_intercept = 456789; + let garbage_creditor_thresholds = CreditorThresholds::new(123456); let prepare_account = |n: u64| { let mut account = make_payable_account(n); account.balance_wei = account_balance; @@ -351,7 +352,11 @@ mod tests { let proposed_ok_balance = edge + 1; let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_1, garbage_thresholds_intercept), + QualifiedPayableAccount::new( + payable_account_1, + garbage_thresholds_intercept, + garbage_creditor_thresholds, + ), garbage_weight, ), proposed_ok_balance, @@ -359,7 +364,11 @@ mod tests { let proposed_bad_balance_because_equal = edge; let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_2, garbage_thresholds_intercept), + QualifiedPayableAccount::new( + payable_account_2, + garbage_thresholds_intercept, + garbage_creditor_thresholds, + ), garbage_weight, ), proposed_bad_balance_because_equal, @@ -367,7 +376,11 @@ mod tests { let proposed_bad_balance_because_smaller = edge - 1; let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( WeightedAccount::new( - QualifiedPayableAccount::new(payable_account_3, garbage_thresholds_intercept), + QualifiedPayableAccount::new( + payable_account_3, + garbage_thresholds_intercept, + garbage_creditor_thresholds, + ), garbage_weight, ), proposed_bad_balance_because_smaller, @@ -389,11 +402,10 @@ mod tests { #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { let idx_of_expected_result = 1; - let (adjustments, expected_result) = - make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( - vec![1004, 1000, 1002, 1001], - idx_of_expected_result, - ); + let (adjustments, expected_result) = make_unconfirmed_adjustments_and_expected_test_result( + vec![1004, 1000, 1002, 1001], + idx_of_expected_result, + ); let referenced_unconfirmed_adjustments = by_reference(&adjustments); let result = DisqualificationArbiter::find_account_with_smallest_weight( @@ -406,11 +418,10 @@ mod tests { #[test] fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { let idx_of_expected_result = 0; - let (adjustments, expected_result) = - make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( - vec![1111, 1113, 1111], - idx_of_expected_result, - ); + let (adjustments, expected_result) = make_unconfirmed_adjustments_and_expected_test_result( + vec![1111, 1113, 1111], + idx_of_expected_result, + ); let referenced_non_finalized_accounts = by_reference(&adjustments); let result = DisqualificationArbiter::find_account_with_smallest_weight( @@ -477,7 +488,7 @@ mod tests { assert_eq!(result, Some(wallet_3)); } - fn make_unconfirmed_adjustments_and_select_expected_account_returned_in_the_test( + fn make_unconfirmed_adjustments_and_expected_test_result( weights: Vec, idx_of_expected_result: usize, ) -> ( @@ -488,14 +499,19 @@ mod tests { Vec, Option, ) = (vec![], None); + let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( init, |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { let original_account = make_payable_account(actual_idx as u64); let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; + let garbage_permanent_debt_allowed_wei = 1_111_111_111; let qualified_account = QualifiedPayableAccount { payable: original_account, - payment_threshold_intercept: garbage_intercept, + payment_threshold_intercept_minor: garbage_intercept, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, + }, }; let garbage_proposed_balance = 1_000_000_000; // Same here let new_adjustment_to_be_added = UnconfirmedAdjustment::new( diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index a23421110..51ee0152b 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -128,7 +128,9 @@ pub fn info_log_for_disqualified_account( round of payments. The proposed adjustment {} wei was less than half of the recorded \ debt, {} wei", account.original_qualified_account.payable.wallet, - account.proposed_adjusted_balance.separate_with_commas(), + account + .proposed_adjusted_balance_minor + .separate_with_commas(), account .original_qualified_account .payable diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 64bdcca0a..c5f2525ca 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -60,14 +60,17 @@ pub enum RequiredSpecialTreatment { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { pub original_qualified_account: QualifiedPayableAccount, - pub proposed_adjusted_balance: u128, + pub proposed_adjusted_balance_minor: u128, } impl AdjustedAccountBeforeFinalization { - pub fn new(original_account: QualifiedPayableAccount, proposed_adjusted_balance: u128) -> Self { + pub fn new( + original_account: QualifiedPayableAccount, + proposed_adjusted_balance_minor: u128, + ) -> Self { Self { original_qualified_account: original_account, - proposed_adjusted_balance, + proposed_adjusted_balance_minor, } } } @@ -79,11 +82,11 @@ pub struct UnconfirmedAdjustment { } impl UnconfirmedAdjustment { - pub fn new(weighted_account: WeightedAccount, proposed_adjusted_balance: u128) -> Self { + pub fn new(weighted_account: WeightedAccount, proposed_adjusted_balance_minor: u128) -> Self { Self { non_finalized_account: AdjustedAccountBeforeFinalization::new( weighted_account.qualified_account, - proposed_adjusted_balance, + proposed_adjusted_balance_minor, ), weight: weighted_account.weight, } @@ -155,15 +158,15 @@ mod tests { fn merging_results_from_recursion_works() { let non_finalized_account_1 = AdjustedAccountBeforeFinalization { original_qualified_account: make_non_guaranteed_qualified_payable(111), - proposed_adjusted_balance: 1234, + proposed_adjusted_balance_minor: 1234, }; let non_finalized_account_2 = AdjustedAccountBeforeFinalization { original_qualified_account: make_non_guaranteed_qualified_payable(222), - proposed_adjusted_balance: 5555, + proposed_adjusted_balance_minor: 5555, }; let non_finalized_account_3 = AdjustedAccountBeforeFinalization { original_qualified_account: make_non_guaranteed_qualified_payable(333), - proposed_adjusted_balance: 6789, + proposed_adjusted_balance_minor: 6789, }; let subject = RecursionResults { here_decided_accounts: vec![non_finalized_account_1.clone()], diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e0a1fd41f..83f149bc9 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -89,7 +89,7 @@ pub fn adjust_account_balance_if_outweighed( ) -> (Vec, Vec) { if current_adjustment_info .non_finalized_account - .proposed_adjusted_balance + .proposed_adjusted_balance_minor > current_adjustment_info .non_finalized_account .original_qualified_account @@ -100,7 +100,7 @@ pub fn adjust_account_balance_if_outweighed( current_adjustment_info .non_finalized_account - .proposed_adjusted_balance = current_adjustment_info + .proposed_adjusted_balance_minor = current_adjustment_info .non_finalized_account .original_qualified_account .payable @@ -119,7 +119,7 @@ pub fn exhaust_cw_till_the_last_drop( original_cw_service_fee_balance_minor: u128, ) -> Vec { let adjusted_balances_total: u128 = sum_as(&approved_accounts, |account_info| { - account_info.proposed_adjusted_balance + account_info.proposed_adjusted_balance_minor }); let cw_reminder = original_cw_service_fee_balance_minor @@ -136,8 +136,8 @@ pub fn exhaust_cw_till_the_last_drop( .into_iter() .sorted_by(|info_a, info_b| { Ord::cmp( - &info_a.proposed_adjusted_balance, - &info_b.proposed_adjusted_balance, + &info_a.proposed_adjusted_balance_minor, + &info_b.proposed_adjusted_balance_minor, ) }) .fold( @@ -159,12 +159,12 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( .original_qualified_account .payable .balance_wei - .checked_sub(non_finalized_account.proposed_adjusted_balance) + .checked_sub(non_finalized_account.proposed_adjusted_balance_minor) .unwrap_or_else(|| { panic!( "Proposed balance should never be bigger than the original one. Proposed: \ {}, original: {}", - non_finalized_account.proposed_adjusted_balance, + non_finalized_account.proposed_adjusted_balance_minor, non_finalized_account .original_qualified_account .payable @@ -206,7 +206,7 @@ impl ConsumingWalletExhaustingStatus { possible_extra_addition: u128, ) -> Self { let corrected_adjusted_account_before_finalization = { - non_finalized_account_info.proposed_adjusted_balance += possible_extra_addition; + non_finalized_account_info.proposed_adjusted_balance_minor += possible_extra_addition; non_finalized_account_info }; self.remainder = self @@ -281,7 +281,7 @@ impl From for PayableAccount { AdjustmentResolution::Finalize => PayableAccount { balance_wei: resolution_info .non_finalized_adjustment - .proposed_adjusted_balance, + .proposed_adjusted_balance_minor, ..resolution_info .non_finalized_adjustment .original_qualified_account @@ -319,7 +319,7 @@ mod tests { make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account, }; - use crate::accountant::QualifiedPayableAccount; + use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; @@ -563,17 +563,21 @@ mod tests { last_paid_timestamp: SystemTime::now(), pending_payable_opt: None, }; - let qualified_payable = QualifiedPayableAccount { - payable, - payment_threshold_intercept: 1234567, - }; - let unconfirmed_adjustment = UnconfirmedAdjustment { - non_finalized_account: AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable, - proposed_adjusted_balance: 9_000_000_000, - }, - weight: 123456, + let garbage_payment_threshold_intercept = 1234567; + let garbage_creditor_thresholds = CreditorThresholds { + permanent_debt_allowed_wei: 1000000, }; + let qualified_payable = QualifiedPayableAccount::new( + payable, + garbage_payment_threshold_intercept, + garbage_creditor_thresholds, + ); + let garbage_weight = 123456; + let garbage_proposed_adjusted_balance_minor = 9_000_000_000; + let unconfirmed_adjustment = UnconfirmedAdjustment::new( + WeightedAccount::new(qualified_payable, garbage_weight), + garbage_proposed_adjusted_balance_minor, + ); let init = (vec![], vec![]); let (outweighed, ok) = @@ -588,19 +592,22 @@ mod tests { original_balance: u128, proposed_adjusted_balance: u128, ) -> AdjustedAccountBeforeFinalization { + let garbage_last_paid_timestamp = SystemTime::now(); + let garbage_payment_threshold_intercept_minor = u128::MAX; + let garbage_permanent_debt_allowed_wei = 123456789; let qualified_payable = QualifiedPayableAccount { payable: PayableAccount { wallet: wallet.clone(), balance_wei: original_balance, - last_paid_timestamp: SystemTime::now(), + last_paid_timestamp: garbage_last_paid_timestamp, pending_payable_opt: None, }, - payment_threshold_intercept: u128::MAX, // Doesn't play any importance + payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, + }, }; - AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable, - proposed_adjusted_balance, - } + AdjustedAccountBeforeFinalization::new(qualified_payable, proposed_adjusted_balance) } fn assert_payable_accounts_after_adjustment_finalization( diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 5239a5693..e458c01c6 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -552,7 +552,7 @@ impl PaymentAdjusterReal { processed_outweighed: &[AdjustedAccountBeforeFinalization], ) { let subtrahend_total: u128 = sum_as(processed_outweighed, |account| { - account.proposed_adjusted_balance + account.proposed_adjusted_balance_minor }); self.inner .subtract_from_unallocated_cw_service_fee_balance_minor(subtrahend_total); @@ -695,7 +695,7 @@ mod tests { Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; - use crate::accountant::{gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; + use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; @@ -1037,7 +1037,7 @@ mod tests { subject.compute_unconfirmed_adjustments(criteria_and_accounts, weights_total); let proposed_adjusted_balance_2 = unconfirmed_adjustments[1] .non_finalized_account - .proposed_adjusted_balance; + .proposed_adjusted_balance_minor; // The criteria sum of the second account grew very progressively due to the effect of the greater age; // consequences would've been that redistributing the new balances according to the computed criteria would've // attributed the second account with a larger amount to pay than it would've had before the test started; @@ -1055,7 +1055,10 @@ mod tests { &first_returned_account.original_qualified_account, &qualified_payables[1] ); - assert_eq!(first_returned_account.proposed_adjusted_balance, balance_2); + assert_eq!( + first_returned_account.proposed_adjusted_balance_minor, + balance_2 + ); let second_returned_account = result.remove(0); assert_eq!( &second_returned_account.original_qualified_account, @@ -1064,12 +1067,12 @@ mod tests { let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; let lower_limit = (upper_limit * 9) / 10; assert!( - lower_limit <= second_returned_account.proposed_adjusted_balance - && second_returned_account.proposed_adjusted_balance <= upper_limit, + lower_limit <= second_returned_account.proposed_adjusted_balance_minor + && second_returned_account.proposed_adjusted_balance_minor <= upper_limit, "we expected the roughly adjusted account to be between {} and {} but was {}", lower_limit, upper_limit, - second_returned_account.proposed_adjusted_balance + second_returned_account.proposed_adjusted_balance_minor ); assert!(result.is_empty()); } @@ -1289,10 +1292,8 @@ mod tests { last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; - let qualified_payable = QualifiedPayableAccount { - payable, - payment_threshold_intercept: 111222333, - }; + let qualified_payable = + QualifiedPayableAccount::new(payable, 111222333, CreditorThresholds::new(1000000)); let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_params(&determine_limit_params_arc) .determine_limit_result(4_500_600); @@ -1304,7 +1305,7 @@ mod tests { result, Some(AdjustedAccountBeforeFinalization { original_qualified_account: qualified_payable.clone(), - proposed_adjusted_balance: cw_balance, + proposed_adjusted_balance_minor: cw_balance, }) ); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); @@ -1312,8 +1313,10 @@ mod tests { *determine_limit_params, vec![( qualified_payable.payable.balance_wei, - qualified_payable.payment_threshold_intercept, - todo!() + qualified_payable.payment_threshold_intercept_minor, + qualified_payable + .creditor_thresholds + .permanent_debt_allowed_wei )] ) } @@ -1323,15 +1326,22 @@ mod tests { disqualification_gauge: DisqualificationGaugeMock, ) { let now = SystemTime::now(); + let garbage_balance_wei = 123456789; + let garbage_last_paid_timestamp = SystemTime::now(); + let garbage_payment_threshold_intercept_minor = 33333333; + let garbage_permanent_debt_allowed_wei = 11111111; let payable = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 3 * cw_balance, // Unimportant. Mock in use, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), + balance_wei: garbage_balance_wei, + last_paid_timestamp: garbage_last_paid_timestamp, pending_payable_opt: None, }; let qualified_payable = QualifiedPayableAccount { payable, - payment_threshold_intercept: cw_balance / 2, // Unimportant. Mock in use + payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, + }, }; let payment_adjuster = prepare_subject(cw_balance, now, disqualification_gauge); @@ -1439,35 +1449,38 @@ mod tests { let test_name = "qualified_accounts_count_before_equals_the_payments_count_after"; let now = SystemTime::now(); let balance_1 = 4_444_444_444_444_444_444; - let qualified_account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let qualified_account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let balance_2 = 6_000_000_000_000_000_000; - let qualified_account_2 = QualifiedPayableAccount { - payable: PayableAccount { + let qualified_account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let balance_3 = 6_666_666_666_000_000_000; - let qualified_account_3 = QualifiedPayableAccount { - payable: PayableAccount { + let qualified_account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![ qualified_account_1.clone(), qualified_account_2.clone(), @@ -1546,33 +1559,36 @@ mod tests { let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); - let account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("abc"), balance_wei: 111_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_2 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("def"), balance_wei: 333_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_3 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("ghi"), balance_wei: 222_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); @@ -1629,33 +1645,36 @@ mod tests { // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("abc"), balance_wei: 111_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_2 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("def"), balance_wei: 333_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_3 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("ghk"), balance_wei: 222_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); let service_fee_balance_in_minor_units = 111_000_000_000_000_u128 + 333_000_000_000_000; @@ -1701,38 +1720,41 @@ mod tests { let now = SystemTime::now(); let wallet_1 = make_wallet("def"); // Account to be adjusted to keep as much as how much is left in the cw balance - let account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: wallet_1.clone(), balance_wei: 333_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); // Account to be outweighed and fully preserved let wallet_2 = make_wallet("abc"); - let account_2 = QualifiedPayableAccount { - payable: PayableAccount { + let account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: wallet_2.clone(), balance_wei: 111_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); // Account to be disqualified let wallet_3 = make_wallet("ghk"); let balance_3 = 600_000_000_000; - let account_3 = QualifiedPayableAccount { - payable: PayableAccount { + let account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: wallet_3.clone(), balance_wei: balance_3, last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); @@ -1942,37 +1964,40 @@ mod tests { // Thrown away as the second one due to shortage of service fee, // for the proposed adjusted balance insignificance (the third account withdraws // most of the available balance from the consuming wallet for itself) - let account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("abc"), balance_wei: 10_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); // Thrown away as the first one due to shortage of transaction fee, // as it is the least significant by criteria at the moment - let account_2 = QualifiedPayableAccount { - payable: PayableAccount { + let account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("def"), balance_wei: 55_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let wallet_3 = make_wallet("ghi"); let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let account_3 = QualifiedPayableAccount { - payable: PayableAccount { + let account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: wallet_3.clone(), balance_wei: 333_000_000_000_000, last_paid_timestamp: last_paid_timestamp_3, pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![account_1, account_2, account_3.clone()]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); @@ -2031,33 +2056,36 @@ mod tests { let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); // This account is eliminated in the transaction fee cut - let account_1 = QualifiedPayableAccount { - payable: PayableAccount { + let account_1 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("abc"), balance_wei: 111_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_2 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_2 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("def"), balance_wei: 333_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; - let account_3 = QualifiedPayableAccount { - payable: PayableAccount { + todo!(), + todo!(), + ); + let account_3 = QualifiedPayableAccount::new( + PayableAccount { wallet: make_wallet("ghi"), balance_wei: 222_000_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept: todo!(), - }; + todo!(), + todo!(), + ); let qualified_payables = vec![account_1, account_2, account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 19baf5475..0ee83cd87 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -244,13 +244,13 @@ mod tests { vec![ ( account_1.payable.balance_wei, - account_1.payment_threshold_intercept, - todo!() + account_1.payment_threshold_intercept_minor, + account_1.creditor_thresholds.permanent_debt_allowed_wei ), ( account_2.payable.balance_wei, - account_2.payment_threshold_intercept, - todo!() + account_2.payment_threshold_intercept_minor, + account_2.creditor_thresholds.permanent_debt_allowed_wei ) ] ) diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 0925aa5c4..94283c3eb 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -17,7 +17,7 @@ use crate::accountant::scanners::scanners_utils::pending_payable_scanner_utils:: PendingPayableScanReport, }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; -use crate::accountant::{PendingPayableId, QualifiedPayableAccount}; +use crate::accountant::{CreditorThresholds, gwei_to_wei, PendingPayableId, QualifiedPayableAccount}; use crate::accountant::{ comma_joined_stringifiable, Accountant, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, @@ -353,9 +353,15 @@ impl PayableScanner { .into_iter() .flat_map(|account| { self.payable_exceeded_threshold(&account, SystemTime::now()) - .map(|payment_threshold_intercept| QualifiedPayableAccount { - payable: account, - payment_threshold_intercept, + .map(|payment_threshold_intercept| { + let creditor_thresholds = CreditorThresholds::new(gwei_to_wei( + self.common.payment_thresholds.permanent_debt_allowed_gwei, + )); + QualifiedPayableAccount::new( + account, + payment_threshold_intercept, + creditor_thresholds, + ) }) }) .collect(); @@ -1098,8 +1104,8 @@ mod tests { ReceivableScannerBuilder, }; use crate::accountant::{ - gwei_to_wei, PendingPayableId, QualifiedPayableAccount, ReceivedPayments, - ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, + gwei_to_wei, CreditorThresholds, PendingPayableId, QualifiedPayableAccount, + ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; @@ -1221,14 +1227,16 @@ mod tests { fn protected_payables_can_be_cast_from_and_back_to_vec_of_payable_accounts_by_payable_scanner() { let initial_unprotected = vec![ - QualifiedPayableAccount { - payable: make_payable_account(123), - payment_threshold_intercept: 123456789, - }, - QualifiedPayableAccount { - payable: make_payable_account(456), - payment_threshold_intercept: 987654321, - }, + QualifiedPayableAccount::new( + make_payable_account(123), + 123456789, + CreditorThresholds::new(11111111), + ), + QualifiedPayableAccount::new( + make_payable_account(456), + 987654321, + CreditorThresholds::new(22222222), + ), ]; let subject = PayableScannerBuilder::new().build(); @@ -2106,10 +2114,11 @@ mod tests { assert_eq!(result.len(), 1); let expected_intercept = PayableThresholdsGaugeReal::default() .calculate_payout_threshold_in_gwei(&payment_thresholds, debt_age as u64); - let expected_qualified_payable = QualifiedPayableAccount { + let expected_qualified_payable = QualifiedPayableAccount::new( payable, - payment_threshold_intercept: expected_intercept, - }; + expected_intercept, + CreditorThresholds::new(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei)), + ); assert_eq!(&result[0], &expected_qualified_payable); TestLogHandler::new().exists_log_matching(&format!( "DEBUG: {}: Paying qualified debts:\n999,999,999,000,000,\ @@ -2186,8 +2195,8 @@ mod tests { .unwrap(); assert_eq!(result.len(), 1); assert_eq!(&result[0].payable.wallet, &wallet); - assert!(intercept_before >= result[0].payment_threshold_intercept && result[0].payment_threshold_intercept >= intercept_after, - "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept, intercept_before, intercept_after + assert!(intercept_before >= result[0].payment_threshold_intercept_minor && result[0].payment_threshold_intercept_minor >= intercept_after, + "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept_minor, intercept_before, intercept_after ) } diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index ca3ae463c..76e506f16 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -186,7 +186,7 @@ pub mod payable_scanner_utils { account.balance_wei.separate_with_commas(), p_age.as_secs(), qualified_account - .payment_threshold_intercept + .payment_threshold_intercept_minor .separate_with_commas(), account.wallet ) @@ -489,7 +489,7 @@ mod tests { PayableThresholdsGaugeReal, }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; - use crate::accountant::{checked_conversion, gwei_to_wei, QualifiedPayableAccount, SentPayables}; + use crate::accountant::{checked_conversion, CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, SentPayables}; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; @@ -649,51 +649,40 @@ mod tests { fn payables_debug_summary_prints_pretty_summary() { init_test_logging(); let now = to_time_t(SystemTime::now()); - let payment_thresholds = PaymentThresholds { - threshold_interval_sec: 2_592_000, - debt_threshold_gwei: 1_000_000_000, - payment_grace_period_sec: 86_400, - maturity_threshold_sec: 86_400, - permanent_debt_allowed_gwei: 10_000_000, - unban_below_gwei: 10_000_000, - }; let qualified_payables_and_threshold_points = vec![ QualifiedPayableAccount { payable: PayableAccount { wallet: make_wallet("wallet0"), - balance_wei: gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei + 2000), - last_paid_timestamp: from_time_t( - now - checked_conversion::( - payment_thresholds.maturity_threshold_sec - + payment_thresholds.threshold_interval_sec, - ), - ), + balance_wei: gwei_to_wei(10_002_000_u64), + last_paid_timestamp: from_time_t(now - 2678400), pending_payable_opt: None, }, - payment_threshold_intercept: 10_000_000_001_152_000_u128, + payment_threshold_intercept_minor: 10_000_000_001_152_000_u128, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: 333_333, + }, }, QualifiedPayableAccount { payable: PayableAccount { wallet: make_wallet("wallet1"), - balance_wei: gwei_to_wei(payment_thresholds.debt_threshold_gwei - 1), - last_paid_timestamp: from_time_t( - now - checked_conversion::( - payment_thresholds.maturity_threshold_sec + 55, - ), - ), + balance_wei: gwei_to_wei(999_999_999_u64), + last_paid_timestamp: from_time_t(now - 86455), pending_payable_opt: None, }, - payment_threshold_intercept: 999_978_993_055_555_580, + payment_threshold_intercept_minor: 999_978_993_055_555_580, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: 10_000_000, + }, }, ]; let logger = Logger::new("test"); payables_debug_summary(&qualified_payables_and_threshold_points, &logger); - TestLogHandler::new().exists_log_containing("Paying qualified debts:\n\ - 10,002,000,000,000,000 wei owed for 2678400 sec exceeds threshold: \ + TestLogHandler::new().exists_log_matching("Paying qualified debts:\n\ + 10,002,000,000,000,000 wei owed for 267840\\d sec exceeds threshold: \ 10,000,000,001,152,000 wei; creditor: 0x0000000000000000000000000077616c6c657430\n\ - 999,999,999,000,000,000 wei owed for 86455 sec exceeds threshold: \ + 999,999,999,000,000,000 wei owed for 8645\\d sec exceeds threshold: \ 999,978,993,055,555,580 wei; creditor: 0x0000000000000000000000000077616c6c657431"); } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index b2203a0f9..6fed49421 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -29,8 +29,8 @@ use crate::accountant::scanners::{ ReceivableScanner, ScanSchedulers, Scanner, }; use crate::accountant::{ - gwei_to_wei, Accountant, QualifiedPayableAccount, ResponseSkeleton, SentPayables, - DEFAULT_PENDING_TOO_LONG_SEC, + gwei_to_wei, Accountant, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, + SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; @@ -1689,10 +1689,11 @@ impl ScanSchedulers { pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount { // Without guarantee that the generated payable would cross the given thresholds - QualifiedPayableAccount { - payable: make_payable_account(n), - payment_threshold_intercept: n as u128 * 12345, - } + QualifiedPayableAccount::new( + make_payable_account(n), + n as u128 * 12345, + CreditorThresholds::new(111_111_111), + ) } pub fn make_guaranteed_qualified_payables( @@ -1704,6 +1705,6 @@ pub fn make_guaranteed_qualified_payables( payables.into_iter().map(|payable|{ let payment_threshold_intercept = payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) .unwrap_or_else(||panic!("You intend to create qualified payables but their paramters not always make them qualify as in: {:?}", payable)); - QualifiedPayableAccount{ payable, payment_threshold_intercept } + QualifiedPayableAccount::new(payable, payment_threshold_intercept, CreditorThresholds::new(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei))) }).collect() } From abea7f07604aa3388dcbe3701ae15a86d216e769 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 24 Mar 2024 21:30:50 +0100 Subject: [PATCH 144/250] GH-711-b: drove out miltiple todos in the calculator and disqualification arbiter --- node/src/accountant/mod.rs | 14 +- .../payment_adjuster/adjustment_runners.rs | 16 +- .../balance_and_age_calculator.rs | 78 ----- .../criteria_calculators/mod.rs | 226 --------------- .../balance_and_age_calculator.rs | 57 ++++ .../criterion_calculators/mod.rs | 13 + .../payment_adjuster/diagnostics.rs | 2 +- .../disqualification_arbiter.rs | 274 ++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 10 +- .../accountant/payment_adjuster/test_utils.rs | 9 +- 10 files changed, 213 insertions(+), 486 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs delete mode 100644 node/src/accountant/payment_adjuster/criteria_calculators/mod.rs create mode 100644 node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs create mode 100644 node/src/accountant/payment_adjuster/criterion_calculators/mod.rs diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 2bcbfa262..1ebc98319 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -138,11 +138,15 @@ pub struct QualifiedPayableAccount { impl QualifiedPayableAccount { pub fn new( - payable_account: PayableAccount, - payment_threshold_intercept: u128, + payable: PayableAccount, + payment_threshold_intercept_minor: u128, creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { - todo!() + Self{ + payable, + payment_threshold_intercept_minor, + creditor_thresholds, + } } } @@ -153,7 +157,9 @@ pub struct CreditorThresholds { impl CreditorThresholds { pub fn new(permanent_debt_allowed_wei: u128) -> Self { - todo!() + Self{ + permanent_debt_allowed_wei, + } } } diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 92b0958b0..899e44a85 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -117,7 +117,7 @@ mod tests { }; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; - use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; + use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, @@ -138,18 +138,8 @@ mod tests { { let now = SystemTime::now(); let wallet = make_wallet("abc"); - let account = PayableAccount { - wallet, - balance_wei: 9_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payable = make_guaranteed_qualified_payables( - vec![account], - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - ) - .remove(0); + let mut qualified_payable = make_non_guaranteed_qualified_payable(111); + qualified_payable.payable.balance_wei = 9_000_000_000; let cw_balance = 8_645_123_505; let adjustment = Adjustment::ByServiceFee; let mut payment_adjuster = PaymentAdjusterReal::new(); diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs deleted file mode 100644 index e8a57d557..000000000 --- a/node/src/accountant/payment_adjuster/criteria_calculators/balance_and_age_calculator.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; -use crate::accountant::QualifiedPayableAccount; - -// This parameter affects the steepness inversely, but just slowly. -// -// Don't worry to joggle with this number; it's not as scientific as it looks like. True, I arrived at it after many -// attempts when I finally aligned myself with the tuning. The issue is it needs to be carefully compared to the values -// the Age criterion calculator yields. -// (If you are preparing to follow my steps you'll need to enable the rendering from the 'diagnostics' module, getting -// back a chance for a close look into each characteristic of the calculators formulas and therefore also how sync -// they are. See and think about the effects of your new settings) -const BALANCE_LOG_2_ARG_DIVISOR: u128 = 18_490_000; -// This parameter affects the steepness analogously, but energetically -const BALANCE_FINAL_MULTIPLIER: u128 = 2; - -#[derive(Default)] -pub struct BalanceAndAgeCriterionCalculator {} - -impl CriterionCalculator for BalanceAndAgeCriterionCalculator { - fn calculate(&self, account: &QualifiedPayableAccount) -> u128 { - todo!() - } - - fn parameter_name(&self) -> &'static str { - todo!() - } -} - -impl BalanceAndAgeCriterionCalculator { - // fn nonzero_log2(input: u128) -> u32 { - // let log = log_2(input); - // if log > 0 { - // log - // } else { - // 1 - // } - // } - // - // fn calculate_binary_argument(balance_minor: u128) -> u128 { - // balance_minor / BALANCE_LOG_2_ARG_DIVISOR - // } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::{ - BalanceAndAgeCriterionCalculator, BALANCE_FINAL_MULTIPLIER, BALANCE_LOG_2_ARG_DIVISOR, - }; - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; - use crate::accountant::payment_adjuster::test_utils::assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes; - - #[test] - fn constants_are_correct() { - let constants_and_their_expected_values: Vec<(i128, i128)> = vec![ - (BALANCE_LOG_2_ARG_DIVISOR.try_into().unwrap(), 18_490_000), - (BALANCE_FINAL_MULTIPLIER.try_into().unwrap(), 2), - ]; - - assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( - &constants_and_their_expected_values, - 18490002, - ) - } - - #[test] - fn balance_criteria_calculation_works() { - let subject = BalanceAndAgeCriterionCalculator::default(); - let balance_wei = 111_333_555_777_u128; - let account = todo!(); - - let result = subject.calculate(account); - - let expected_result = todo!(); - assert_eq!(result, expected_result) - } -} diff --git a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs b/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs deleted file mode 100644 index 49830f66c..000000000 --- a/node/src/accountant/payment_adjuster/criteria_calculators/mod.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -pub mod balance_and_age_calculator; - -use crate::accountant::QualifiedPayableAccount; - -// Caution: always remember to use checked math operations in the criteria formulas! -pub trait CriterionCalculator { - fn calculate(&self, account: &QualifiedPayableAccount) -> u128; - - fn parameter_name(&self) -> &'static str; -} -// -// #[derive(PartialEq, Debug, VariantCount)] -// pub enum CalculatorInputHolder { -// DebtBalance(u128), -// DebtAge { last_paid_timestamp: SystemTime }, -// } -// -// impl CalculatorInputHolder { -// fn age_input(self) -> SystemTime { -// if let CalculatorInputHolder::DebtAge { -// last_paid_timestamp, -// } = self -// { -// last_paid_timestamp -// } else { -// self.mismatched_call_panic("age") -// } -// } -// -// fn balance_input(self) -> u128 { -// if let CalculatorInputHolder::DebtBalance(balance_wei) = self { -// balance_wei -// } else { -// self.mismatched_call_panic("balance") -// } -// } -// -// fn mismatched_call_panic(&self, param_name: &str) -> ! { -// panic!( -// "Call for {} while the underlying enum variant is {:?}", -// param_name, self -// ) -// } -// } -// -// impl<'account> From<(CalculatorType, &'account QualifiedPayableAccount)> for CalculatorInputHolder { -// fn from( -// (calculator_type, qualified_payable): (CalculatorType, &'account QualifiedPayableAccount), -// ) -> Self { -// match calculator_type { -// CalculatorType::DebtBalance => { -// CalculatorInputHolder::DebtBalance(qualified_payable.payable.balance_wei) -// } -// CalculatorType::DebtAge => CalculatorInputHolder::DebtAge { -// last_paid_timestamp: qualified_payable.payable.last_paid_timestamp, -// }, -// } -// } -// } -// -// impl Display for CalculatorType { -// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { -// match self { -// CalculatorType::DebtBalance => write!(f, "BALANCE"), -// CalculatorType::DebtAge => write!(f, "AGE"), -// } -// } -// } -// -// #[cfg(test)] -// mod tests { -// use crate::accountant::db_access_objects::payable_dao::PayableAccount; -// use crate::accountant::payment_adjuster::criteria_calculators::{ -// CalculatorInputHolder, CalculatorType, -// }; -// use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; -// use crate::accountant::QualifiedPayableAccount; -// use crate::test_utils::make_wallet; -// use std::panic::{catch_unwind, RefUnwindSafe}; -// use std::time::{Duration, SystemTime}; -// -// #[test] -// fn input_holders_can_be_derived_from_calculator_type_and_payable_account() { -// let balance_wei = 135_792_468; -// let last_paid_timestamp = SystemTime::now() -// .checked_sub(Duration::from_secs(3)) -// .unwrap(); -// let payable = PayableAccount { -// wallet: make_wallet("abc"), -// balance_wei, -// last_paid_timestamp, -// pending_payable_opt: None, -// }; -// let payment_threshold_intercept = 65432; -// let qualified_account = QualifiedPayableAccount { -// payable, -// payment_threshold_intercept, -// }; -// let result = [CalculatorType::DebtAge, CalculatorType::DebtBalance] -// .into_iter() -// .map(|calculator_type| { -// CalculatorInputHolder::from((calculator_type, &qualified_account)) -// }) -// .collect::>(); -// -// let expected = vec![ -// CalculatorInputHolder::DebtAge { -// last_paid_timestamp, -// }, -// CalculatorInputHolder::DebtBalance(balance_wei - payment_threshold_intercept), -// ]; -// assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); -// assert_eq!(result, expected); -// } -// -// #[test] -// fn calculator_type_implements_display() { -// assert_eq!(CalculatorType::DebtBalance.to_string(), "BALANCE"); -// assert_eq!(CalculatorType::DebtAge.to_string(), "AGE") -// } -// -// #[test] -// fn input_values_can_be_fetched_from_input_holder() { -// let last_paid_timestamp = SystemTime::now() -// .checked_sub(Duration::from_millis(1234)) -// .unwrap(); -// let age_input_holder = CalculatorInputHolder::DebtAge { -// last_paid_timestamp, -// }; -// let balance = 333_444_555_666; -// let balance_input_holder = CalculatorInputHolder::DebtBalance(balance); -// -// let result = vec![age_input_holder, balance_input_holder]; -// -// assert_eq!(result.len(), CalculatorInputHolder::VARIANT_COUNT); -// let mut result = result.into_iter(); -// assert_eq!(result.next().unwrap().age_input(), last_paid_timestamp); -// assert_eq!(result.next().unwrap().balance_input(), balance); -// assert_eq!(result.next(), None) -// } -// -// #[test] -// fn there_is_same_amount_of_calculator_types_as_calculator_input_holders() { -// assert_eq!( -// CalculatorType::VARIANT_COUNT, -// CalculatorInputHolder::VARIANT_COUNT -// ) -// } -// -// #[test] -// fn panics_for_age_input_when_the_enum_is_not_age_input_holder() { -// test_panics_for_mismatched_input_holder( -// CalculatorType::DebtAge, -// |calculator_type, account| { -// CalculatorInputHolder::from((calculator_type, account)).age_input(); -// // should cause panic -// }, -// ) -// } -// -// #[test] -// fn panics_for_balance_input_when_the_enum_is_not_balance_input_holder() { -// test_panics_for_mismatched_input_holder( -// CalculatorType::DebtBalance, -// |calculator_type, account| { -// CalculatorInputHolder::from((calculator_type, account)).balance_input(); -// // should cause panic -// }, -// ) -// } -// -// fn test_panics_for_mismatched_input_holder( -// the_only_correct_type: CalculatorType, -// tested_function_call_for_panics: F, -// ) where -// F: Fn(CalculatorType, &QualifiedPayableAccount) + RefUnwindSafe, -// { -// let mut qualified_payable = make_non_guaranteed_qualified_payable(12345); -// qualified_payable.payable.balance_wei = 2_000_000; -// qualified_payable.payment_threshold_intercept = 778_899; -// let difference = -// qualified_payable.payable.balance_wei - qualified_payable.payment_threshold_intercept; -// let all_types = vec![ -// ( -// CalculatorType::DebtBalance, -// "balance", -// format!("DebtBalance({difference})"), -// ), -// ( -// CalculatorType::DebtAge, -// "age", -// "DebtAge { last_paid_timestamp: SystemTime { tv_sec: 0, tv_nsec: 0 } }".to_string(), -// ), -// ]; -// -// assert_eq!(all_types.len(), CalculatorType::VARIANT_COUNT); -// let (_, the_right_param_literal_name, _) = all_types -// .iter() -// .find(|(calculator_type, _, _)| calculator_type == &the_only_correct_type) -// .unwrap(); -// // To be able to shut the reference before we consume the vec -// let the_right_param_literal_name = the_right_param_literal_name.to_string(); -// all_types -// .into_iter() -// .filter(|(calculator_type, _, _)| calculator_type != &the_only_correct_type) -// .for_each( -// |(calculator_type, _, debug_rendering_of_the_corresponding_input_holder)| { -// let result = catch_unwind(|| { -// tested_function_call_for_panics(calculator_type, &qualified_payable) -// }) -// .unwrap_err(); -// let panic_msg = result.downcast_ref::().unwrap(); -// assert_eq!( -// panic_msg, -// &format!( -// "Call for {} while the underlying enum variant is {}", -// the_right_param_literal_name, -// debug_rendering_of_the_corresponding_input_holder -// ) -// ); -// }, -// ) -// } -// } diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs new file mode 100644 index 000000000..68882d4bb --- /dev/null +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; +use crate::accountant::QualifiedPayableAccount; + +#[derive(Default)] +pub struct BalanceAndAgeCriterionCalculator {} + +impl CriterionCalculator for BalanceAndAgeCriterionCalculator { + fn calculate(&self, account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner) -> u128 { + let now = context.now(); + let debt_age_s = now.duration_since(account.payable.last_paid_timestamp).expect("time traveller").as_secs(); + account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128 + } + + fn parameter_name(&self) -> &'static str { + "BALANCE AND AGE" + } +} + +#[cfg(test)] +mod tests { + use std::time::SystemTime; + use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::{ + BalanceAndAgeCriterionCalculator, + }; + use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + + #[test] + fn calculator_knows_its_name(){ + let subject = BalanceAndAgeCriterionCalculator::default(); + + let result = subject.parameter_name(); + + assert_eq!(result, "BALANCE AND AGE") + } + + #[test] + fn balance_and_age_criterion_calculator_works() { + let now = SystemTime::now(); + let qualified_accounts = [0,10,22].into_iter().map(|n| make_non_guaranteed_qualified_payable(n)).collect::>(); + let payment_adjuster_inner = PaymentAdjusterInnerReal::new(now, None, 123456789); + let subject = BalanceAndAgeCriterionCalculator::default(); + + let computed_criteria = qualified_accounts.iter().map(|qualified_account|subject.calculate(qualified_account, &payment_adjuster_inner)).collect::>(); + + let zipped = qualified_accounts.into_iter().zip(computed_criteria.into_iter()); + zipped.into_iter().for_each(|(account, actual_criterion)|{ + let debt_age_s = now.duration_since(account.payable.last_paid_timestamp).unwrap().as_secs(); + let expected_criterion = account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128; + assert_eq!(actual_criterion,expected_criterion) + }) + } +} diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs new file mode 100644 index 000000000..8e93e06bd --- /dev/null +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod balance_and_age_calculator; + +use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; +use crate::accountant::QualifiedPayableAccount; + +// Caution: always remember to use checked math operations in the criteria formulas! +pub trait CriterionCalculator { + fn calculate(&self, account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner) -> u128; + + fn parameter_name(&self) -> &'static str; +} diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 379a11a17..2f00ae4d3 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -91,7 +91,7 @@ pub fn collection_diagnostics( } pub mod ordinary_diagnostic_functions { - use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 0d41fafc5..b47c6ceda 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -18,8 +18,8 @@ pub struct DisqualificationArbiter { } impl DisqualificationArbiter { - pub fn new(dsq_edge_detector: Box) -> Self { - todo!() + pub fn new(disqualification_gauge: Box) -> Self { + Self{ disqualification_gauge } } pub fn try_finding_an_account_to_disqualify_in_this_iteration( &self, @@ -63,9 +63,9 @@ impl DisqualificationArbiter { pub fn calculate_disqualification_edge( &self, - qualified_payable_account: &QualifiedPayableAccount, + qualified_payable: &QualifiedPayableAccount, ) -> u128 { - todo!() + self.disqualification_gauge.determine_limit(qualified_payable.payable.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable.creditor_thresholds.permanent_debt_allowed_wei) } fn list_accounts_nominated_for_disqualification<'unconfirmed_adj>( @@ -135,35 +135,45 @@ pub struct DisqualificationGaugeReal {} impl DisqualificationGauge for DisqualificationGaugeReal { fn determine_limit( &self, - account_balance_wei: u128, - threshold_intercept_wei: u128, - permanent_debt_allowed_wei: u128, + account_balance_minor: u128, + threshold_intercept_minor: u128, + permanent_debt_allowed_minor: u128, ) -> u128 { - todo!() + if threshold_intercept_minor == permanent_debt_allowed_minor { + return account_balance_minor + } + let exceeding_debt_part = account_balance_minor - threshold_intercept_minor; + if DisqualificationGaugeReal::qualifies_for_double_margin(account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor){ + exceeding_debt_part + 2* permanent_debt_allowed_minor + } else { + exceeding_debt_part + permanent_debt_allowed_minor + } } } + + impl DisqualificationGaugeReal { + + const FIRST_CONDITION_COEFFICIENT: u128 = 2; + const SECOND_CONDITION_COEFFICIENT: u128 = 2; fn qualifies_for_double_margin( - account_balance_wei: u128, - threshold_intercept_wei: u128, - permanent_debt_allowed_wei: u128, + account_balance_minor: u128, + threshold_intercept_minor: u128, + permanent_debt_allowed_minor: u128, ) -> bool { - let exceeding_part = account_balance_wei - threshold_intercept_wei; - let considered_forgiven = threshold_intercept_wei - permanent_debt_allowed_wei; - let extra_condition: bool = if considered_forgiven == 2 * permanent_debt_allowed_wei { - todo!() - } else if considered_forgiven < 2 * permanent_debt_allowed_wei { - todo!() - } else { - todo!() - }; - if exceeding_part > 2 * considered_forgiven && extra_condition { - todo!() - } else if exceeding_part == 2 * considered_forgiven && extra_condition { - todo!() + let exceeding_threshold = account_balance_minor - threshold_intercept_minor; + let considered_forgiven = threshold_intercept_minor - permanent_debt_allowed_minor; + let minimal_payment_accepted = exceeding_threshold + permanent_debt_allowed_minor; + + let first_condition = minimal_payment_accepted >= Self::FIRST_CONDITION_COEFFICIENT * considered_forgiven; + + let second_condition = considered_forgiven >= Self::SECOND_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; + + if first_condition && second_condition { + true } else { - todo!() + false } } } @@ -178,7 +188,7 @@ mod tests { AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - weights_total, EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, + weights_total, }; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, DisqualificationGaugeMock, PRESERVED_TEST_PAYMENT_THRESHOLDS, @@ -191,17 +201,24 @@ mod tests { use crate::test_utils::make_wallet; use masq_lib::logger::Logger; use std::time::{Duration, SystemTime}; + use crate::sub_lib::accountant::PaymentThresholds; + + #[test] + fn constants_are_correct(){ + assert_eq!(DisqualificationGaugeReal::FIRST_CONDITION_COEFFICIENT, 2); + assert_eq!(DisqualificationGaugeReal::SECOND_CONDITION_COEFFICIENT, 2) + } #[test] fn qualifies_for_double_margin_granted_on_both_conditions_returning_equals() { - let account_balance_wei = 6_000_000_000; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 3_000_000_000; + let account_balance_minor = 6_000_000_000; + let threshold_intercept_minor = 3_000_000_000; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor ); assert_eq!(result, true) @@ -209,14 +226,14 @@ mod tests { #[test] fn qualifies_for_double_margin_granted_on_first_condition_bigger_second_equal() { - let account_balance_wei = 6_000_000_001; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 3_000_000_000; + let account_balance_minor = 6_000_000_001; + let threshold_intercept_minor = 3_000_000_000; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, true) @@ -224,56 +241,59 @@ mod tests { #[test] fn qualifies_for_double_margin_granted_on_first_condition_equal_second_bigger() { - let account_balance_wei = 6_000_000_002; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 3_000_000_001; + let account_balance_minor = 6_000_000_003; + let threshold_intercept_minor = 3_000_000_001; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, true) } + #[test] fn qualifies_for_double_margin_granted_on_both_conditions_returning_bigger() { - let account_balance_wei = 6_000_000_003; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 3_000_000_001; + let account_balance_minor = 6_000_000_004; + let threshold_intercept_minor = 3_000_000_001; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, true) } + #[test] fn qualifies_for_double_margin_declined_on_first_condition() { - let account_balance_wei = 5_999_999_999; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 3_000_000_000; + let account_balance_minor = 5_999_999_999; + let threshold_intercept_minor = 3_000_000_000; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, false) } + #[test] fn qualifies_for_double_margin_declined_on_second_condition() { - let account_balance_wei = 6_000_000_000; - let permanent_debt_allowed_wei = 1_000_000_000; - let threshold_intercept_wei = 2_999_999_999; + let account_balance_minor = 6_000_000_000; + let threshold_intercept_minor = 2_999_999_999; + let permanent_debt_allowed_minor = 1_000_000_000; let result = DisqualificationGaugeReal::qualifies_for_double_margin( - account_balance_wei, - permanent_debt_allowed_wei, - threshold_intercept_wei, + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, false) @@ -281,16 +301,15 @@ mod tests { #[test] fn calculate_disqualification_edge_in_the_horizontal_thresholds_area() { - let balance_wei = 30_000_000_000; - let threshold_intercept_wei = 4_000_000_000; - let permanent_debt_allowed_wei = 4_000_000_000; + let balance_minor = 30_000_000_000; + let threshold_intercept_minor = 4_000_000_000; + let permanent_debt_allowed_minor = 4_000_000_000; let subject = DisqualificationGaugeReal::default(); - todo!("finish the setup when pda can be added"); let result = subject.determine_limit( - balance_wei, - threshold_intercept_wei, - permanent_debt_allowed_wei, + balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!(result, 30_000_000_000) @@ -298,33 +317,31 @@ mod tests { #[test] fn calculate_disqualification_edge_in_the_tilted_thresholds_area_with_normal_margin() { - let balance_wei = 6_000_000_000; - let threshold_intercept_wei = 4_000_000_000; - let permanent_debt_allowed_wei = 1_000_000_000; + let balance_minor = 6_000_000_000; + let threshold_intercept_minor = 4_000_000_000; + let permanent_debt_allowed_minor = 1_000_000_000; let subject = DisqualificationGaugeReal::default(); - todo!("finish the setup when pda can be added"); let result = subject.determine_limit( - balance_wei, - threshold_intercept_wei, - permanent_debt_allowed_wei, + balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); - assert_eq!(result, (30_000_000_000 - 4_000_000_000) + 1_000_000_000) + assert_eq!(result, (6_000_000_000 - 4_000_000_000) + 1_000_000_000) } #[test] fn calculate_disqualification_edge_in_the_tilted_thresholds_area_with_double_margin() { - let balance_wei = 30_000_000_000; - let threshold_intercept_wei = 4_000_000_000; - let permanent_debt_allowed_wei = 1_000_000_000; + let balance_minor = 30_000_000_000; + let threshold_intercept_minor = 4_000_000_000; + let permanent_debt_allowed_minor = 1_000_000_000; let subject = DisqualificationGaugeReal::default(); - todo!("finish the setup when pda can be added"); let result = subject.determine_limit( - balance_wei, - threshold_intercept_wei, - permanent_debt_allowed_wei, + balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, ); assert_eq!( @@ -333,72 +350,6 @@ mod tests { ) } - #[test] - fn list_accounts_nominated_for_disqualification_uses_the_right_manifest_const() { - todo!("do I still want this test??"); - let account_balance = 1_000_000; - let garbage_weight = 22222222; - let garbage_thresholds_intercept = 456789; - let garbage_creditor_thresholds = CreditorThresholds::new(123456); - let prepare_account = |n: u64| { - let mut account = make_payable_account(n); - account.balance_wei = account_balance; - account - }; - let payable_account_1 = prepare_account(1); - let payable_account_2 = prepare_account(2); - let payable_account_3 = prepare_account(3); - let edge: u128 = todo!(); - let proposed_ok_balance = edge + 1; - let unconfirmed_adjustment_1 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new( - payable_account_1, - garbage_thresholds_intercept, - garbage_creditor_thresholds, - ), - garbage_weight, - ), - proposed_ok_balance, - ); - let proposed_bad_balance_because_equal = edge; - let unconfirmed_adjustment_2 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new( - payable_account_2, - garbage_thresholds_intercept, - garbage_creditor_thresholds, - ), - garbage_weight, - ), - proposed_bad_balance_because_equal, - ); - let proposed_bad_balance_because_smaller = edge - 1; - let unconfirmed_adjustment_3 = UnconfirmedAdjustment::new( - WeightedAccount::new( - QualifiedPayableAccount::new( - payable_account_3, - garbage_thresholds_intercept, - garbage_creditor_thresholds, - ), - garbage_weight, - ), - proposed_bad_balance_because_smaller, - ); - let unconfirmed_adjustments = vec![ - unconfirmed_adjustment_1, - unconfirmed_adjustment_2.clone(), - unconfirmed_adjustment_3.clone(), - ]; - let subject = DisqualificationArbiter::new(Box::new(DisqualificationGaugeReal::default())); - - let result = subject.list_accounts_nominated_for_disqualification(&unconfirmed_adjustments); - - let expected_disqualified_accounts = - vec![&unconfirmed_adjustment_2, &unconfirmed_adjustment_3]; - assert_eq!(result, expected_disqualified_accounts) - } - #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { let idx_of_expected_result = 1; @@ -441,39 +392,48 @@ mod tests { "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; let now = SystemTime::now(); let cw_masq_balance = 200_000_000_000; + let mut payment_thresholds = PaymentThresholds::default(); + payment_thresholds.permanent_debt_allowed_gwei = 10; + payment_thresholds.maturity_threshold_sec = 1_000; + payment_thresholds.threshold_interval_sec = 10_000; + let base_time_for_qualified = payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec + 1; let logger = Logger::new(test_name); let subject = make_initialized_subject(now, Some(cw_masq_balance), None); let wallet_1 = make_wallet("abc"); + let debt_age_1 = base_time_for_qualified + 1; let account_1 = PayableAccount { wallet: wallet_1.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_1)).unwrap(), pending_payable_opt: None, }; let wallet_2 = make_wallet("def"); + let debt_age_2 = base_time_for_qualified + 3; let account_2 = PayableAccount { wallet: wallet_2.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_001)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_2)).unwrap(), pending_payable_opt: None, }; let wallet_3 = make_wallet("ghi"); + let debt_age_3 = base_time_for_qualified; let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_003)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_3)).unwrap(), pending_payable_opt: None, }; let wallet_4 = make_wallet("jkl"); + let debt_age_4 = base_time_for_qualified + 2; let account_4 = PayableAccount { wallet: wallet_4.clone(), balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1_000_002)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_4)).unwrap(), pending_payable_opt: None, }; let accounts = vec![account_1, account_2, account_3, account_4]; let qualified_payables = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = @@ -485,6 +445,10 @@ mod tests { &logger, ); + unconfirmed_adjustments.iter().for_each(|payable|{ + // Condition of disqualification at the horizontal threshold + assert!(payable.non_finalized_account.proposed_adjusted_balance_minor < 120_000_000_000) + }); assert_eq!(result, Some(wallet_3)); } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e458c01c6..3df969e3f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -2,7 +2,7 @@ // If possible, let these modules be private mod adjustment_runners; -mod criteria_calculators; +mod criterion_calculators; mod diagnostics; mod disqualification_arbiter; mod inner; @@ -43,8 +43,8 @@ use std::time::SystemTime; use thousands::Separable; use web3::types::U256; use masq_lib::utils::convert_collection; -use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::{CriterionCalculator}; +use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; +use crate::accountant::payment_adjuster::criterion_calculators::{CriterionCalculator}; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::disqualification_arbiter::{DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal}; use crate::accountant::QualifiedPayableAccount; @@ -372,13 +372,13 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = criterion_calculator.calculate(&payable); + let new_criterion = criterion_calculator.calculate(&payable, self.inner.as_ref()); let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( &payable.payable.wallet, - &**criterion_calculator, + criterion_calculator.as_ref(), new_criterion, summed_up, ); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index ff47745e9..c5bd5b3d8 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -3,10 +3,10 @@ #![cfg(test)] use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criteria_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; -use crate::accountant::payment_adjuster::criteria_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; +use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; -use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; +use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::QualifiedPayableAccount; @@ -114,7 +114,8 @@ pub struct CriterionCalculatorMock { } impl CriterionCalculator for CriterionCalculatorMock { - fn calculate(&self, _account: &QualifiedPayableAccount) -> u128 { + fn calculate(&self, _account: &QualifiedPayableAccount, _context: &dyn PaymentAdjusterInner) -> u128 { + // TODO consider using params assertions self.calculate_results.borrow_mut().remove(0) } From 1ce2cddf914779f6592347789230063bc0a0f244 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 25 Mar 2024 11:04:10 +0100 Subject: [PATCH 145/250] GH-711-b: couple of more tests made passing --- .../payment_adjuster/adjustment_runners.rs | 17 ++- .../balance_and_age_calculator.rs | 2 +- .../disqualification_arbiter.rs | 41 ++++--- node/src/accountant/payment_adjuster/mod.rs | 4 +- .../payment_adjuster/preparatory_analyser.rs | 103 ++++++++---------- .../accountant/payment_adjuster/test_utils.rs | 20 +++- 6 files changed, 105 insertions(+), 82 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 899e44a85..cd5c043e8 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -115,19 +115,17 @@ mod tests { empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; - use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, }; - use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; use std::fmt::Debug; use std::time::{Duration, SystemTime}; + use crate::sub_lib::accountant::PaymentThresholds; fn test_adjust_last_one( subject: AR, @@ -140,6 +138,8 @@ mod tests { let wallet = make_wallet("abc"); let mut qualified_payable = make_non_guaranteed_qualified_payable(111); qualified_payable.payable.balance_wei = 9_000_000_000; + qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; + qualified_payable.creditor_thresholds.permanent_debt_allowed_wei = 2_000_000_000; let cw_balance = 8_645_123_505; let adjustment = Adjustment::ByServiceFee; let mut payment_adjuster = PaymentAdjusterReal::new(); @@ -175,9 +175,13 @@ mod tests { fn adjust_multiple_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); let wallet_1 = make_wallet("abc"); + let mut payment_thresholds = PaymentThresholds::default(); + payment_thresholds.maturity_threshold_sec = 100; + payment_thresholds.threshold_interval_sec = 1000; + payment_thresholds.permanent_debt_allowed_gwei = 1; let account_1 = PayableAccount { wallet: wallet_1.clone(), - balance_wei: 5_000_000, + balance_wei: 5_000_000_000, last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), pending_payable_opt: None, }; @@ -186,12 +190,13 @@ mod tests { account_2.wallet = wallet_2.clone(); let accounts = vec![account_1, account_2]; let qualified_payables = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); - let cw_balance = 9_000_000; + todo!("what about this??...it's inaccurate due to the time addition"); + let cw_balance = 10_000_000_000; payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 68882d4bb..a35d09f57 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -11,7 +11,7 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { fn calculate(&self, account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner) -> u128 { let now = context.now(); let debt_age_s = now.duration_since(account.payable.last_paid_timestamp).expect("time traveller").as_secs(); - account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128 + (account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128) } fn parameter_name(&self) -> &'static str { diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index b47c6ceda..a36bbb71a 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -17,6 +17,12 @@ pub struct DisqualificationArbiter { disqualification_gauge: Box, } +impl Default for DisqualificationArbiter{ + fn default() -> Self { + Self::new(Box::new(DisqualificationGaugeReal::default())) + } +} + impl DisqualificationArbiter { pub fn new(disqualification_gauge: Box) -> Self { Self{ disqualification_gauge } @@ -84,13 +90,12 @@ impl DisqualificationArbiter { .non_finalized_account .proposed_adjusted_balance_minor; - if proposed_adjusted_balance <= disqualification_edge { + if proposed_adjusted_balance < disqualification_edge { account_nominated_for_disqualification_diagnostics( adjustment_info, proposed_adjusted_balance, disqualification_edge, ); - Some(adjustment_info) } else { None @@ -99,9 +104,9 @@ impl DisqualificationArbiter { .collect() } - fn find_account_with_smallest_weight<'a>( - accounts: &'a [&'a UnconfirmedAdjustment], - ) -> &'a AdjustedAccountBeforeFinalization { + fn find_account_with_smallest_weight<'account>( + accounts: &'account [&'account UnconfirmedAdjustment], + ) -> &'account AdjustedAccountBeforeFinalization { let first_account = &accounts.first().expect("collection was empty"); &accounts .iter() @@ -190,13 +195,8 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ weights_total, }; - use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, DisqualificationGaugeMock, PRESERVED_TEST_PAYMENT_THRESHOLDS, - }; - use crate::accountant::test_utils::{ - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - make_payable_account, - }; + use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment}; + use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; @@ -350,6 +350,21 @@ mod tests { ) } + #[test] + fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit(){ + let disqualification_gauge= DisqualificationGaugeMock::default().determine_limit_result(1_000_000_000).determine_limit_result(9_999_999_999); + let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(444); + account_1.non_finalized_account.proposed_adjusted_balance_minor = 1_000_000_000; + let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(777); + account_2.non_finalized_account.proposed_adjusted_balance_minor = 9_999_999_999; + let accounts = vec![account_1, account_2]; + let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); + + let result = subject.list_accounts_nominated_for_disqualification(&accounts); + + assert!(result.is_empty()) + } + #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { let idx_of_expected_result = 1; @@ -438,7 +453,7 @@ mod tests { let weights_total = weights_total(&weights_and_accounts); let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); - let subject = DisqualificationArbiter::new(Box::new(DisqualificationGaugeReal::default())); + let subject = DisqualificationArbiter::default(); let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( &unconfirmed_adjustments, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 3df969e3f..8742820b5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -151,9 +151,7 @@ impl PaymentAdjusterReal { pub fn new() -> Self { Self { analyzer: PreparatoryAnalyzer::new(), - disqualification_arbiter: DisqualificationArbiter::new(Box::new( - DisqualificationGaugeReal::default(), - )), + disqualification_arbiter: DisqualificationArbiter::default(), inner: Box::new(PaymentAdjusterInnerNull {}), calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], logger: Logger::new("PaymentAdjuster"), diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 0ee83cd87..4e6a3774f 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -1,6 +1,5 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::log_fns::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, @@ -15,7 +14,6 @@ use crate::accountant::QualifiedPayableAccount; use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; -use masq_lib::utils::type_name_of; use std::cmp::Ordering; pub struct PreparatoryAnalyzer {} @@ -67,21 +65,21 @@ impl PreparatoryAnalyzer { fn transaction_counts_verification( cw_transaction_fee_balance_minor: U256, - per_transaction_requirement_minor: u128, + fee_requirement_per_tx_minor: u128, number_of_qualified_accounts: usize, ) -> TransactionCountsWithin16bits { let max_possible_tx_count_u256 = Self::max_possible_tx_count( cw_transaction_fee_balance_minor, - per_transaction_requirement_minor, + fee_requirement_per_tx_minor, ); TransactionCountsWithin16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) } fn max_possible_tx_count( cw_transaction_fee_balance_minor: U256, - tx_fee_requirement_per_tx_minor: u128, + fee_requirement_per_tx_minor: u128, ) -> U256 { - cw_transaction_fee_balance_minor / U256::from(tx_fee_requirement_per_tx_minor) + cw_transaction_fee_balance_minor / U256::from(fee_requirement_per_tx_minor) } pub fn check_need_of_adjustment_by_service_fee( @@ -91,7 +89,7 @@ impl PreparatoryAnalyzer { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> Result { - let qualified_payables = Self::welcome_qualified_payables(payables); + let qualified_payables = Self::comb_qualified_payables(payables); let required_service_fee_sum: u128 = sum_as(&qualified_payables, |qa| qa.payable.balance_wei); @@ -99,7 +97,7 @@ impl PreparatoryAnalyzer { if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { - // TODO here you need to iterate through all and compute the edge and choose the smallest one + self.analyse_smallest_adjustment_possibility( disqualification_arbiter, &qualified_payables, @@ -115,7 +113,7 @@ impl PreparatoryAnalyzer { } } - fn welcome_qualified_payables<'payables>( + fn comb_qualified_payables<'payables>( payables: Either<&'payables [QualifiedPayableAccount], &'payables [WeightedAccount]>, ) -> Vec<&'payables QualifiedPayableAccount> { match payables { @@ -137,14 +135,7 @@ impl PreparatoryAnalyzer { qualified_payables: &[&QualifiedPayableAccount], cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - let lowest_disqualification_limit = qualified_payables - .iter() - .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) - .fold(todo!(), |lowest, limit| match Ord::cmp(&lowest, &limit) { - Ordering::Less => todo!(), - Ordering::Equal => todo!(), - Ordering::Greater => todo!(), - }); + let lowest_disqualification_limit = Self::find_lowest_dsq_limit(qualified_payables, disqualification_arbiter); if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(()) @@ -160,58 +151,54 @@ impl PreparatoryAnalyzer { ) } } + + fn find_lowest_dsq_limit(qualified_payables: &[&QualifiedPayableAccount], disqualification_arbiter: &DisqualificationArbiter)->u128{ + qualified_payables + .iter() + .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) + .fold(u128::MAX, |lowest_so_far, limit| match Ord::cmp(&lowest_so_far, &limit) { + Ordering::Less => lowest_so_far, + Ordering::Equal => lowest_so_far, + Ordering::Greater => limit, + }) + } } #[cfg(test)] mod tests { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::disqualification_arbiter::{ - DisqualificationArbiter, DisqualificationGaugeReal, + DisqualificationArbiter, }; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::test_utils::DisqualificationGaugeMock; use crate::accountant::payment_adjuster::PaymentAdjusterError; - use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, make_payable_account, + make_non_guaranteed_qualified_payable, }; use crate::accountant::QualifiedPayableAccount; - use ethereum_types::U256; - use masq_lib::logger::Logger; - use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; #[test] - fn tx_fee_check_panics_on_ration_between_tx_fee_balance_and_estimated_tx_fee_bigger_than_u128() - { - let deadly_ratio = U256::from(u128::MAX) + 1; - let estimated_transaction_fee_per_one_tx_minor = 123_456_789; - let critical_wallet_balance = - deadly_ratio * U256::from(estimated_transaction_fee_per_one_tx_minor); - let blockchain_agent = BlockchainAgentMock::default() - .estimated_transaction_fee_per_transaction_minor_result( - estimated_transaction_fee_per_one_tx_minor, - ) - .transaction_fee_balance_minor_result(critical_wallet_balance); - let subject = PreparatoryAnalyzer::new(); - - let panic_err = catch_unwind(AssertUnwindSafe(|| { - subject.determine_transaction_count_limit_by_transaction_fee( - &blockchain_agent, - 123, - &Logger::new("test"), - ) - })) - .unwrap_err(); - - let err_msg = panic_err.downcast_ref::().unwrap(); - let expected_panic = format!( - "Transaction fee balance {} wei in the consuming wallet cases panic given estimated \ - transaction fee per tx {} wei and resulting ratio {}, that should fit in u128, \ - respectively: \"integer overflow when casting to u128\"", - critical_wallet_balance, estimated_transaction_fee_per_one_tx_minor, deadly_ratio - ); - assert_eq!(err_msg, &expected_panic) + fn find_lowest_dsq_limit_begins_at_u128_max(){ + let disqualification_arbiter = DisqualificationArbiter::default(); + let result = PreparatoryAnalyzer::find_lowest_dsq_limit(&[], &disqualification_arbiter); + + assert_eq!(result, u128::MAX) + } + + #[test] + fn find_lowest_dsq_limit_when_multiple_accounts_with_the_same_limit(){ + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(200_000_000) + .determine_limit_result(200_000_000); + let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let account_1 = make_non_guaranteed_qualified_payable(111); + let account_2 = make_non_guaranteed_qualified_payable(222); + let accounts = vec![&account_1, &account_2]; + + let result = PreparatoryAnalyzer::find_lowest_dsq_limit(&accounts, &disqualification_arbiter); + + assert_eq!(result, 200_000_000) } fn test_body_for_adjustment_possibility_nearly_rejected( @@ -299,9 +286,9 @@ mod tests { let mut account_1 = make_non_guaranteed_qualified_payable(111); account_1.payable.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(222); - account_2.payable.balance_wei = 1_000_000_000; + account_2.payable.balance_wei = 1_000_050_000; let mut account_3 = make_non_guaranteed_qualified_payable(333); - account_3.payable.balance_wei = 1_000_100_000; + account_3.payable.balance_wei = 1_000_111_111; let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts @@ -310,7 +297,7 @@ mod tests { let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(1_500_000_000) .determine_limit_result(1_000_000_101) - .determine_limit_result(1_000_123_000); + .determine_limit_result(1_000_000_222); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; @@ -326,7 +313,7 @@ mod tests { Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, - total_amount_demanded_minor: 2_000_000_000 + 2_000_000_002 + 1_000_000_002, + total_amount_demanded_minor: 2_000_000_000 + 1_000_050_000 + 1_000_111_111, cw_service_fee_balance_minor: cw_service_fee_balance } ) diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index c5bd5b3d8..340edb88d 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -19,6 +19,11 @@ use masq_lib::logger::Logger; use std::cell::RefCell; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; +use rusqlite::params; +use websocket::header::q; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, UnconfirmedAdjustment}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_10; +use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; lazy_static! { pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = @@ -109,6 +114,18 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ ) } +pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64)-> UnconfirmedAdjustment{ + let qualified_payable = make_non_guaranteed_qualified_payable(n); + let proposed_adjusted_balance_minor= (qualified_payable.payable.balance_wei / 2) + log_10(n as u128) as u128; + UnconfirmedAdjustment{ + non_finalized_account: AdjustedAccountBeforeFinalization{ + original_qualified_account: qualified_payable, + proposed_adjusted_balance_minor, + }, + weight: (n as u128).pow(3), + } +} + pub struct CriterionCalculatorMock { calculate_results: RefCell>, } @@ -144,7 +161,8 @@ impl DisqualificationGauge for DisqualificationGaugeMock { threshold_intercept_wei: u128, permanent_debt_allowed_wei: u128, ) -> u128 { - todo!() + self.determine_limit_params.lock().unwrap().push((account_balance_wei, threshold_intercept_wei, permanent_debt_allowed_wei)); + self.determine_limit_results.borrow_mut().remove(0) } } From bcd3d9a3a779ec5e27bee67decee34ddb711cdf5 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 25 Mar 2024 22:05:09 +0100 Subject: [PATCH 146/250] GH-711-b: savepoint before considering more precise method of avoiding the fractional computation --- node/src/accountant/mod.rs | 4 +- .../payment_adjuster/adjustment_runners.rs | 7 +- .../balance_and_age_calculator.rs | 49 ++++++++---- .../criterion_calculators/mod.rs | 6 +- .../disqualification_arbiter.rs | 78 +++++++++++++------ .../miscellaneous/data_structures.rs | 16 ---- .../miscellaneous/helper_functions.rs | 34 ++------ node/src/accountant/payment_adjuster/mod.rs | 14 ++-- .../payment_adjuster/preparatory_analyser.rs | 37 +++++---- .../accountant/payment_adjuster/test_utils.rs | 41 ++++++---- 10 files changed, 159 insertions(+), 127 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 1ebc98319..28459219c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -142,7 +142,7 @@ impl QualifiedPayableAccount { payment_threshold_intercept_minor: u128, creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { - Self{ + Self { payable, payment_threshold_intercept_minor, creditor_thresholds, @@ -157,7 +157,7 @@ pub struct CreditorThresholds { impl CreditorThresholds { pub fn new(permanent_debt_allowed_wei: u128) -> Self { - Self{ + Self { permanent_debt_allowed_wei, } } diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index cd5c043e8..20d566cdb 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -120,12 +120,12 @@ mod tests { use crate::accountant::test_utils::{ make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, }; + use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; use std::fmt::Debug; use std::time::{Duration, SystemTime}; - use crate::sub_lib::accountant::PaymentThresholds; fn test_adjust_last_one( subject: AR, @@ -139,7 +139,9 @@ mod tests { let mut qualified_payable = make_non_guaranteed_qualified_payable(111); qualified_payable.payable.balance_wei = 9_000_000_000; qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; - qualified_payable.creditor_thresholds.permanent_debt_allowed_wei = 2_000_000_000; + qualified_payable + .creditor_thresholds + .permanent_debt_allowed_wei = 2_000_000_000; let cw_balance = 8_645_123_505; let adjustment = Adjustment::ByServiceFee; let mut payment_adjuster = PaymentAdjusterReal::new(); @@ -195,7 +197,6 @@ mod tests { affordable_transaction_count: 1, }; let mut payment_adjuster = PaymentAdjusterReal::new(); - todo!("what about this??...it's inaccurate due to the time addition"); let cw_balance = 10_000_000_000; payment_adjuster.initialize_inner(cw_balance, adjustment, now); let subject = ServiceFeeOnlyAdjustmentRunner {}; diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index a35d09f57..83d712b13 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -8,10 +8,18 @@ use crate::accountant::QualifiedPayableAccount; pub struct BalanceAndAgeCriterionCalculator {} impl CriterionCalculator for BalanceAndAgeCriterionCalculator { - fn calculate(&self, account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner) -> u128 { + fn calculate( + &self, + account: &QualifiedPayableAccount, + context: &dyn PaymentAdjusterInner, + ) -> u128 { let now = context.now(); - let debt_age_s = now.duration_since(account.payable.last_paid_timestamp).expect("time traveller").as_secs(); - (account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128) + let debt_age_s = now + .duration_since(account.payable.last_paid_timestamp) + .expect("time traveller") + .as_secs(); + (account.payable.balance_wei - account.payment_threshold_intercept_minor + + debt_age_s as u128) } fn parameter_name(&self) -> &'static str { @@ -21,16 +29,14 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { #[cfg(test)] mod tests { - use std::time::SystemTime; - use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::{ - BalanceAndAgeCriterionCalculator, - }; + use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use std::time::SystemTime; #[test] - fn calculator_knows_its_name(){ + fn calculator_knows_its_name() { let subject = BalanceAndAgeCriterionCalculator::default(); let result = subject.parameter_name(); @@ -41,17 +47,30 @@ mod tests { #[test] fn balance_and_age_criterion_calculator_works() { let now = SystemTime::now(); - let qualified_accounts = [0,10,22].into_iter().map(|n| make_non_guaranteed_qualified_payable(n)).collect::>(); + let qualified_accounts = [0, 10, 22] + .into_iter() + .map(|n| make_non_guaranteed_qualified_payable(n)) + .collect::>(); let payment_adjuster_inner = PaymentAdjusterInnerReal::new(now, None, 123456789); let subject = BalanceAndAgeCriterionCalculator::default(); - let computed_criteria = qualified_accounts.iter().map(|qualified_account|subject.calculate(qualified_account, &payment_adjuster_inner)).collect::>(); + let computed_criteria = qualified_accounts + .iter() + .map(|qualified_account| subject.calculate(qualified_account, &payment_adjuster_inner)) + .collect::>(); - let zipped = qualified_accounts.into_iter().zip(computed_criteria.into_iter()); - zipped.into_iter().for_each(|(account, actual_criterion)|{ - let debt_age_s = now.duration_since(account.payable.last_paid_timestamp).unwrap().as_secs(); - let expected_criterion = account.payable.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128; - assert_eq!(actual_criterion,expected_criterion) + let zipped = qualified_accounts + .into_iter() + .zip(computed_criteria.into_iter()); + zipped.into_iter().for_each(|(account, actual_criterion)| { + let debt_age_s = now + .duration_since(account.payable.last_paid_timestamp) + .unwrap() + .as_secs(); + let expected_criterion = account.payable.balance_wei + - account.payment_threshold_intercept_minor + + debt_age_s as u128; + assert_eq!(actual_criterion, expected_criterion) }) } } diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index 8e93e06bd..df7a4765e 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -7,7 +7,11 @@ use crate::accountant::QualifiedPayableAccount; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { - fn calculate(&self, account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner) -> u128; + fn calculate( + &self, + account: &QualifiedPayableAccount, + context: &dyn PaymentAdjusterInner, + ) -> u128; fn parameter_name(&self) -> &'static str; } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index a36bbb71a..61f6191ea 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -17,7 +17,7 @@ pub struct DisqualificationArbiter { disqualification_gauge: Box, } -impl Default for DisqualificationArbiter{ +impl Default for DisqualificationArbiter { fn default() -> Self { Self::new(Box::new(DisqualificationGaugeReal::default())) } @@ -25,7 +25,9 @@ impl Default for DisqualificationArbiter{ impl DisqualificationArbiter { pub fn new(disqualification_gauge: Box) -> Self { - Self{ disqualification_gauge } + Self { + disqualification_gauge, + } } pub fn try_finding_an_account_to_disqualify_in_this_iteration( &self, @@ -71,7 +73,13 @@ impl DisqualificationArbiter { &self, qualified_payable: &QualifiedPayableAccount, ) -> u128 { - self.disqualification_gauge.determine_limit(qualified_payable.payable.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable.creditor_thresholds.permanent_debt_allowed_wei) + self.disqualification_gauge.determine_limit( + qualified_payable.payable.balance_wei, + qualified_payable.payment_threshold_intercept_minor, + qualified_payable + .creditor_thresholds + .permanent_debt_allowed_wei, + ) } fn list_accounts_nominated_for_disqualification<'unconfirmed_adj>( @@ -145,21 +153,22 @@ impl DisqualificationGauge for DisqualificationGaugeReal { permanent_debt_allowed_minor: u128, ) -> u128 { if threshold_intercept_minor == permanent_debt_allowed_minor { - return account_balance_minor + return account_balance_minor; } let exceeding_debt_part = account_balance_minor - threshold_intercept_minor; - if DisqualificationGaugeReal::qualifies_for_double_margin(account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor){ - exceeding_debt_part + 2* permanent_debt_allowed_minor + if DisqualificationGaugeReal::qualifies_for_double_margin( + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, + ) { + exceeding_debt_part + 2 * permanent_debt_allowed_minor } else { exceeding_debt_part + permanent_debt_allowed_minor } } } - - impl DisqualificationGaugeReal { - const FIRST_CONDITION_COEFFICIENT: u128 = 2; const SECOND_CONDITION_COEFFICIENT: u128 = 2; fn qualifies_for_double_margin( @@ -171,9 +180,11 @@ impl DisqualificationGaugeReal { let considered_forgiven = threshold_intercept_minor - permanent_debt_allowed_minor; let minimal_payment_accepted = exceeding_threshold + permanent_debt_allowed_minor; - let first_condition = minimal_payment_accepted >= Self::FIRST_CONDITION_COEFFICIENT * considered_forgiven; + let first_condition = + minimal_payment_accepted >= Self::FIRST_CONDITION_COEFFICIENT * considered_forgiven; - let second_condition = considered_forgiven >= Self::SECOND_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; + let second_condition = considered_forgiven + >= Self::SECOND_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; if first_condition && second_condition { true @@ -192,19 +203,23 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - weights_total, + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::test_utils::{ + make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, + DisqualificationGaugeMock, + }; + use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + make_payable_account, }; - use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment}; - use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; + use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; use std::time::{Duration, SystemTime}; - use crate::sub_lib::accountant::PaymentThresholds; #[test] - fn constants_are_correct(){ + fn constants_are_correct() { assert_eq!(DisqualificationGaugeReal::FIRST_CONDITION_COEFFICIENT, 2); assert_eq!(DisqualificationGaugeReal::SECOND_CONDITION_COEFFICIENT, 2) } @@ -218,7 +233,7 @@ mod tests { let result = DisqualificationGaugeReal::qualifies_for_double_margin( account_balance_minor, threshold_intercept_minor, - permanent_debt_allowed_minor + permanent_debt_allowed_minor, ); assert_eq!(result, true) @@ -351,12 +366,18 @@ mod tests { } #[test] - fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit(){ - let disqualification_gauge= DisqualificationGaugeMock::default().determine_limit_result(1_000_000_000).determine_limit_result(9_999_999_999); + fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit() { + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(1_000_000_000) + .determine_limit_result(9_999_999_999); let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(444); - account_1.non_finalized_account.proposed_adjusted_balance_minor = 1_000_000_000; + account_1 + .non_finalized_account + .proposed_adjusted_balance_minor = 1_000_000_000; let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(777); - account_2.non_finalized_account.proposed_adjusted_balance_minor = 9_999_999_999; + account_2 + .non_finalized_account + .proposed_adjusted_balance_minor = 9_999_999_999; let accounts = vec![account_1, account_2]; let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); @@ -411,7 +432,9 @@ mod tests { payment_thresholds.permanent_debt_allowed_gwei = 10; payment_thresholds.maturity_threshold_sec = 1_000; payment_thresholds.threshold_interval_sec = 10_000; - let base_time_for_qualified = payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec + 1; + let base_time_for_qualified = payment_thresholds.maturity_threshold_sec + + payment_thresholds.threshold_interval_sec + + 1; let logger = Logger::new(test_name); let subject = make_initialized_subject(now, Some(cw_masq_balance), None); let wallet_1 = make_wallet("abc"); @@ -460,9 +483,14 @@ mod tests { &logger, ); - unconfirmed_adjustments.iter().for_each(|payable|{ + unconfirmed_adjustments.iter().for_each(|payable| { // Condition of disqualification at the horizontal threshold - assert!(payable.non_finalized_account.proposed_adjusted_balance_minor < 120_000_000_000) + assert!( + payable + .non_finalized_account + .proposed_adjusted_balance_minor + < 120_000_000_000 + ) }); assert_eq!(result, Some(wallet_3)); } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index c5f2525ca..799401155 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -116,22 +116,6 @@ pub enum AdjustmentResolution { Revert, } -// Sets the minimal percentage of the original balance that must be proposed after the adjustment -// or the account will be eliminated for insignificance -#[derive(Debug, PartialEq, Eq)] -pub struct PercentageAccountInsignificance { - // Using integers means we have to represent accurate percentage - // as set of two constants - pub multiplier: u128, - pub divisor: u128, -} - -impl PercentageAccountInsignificance { - pub fn compute_reduction(&self, debt_part_above_threshold_wei: u128) -> u128 { - todo!() - } -} - pub struct TransactionCountsWithin16bits { pub affordable: u16, pub required: u16, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 83f149bc9..5dc1261a2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -3,31 +3,20 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ - account_nominated_for_disqualification_diagnostics, exhausting_cw_balance_diagnostics, - not_exhausting_cw_balance_diagnostics, possibly_outweighed_accounts_diagnostics, - try_finding_an_account_to_disqualify_diagnostics, + exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, + possibly_outweighed_accounts_diagnostics, }; -use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, - PercentageAccountInsignificance, UnconfirmedAdjustment, WeightedAccount, + UnconfirmedAdjustment, WeightedAccount, }; use crate::accountant::QualifiedPayableAccount; -use crate::sub_lib::wallet::Wallet; use itertools::{Either, Itertools}; -use masq_lib::logger::Logger; -use std::cmp::Ordering; use std::iter::successors; use web3::types::U256; const MAX_EXPONENT_FOR_10_WITHIN_U128: u32 = 76; -const EMPIRIC_PRECISION_COEFFICIENT: usize = 8; -// Represents 50% -pub const EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO: PercentageAccountInsignificance = - PercentageAccountInsignificance { - multiplier: 1, - divisor: 2, - }; +const EMPIRIC_PRECISION_COEFFICIENT: usize = 1; pub fn zero_affordable_accounts_found( accounts: &Either, Vec>, @@ -301,15 +290,13 @@ impl From for PayableAccount { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, PercentageAccountInsignificance, UnconfirmedAdjustment, - WeightedAccount, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, - log_10, weights_total, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, - EMPIRIC_PRECISION_COEFFICIENT, EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, - MAX_EXPONENT_FOR_10_WITHIN_U128, + log_10, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, + EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, }; use crate::accountant::payment_adjuster::test_utils::{ make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -332,13 +319,6 @@ mod tests { fn constants_are_correct() { assert_eq!(MAX_EXPONENT_FOR_10_WITHIN_U128, 76); assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); - assert_eq!( - EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, - PercentageAccountInsignificance { - multiplier: 1, - divisor: 2 - } - ); } #[test] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8742820b5..bb3df141d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -370,7 +370,8 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = criterion_calculator.calculate(&payable, self.inner.as_ref()); + let new_criterion = + criterion_calculator.calculate(&payable, self.inner.as_ref()); let summed_up = weight + new_criterion; @@ -687,7 +688,7 @@ mod tests { use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO, weights_total}; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total}; use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -1164,10 +1165,11 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let service_fee_balance_in_minor_units = ((1_000_000_000_000 - * EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.multiplier) - / EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.divisor) - - 1; + let service_fee_balance_in_minor_units = todo!(); + // ((1_000_000_000_000 + // * EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.multiplier) + // / EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.divisor) + // - 1; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 4e6a3774f..46e0cbafc 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -97,7 +97,6 @@ impl PreparatoryAnalyzer { if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) } else { - self.analyse_smallest_adjustment_possibility( disqualification_arbiter, &qualified_payables, @@ -135,7 +134,8 @@ impl PreparatoryAnalyzer { qualified_payables: &[&QualifiedPayableAccount], cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { - let lowest_disqualification_limit = Self::find_lowest_dsq_limit(qualified_payables, disqualification_arbiter); + let lowest_disqualification_limit = + Self::find_lowest_dsq_limit(qualified_payables, disqualification_arbiter); if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(()) @@ -152,34 +152,35 @@ impl PreparatoryAnalyzer { } } - fn find_lowest_dsq_limit(qualified_payables: &[&QualifiedPayableAccount], disqualification_arbiter: &DisqualificationArbiter)->u128{ + fn find_lowest_dsq_limit( + qualified_payables: &[&QualifiedPayableAccount], + disqualification_arbiter: &DisqualificationArbiter, + ) -> u128 { qualified_payables .iter() .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) - .fold(u128::MAX, |lowest_so_far, limit| match Ord::cmp(&lowest_so_far, &limit) { - Ordering::Less => lowest_so_far, - Ordering::Equal => lowest_so_far, - Ordering::Greater => limit, + .fold(u128::MAX, |lowest_so_far, limit| { + match Ord::cmp(&lowest_so_far, &limit) { + Ordering::Less => lowest_so_far, + Ordering::Equal => lowest_so_far, + Ordering::Greater => limit, + } }) } } #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::disqualification_arbiter::{ - DisqualificationArbiter, - }; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::test_utils::DisqualificationGaugeMock; use crate::accountant::payment_adjuster::PaymentAdjusterError; - use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, - }; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use crate::accountant::QualifiedPayableAccount; use std::sync::{Arc, Mutex}; #[test] - fn find_lowest_dsq_limit_begins_at_u128_max(){ + fn find_lowest_dsq_limit_begins_at_u128_max() { let disqualification_arbiter = DisqualificationArbiter::default(); let result = PreparatoryAnalyzer::find_lowest_dsq_limit(&[], &disqualification_arbiter); @@ -187,16 +188,18 @@ mod tests { } #[test] - fn find_lowest_dsq_limit_when_multiple_accounts_with_the_same_limit(){ + fn find_lowest_dsq_limit_when_multiple_accounts_with_the_same_limit() { let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(200_000_000) .determine_limit_result(200_000_000); - let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); let account_1 = make_non_guaranteed_qualified_payable(111); let account_2 = make_non_guaranteed_qualified_payable(222); let accounts = vec![&account_1, &account_2]; - let result = PreparatoryAnalyzer::find_lowest_dsq_limit(&accounts, &disqualification_arbiter); + let result = + PreparatoryAnalyzer::find_lowest_dsq_limit(&accounts, &disqualification_arbiter); assert_eq!(result, 200_000_000) } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 340edb88d..69276ac59 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -7,8 +7,13 @@ use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_ use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_10; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; +use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; @@ -16,14 +21,11 @@ use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; +use rusqlite::params; use std::cell::RefCell; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; -use rusqlite::params; use websocket::header::q; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, UnconfirmedAdjustment}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_10; -use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; lazy_static! { pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = @@ -114,16 +116,17 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ ) } -pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64)-> UnconfirmedAdjustment{ +pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_payable = make_non_guaranteed_qualified_payable(n); - let proposed_adjusted_balance_minor= (qualified_payable.payable.balance_wei / 2) + log_10(n as u128) as u128; - UnconfirmedAdjustment{ - non_finalized_account: AdjustedAccountBeforeFinalization{ - original_qualified_account: qualified_payable, - proposed_adjusted_balance_minor, - }, - weight: (n as u128).pow(3), - } + let proposed_adjusted_balance_minor = + (qualified_payable.payable.balance_wei / 2) + log_10(n as u128) as u128; + UnconfirmedAdjustment { + non_finalized_account: AdjustedAccountBeforeFinalization { + original_qualified_account: qualified_payable, + proposed_adjusted_balance_minor, + }, + weight: (n as u128).pow(3), + } } pub struct CriterionCalculatorMock { @@ -131,7 +134,11 @@ pub struct CriterionCalculatorMock { } impl CriterionCalculator for CriterionCalculatorMock { - fn calculate(&self, _account: &QualifiedPayableAccount, _context: &dyn PaymentAdjusterInner) -> u128 { + fn calculate( + &self, + _account: &QualifiedPayableAccount, + _context: &dyn PaymentAdjusterInner, + ) -> u128 { // TODO consider using params assertions self.calculate_results.borrow_mut().remove(0) } @@ -161,7 +168,11 @@ impl DisqualificationGauge for DisqualificationGaugeMock { threshold_intercept_wei: u128, permanent_debt_allowed_wei: u128, ) -> u128 { - self.determine_limit_params.lock().unwrap().push((account_balance_wei, threshold_intercept_wei, permanent_debt_allowed_wei)); + self.determine_limit_params.lock().unwrap().push(( + account_balance_wei, + threshold_intercept_wei, + permanent_debt_allowed_wei, + )); self.determine_limit_results.borrow_mut().remove(0) } } From 8c49ec447742b5a69d26550276cce092e535c05a Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 27 Mar 2024 14:52:19 +0100 Subject: [PATCH 147/250] GH-711-b: ready for repairs of big tests in the mod file --- node/src/accountant/mod.rs | 10 +- .../payment_adjuster/adjustment_runners.rs | 140 ++++++- .../balance_and_age_calculator.rs | 12 +- .../payment_adjuster/diagnostics.rs | 24 +- .../disqualification_arbiter.rs | 20 +- node/src/accountant/payment_adjuster/inner.rs | 2 +- .../accountant/payment_adjuster/log_fns.rs | 6 +- .../miscellaneous/data_structures.rs | 18 +- .../miscellaneous/helper_functions.rs | 324 +++++----------- node/src/accountant/payment_adjuster/mod.rs | 358 ++++++++++-------- .../payment_adjuster/preparatory_analyser.rs | 37 +- .../accountant/payment_adjuster/test_utils.rs | 9 +- node/src/accountant/scanners/mod.rs | 2 +- .../src/accountant/scanners/scanners_utils.rs | 6 +- 14 files changed, 485 insertions(+), 483 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 28459219c..1cc6b9ef0 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -131,19 +131,19 @@ pub struct ReceivedPayments { #[derive(Debug, PartialEq, Eq, Clone)] pub struct QualifiedPayableAccount { - pub payable: PayableAccount, + pub qualified_as: PayableAccount, pub payment_threshold_intercept_minor: u128, pub creditor_thresholds: CreditorThresholds, } impl QualifiedPayableAccount { pub fn new( - payable: PayableAccount, + qualified_as: PayableAccount, payment_threshold_intercept_minor: u128, creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { Self { - payable, + qualified_as, payment_threshold_intercept_minor, creditor_thresholds, } @@ -1545,11 +1545,11 @@ mod tests { ); let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1.payable.clone() + ..unadjusted_account_1.qualified_as.clone() }; let adjusted_account_2 = PayableAccount { balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2.payable.clone() + ..unadjusted_account_2.qualified_as.clone() }; let response_skeleton = ResponseSkeleton { client_id: 12, diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 20d566cdb..76e2569f9 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -2,11 +2,13 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, WeightedAccount, + AdjustedAccountBeforeFinalization, WeightedPayable, }; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use crate::accountant::QualifiedPayableAccount; use itertools::Either; +use masq_lib::utils::convert_collection; use std::vec; // There are only two runners. They perform adjustment either by both the transaction and service @@ -20,6 +22,7 @@ use std::vec; pub trait AdjustmentRunner { type ReturnType; + //TODO old code // This specialized method: // a) helps with writing tests targeting edge cases, // b) allows to avoid performing unnecessary computation for an evident result @@ -29,10 +32,10 @@ pub trait AdjustmentRunner { last_account: QualifiedPayableAccount, ) -> Self::ReturnType; - fn adjust_multiple( + fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts_in_descending_order: Vec, + weighted_accounts_in_descending_order: Vec, ) -> Self::ReturnType; } @@ -49,14 +52,15 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: QualifiedPayableAccount, ) -> Self::ReturnType { + todo!("to be pulled out"); let account_opt = payment_adjuster.adjust_last_account_opt(last_account); Ok(Either::Left(empty_or_single_element_vector(account_opt))) } - fn adjust_multiple( + fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts_in_descending_order: Vec, + weighted_accounts_in_descending_order: Vec, ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { @@ -85,17 +89,28 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { payment_adjuster: &PaymentAdjusterReal, last_account: QualifiedPayableAccount, ) -> Self::ReturnType { + todo!("to be pulled out"); let account_opt = payment_adjuster.adjust_last_account_opt(last_account); empty_or_single_element_vector(account_opt) } - fn adjust_multiple( + fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts_in_descending_order: Vec, + weighted_accounts: Vec, ) -> Self::ReturnType { - payment_adjuster - .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order) + let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { + weighted_account.qualified_account.qualified_as.balance_wei + }); + let unallocated_cw_balance = payment_adjuster + .inner + .unallocated_cw_service_fee_balance_minor(); + if check_sum <= unallocated_cw_balance { + // Fast return after a direct conversion into the expected type + return convert_collection(weighted_accounts); + } + + payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts) } } @@ -115,7 +130,12 @@ mod tests { empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, WeightedPayable, + }; + use crate::accountant::payment_adjuster::test_utils::{ + make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, + }; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, @@ -137,7 +157,7 @@ mod tests { let now = SystemTime::now(); let wallet = make_wallet("abc"); let mut qualified_payable = make_non_guaranteed_qualified_payable(111); - qualified_payable.payable.balance_wei = 9_000_000_000; + qualified_payable.qualified_as.balance_wei = 9_000_000_000; qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; qualified_payable .creditor_thresholds @@ -152,7 +172,7 @@ mod tests { assert_eq!( result, expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable, + qualified_payable: qualified_payable, proposed_adjusted_balance_minor: cw_balance, }]) ) @@ -173,8 +193,84 @@ mod tests { }) } + fn initialize_payment_adjuster( + now: SystemTime, + service_fee_balance: u128, + ) -> PaymentAdjusterReal { + make_initialized_subject(now, Some(service_fee_balance), None) + } + + fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { + let mut payable = WeightedPayable { + qualified_account: make_non_guaranteed_qualified_payable(111), + weight: n as u128 * 1234, + }; + payable.qualified_account.qualified_as.balance_wei = initial_balance_minor; + payable + } + + fn test_surplus_incurred_after_disqualification_in_previous_iteration( + payable_1: WeightedPayable, + payable_2: WeightedPayable, + cw_service_fee_balance_minor: u128, + ) { + // The disqualification doesn't take part in here, it is just an explanation for + // those who wonder why the implied surplus may happen + let now = SystemTime::now(); + let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor); + let initial_balance_minor_1 = payable_1.qualified_account.qualified_as.balance_wei; + let initial_balance_minor_2 = payable_2.qualified_account.qualified_as.balance_wei; + let subject = ServiceFeeOnlyAdjustmentRunner {}; + + let result = subject.adjust_accounts( + &mut payment_adjuster, + vec![payable_1.clone(), payable_2.clone()], + ); + + assert_eq!( + result, + vec![ + AdjustedAccountBeforeFinalization { + qualified_payable: payable_1.qualified_account, + proposed_adjusted_balance_minor: initial_balance_minor_1 + }, + AdjustedAccountBeforeFinalization { + qualified_payable: payable_2.qualified_account, + proposed_adjusted_balance_minor: initial_balance_minor_2 + } + ] + ) + } + + #[test] + fn service_fee_only_runner_handles_surplus_even_to_cw_after_dql_in_previous_iteration() { + let cw_service_fee_balance_minor = 10_000_000_000; + let payable_1 = make_weighed_payable(111, 5_000_000_000); + let payable_2 = make_weighed_payable(222, 5_000_000_000); + + test_surplus_incurred_after_disqualification_in_previous_iteration( + payable_1, + payable_2, + cw_service_fee_balance_minor, + ) + } + #[test] - fn adjust_multiple_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { + fn service_fee_only_runner_handles_surplus_incurred_after_disqualification_in_previous_iteration( + ) { + let cw_service_fee_balance_minor = 10_000_000_000; + let payable_1 = make_weighed_payable(111, 5_000_000_000); + let payable_2 = make_weighed_payable(222, 4_999_999_999); + + test_surplus_incurred_after_disqualification_in_previous_iteration( + payable_1, + payable_2, + cw_service_fee_balance_minor, + ) + } + + #[test] + fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { let now = SystemTime::now(); let wallet_1 = make_wallet("abc"); let mut payment_thresholds = PaymentThresholds::default(); @@ -190,28 +286,30 @@ mod tests { let wallet_2 = make_wallet("def"); let mut account_2 = account_1.clone(); account_2.wallet = wallet_2.clone(); + let wallet_3 = make_wallet("ghj"); + let mut account_3 = account_1.clone(); + account_3.wallet = wallet_3; let accounts = vec![account_1, account_2]; let qualified_payables = make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; - let mut payment_adjuster = PaymentAdjusterReal::new(); - let cw_balance = 10_000_000_000; - payment_adjuster.initialize_inner(cw_balance, adjustment, now); + let service_fee_balance_wei = 10_000_000_000; + let mut payment_adjuster = initialize_payment_adjuster(now, service_fee_balance_wei); let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(qualified_payables); - let result = subject.adjust_multiple(&mut payment_adjuster, criteria_and_accounts); + let result = subject.adjust_accounts(&mut payment_adjuster, criteria_and_accounts); let returned_accounts = result .into_iter() - .map(|account| account.original_qualified_account.payable.wallet) + .map(|account| account.qualified_payable.qualified_as.wallet) .collect::>(); assert_eq!(returned_accounts, vec![wallet_1, wallet_2]) - // If the transaction fee adjustment had been available to perform, only one account would've been - // returned. This test passes + // If the transaction fee adjustment had been available to be performed, only one account + // would've been returned. This test passes } #[test] @@ -224,7 +322,7 @@ mod tests { #[test] fn empty_or_single_element_vector_for_some() { let account_info = AdjustedAccountBeforeFinalization { - original_qualified_account: make_non_guaranteed_qualified_payable(123), + qualified_payable: make_non_guaranteed_qualified_payable(123), proposed_adjusted_balance_minor: 123_456_789, }; let result = empty_or_single_element_vector(Some(account_info.clone())); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 83d712b13..fcd8a972b 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -14,12 +14,14 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { context: &dyn PaymentAdjusterInner, ) -> u128 { let now = context.now(); + let debt_age_s = now - .duration_since(account.payable.last_paid_timestamp) + .duration_since(account.qualified_as.last_paid_timestamp) .expect("time traveller") .as_secs(); - (account.payable.balance_wei - account.payment_threshold_intercept_minor - + debt_age_s as u128) + + account.qualified_as.balance_wei - account.payment_threshold_intercept_minor + + debt_age_s as u128 } fn parameter_name(&self) -> &'static str { @@ -64,10 +66,10 @@ mod tests { .zip(computed_criteria.into_iter()); zipped.into_iter().for_each(|(account, actual_criterion)| { let debt_age_s = now - .duration_since(account.payable.last_paid_timestamp) + .duration_since(account.qualified_as.last_paid_timestamp) .unwrap() .as_secs(); - let expected_criterion = account.payable.balance_wei + let expected_criterion = account.qualified_as.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128; assert_eq!(actual_criterion, expected_criterion) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 2f00ae4d3..412951afa 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -104,12 +104,12 @@ pub mod ordinary_diagnostic_functions { account_info: &AdjustedAccountBeforeFinalization, ) { diagnostics!( - &account_info.original_qualified_account.payable.wallet, + &account_info.qualified_payable.qualified_as.wallet, "OUTWEIGHED ACCOUNT FOUND", "Original balance: {}, proposed balance: {}", account_info - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei .separate_with_commas(), account_info @@ -126,8 +126,8 @@ pub mod ordinary_diagnostic_functions { diagnostics!( account_info .non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .wallet, "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", "Proposed: {}, disqualification limit: {}", @@ -144,8 +144,8 @@ pub mod ordinary_diagnostic_functions { "EXHAUSTING CW ON PAYMENT", "For account {} from proposed {} to the possible maximum of {}", non_finalized_account_info - .original_qualified_account - .payable + .qualified_payable + .qualified_as .wallet, non_finalized_account_info.proposed_adjusted_balance_minor, non_finalized_account_info.proposed_adjusted_balance_minor + possible_extra_addition @@ -159,12 +159,12 @@ pub mod ordinary_diagnostic_functions { "FULLY EXHAUSTED CW, PASSING ACCOUNT OVER", "Account {} with original balance {} must be finalized with proposed {}", non_finalized_account_info - .original_qualified_account - .payable + .qualified_payable + .qualified_as .wallet, non_finalized_account_info - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei, non_finalized_account_info.proposed_adjusted_balance_minor ); @@ -175,7 +175,7 @@ pub mod ordinary_diagnostic_functions { proposed_adjusted_balance: u128, ) { diagnostics!( - &account.payable.wallet, + &account.qualified_as.wallet, "PROPOSED ADJUSTED BALANCE", "{}", proposed_adjusted_balance.separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 61f6191ea..16f1a99b1 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -42,8 +42,8 @@ impl DisqualificationArbiter { Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); let wallet = account_to_disqualify - .original_qualified_account - .payable + .qualified_payable + .qualified_as .wallet .clone(); @@ -74,7 +74,7 @@ impl DisqualificationArbiter { qualified_payable: &QualifiedPayableAccount, ) -> u128 { self.disqualification_gauge.determine_limit( - qualified_payable.payable.balance_wei, + qualified_payable.qualified_as.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable .creditor_thresholds @@ -90,9 +90,7 @@ impl DisqualificationArbiter { .iter() .flat_map(|adjustment_info| { let disqualification_edge = self.calculate_disqualification_edge( - &adjustment_info - .non_finalized_account - .original_qualified_account, + &adjustment_info.non_finalized_account.qualified_payable, ); let proposed_adjusted_balance = adjustment_info .non_finalized_account @@ -201,7 +199,7 @@ mod tests { DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; use crate::accountant::payment_adjuster::test_utils::{ @@ -473,9 +471,7 @@ mod tests { let qualified_payables = make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let weights_total = weights_total(&weights_and_accounts); - let unconfirmed_adjustments = - subject.compute_unconfirmed_adjustments(weights_and_accounts, weights_total); + let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts); let subject = DisqualificationArbiter::default(); let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( @@ -514,7 +510,7 @@ mod tests { let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; let garbage_permanent_debt_allowed_wei = 1_111_111_111; let qualified_account = QualifiedPayableAccount { - payable: original_account, + qualified_as: original_account, payment_threshold_intercept_minor: garbage_intercept, creditor_thresholds: CreditorThresholds { permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, @@ -522,7 +518,7 @@ mod tests { }; let garbage_proposed_balance = 1_000_000_000; // Same here let new_adjustment_to_be_added = UnconfirmedAdjustment::new( - WeightedAccount::new(qualified_account, weight), + WeightedPayable::new(qualified_account, weight), garbage_proposed_balance, ); let expected_result_opt = if expected_result_opt_so_far.is_none() diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 3d63c8729..b667abd63 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -7,7 +7,7 @@ pub trait PaymentAdjusterInner { fn transaction_fee_count_limit_opt(&self) -> Option; fn original_cw_service_fee_balance_minor(&self) -> u128; fn unallocated_cw_service_fee_balance_minor(&self) -> u128; - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128); + fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128); } pub struct PaymentAdjusterInnerReal { diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 51ee0152b..85d9174f6 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -127,13 +127,13 @@ pub fn info_log_for_disqualified_account( "Shortage of MASQ in your consuming wallet impacts on payable {}, ruled out from this \ round of payments. The proposed adjustment {} wei was less than half of the recorded \ debt, {} wei", - account.original_qualified_account.payable.wallet, + account.qualified_payable.qualified_as.wallet, account .proposed_adjusted_balance_minor .separate_with_commas(), account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei .separate_with_commas() ) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 799401155..731997452 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -4,12 +4,12 @@ use crate::accountant::QualifiedPayableAccount; use web3::types::U256; #[derive(Clone)] -pub struct WeightedAccount { +pub struct WeightedPayable { pub qualified_account: QualifiedPayableAccount, pub weight: u128, } -impl WeightedAccount { +impl WeightedPayable { pub fn new(qualified_account: QualifiedPayableAccount, weight: u128) -> Self { Self { qualified_account, @@ -59,17 +59,17 @@ pub enum RequiredSpecialTreatment { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { - pub original_qualified_account: QualifiedPayableAccount, + pub qualified_payable: QualifiedPayableAccount, pub proposed_adjusted_balance_minor: u128, } impl AdjustedAccountBeforeFinalization { pub fn new( - original_account: QualifiedPayableAccount, + qualified_payable: QualifiedPayableAccount, proposed_adjusted_balance_minor: u128, ) -> Self { Self { - original_qualified_account: original_account, + qualified_payable, proposed_adjusted_balance_minor, } } @@ -82,7 +82,7 @@ pub struct UnconfirmedAdjustment { } impl UnconfirmedAdjustment { - pub fn new(weighted_account: WeightedAccount, proposed_adjusted_balance_minor: u128) -> Self { + pub fn new(weighted_account: WeightedPayable, proposed_adjusted_balance_minor: u128) -> Self { Self { non_finalized_account: AdjustedAccountBeforeFinalization::new( weighted_account.qualified_account, @@ -141,15 +141,15 @@ mod tests { #[test] fn merging_results_from_recursion_works() { let non_finalized_account_1 = AdjustedAccountBeforeFinalization { - original_qualified_account: make_non_guaranteed_qualified_payable(111), + qualified_payable: make_non_guaranteed_qualified_payable(111), proposed_adjusted_balance_minor: 1234, }; let non_finalized_account_2 = AdjustedAccountBeforeFinalization { - original_qualified_account: make_non_guaranteed_qualified_payable(222), + qualified_payable: make_non_guaranteed_qualified_payable(222), proposed_adjusted_balance_minor: 5555, }; let non_finalized_account_3 = AdjustedAccountBeforeFinalization { - original_qualified_account: make_non_guaranteed_qualified_payable(333), + qualified_payable: make_non_guaranteed_qualified_payable(333), proposed_adjusted_balance_minor: 6789, }; let subject = RecursionResults { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 5dc1261a2..882b1f9d2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -8,15 +8,12 @@ use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functi }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, - UnconfirmedAdjustment, WeightedAccount, + UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::QualifiedPayableAccount; use itertools::{Either, Itertools}; +use std::cmp::Ordering; use std::iter::successors; -use web3::types::U256; - -const MAX_EXPONENT_FOR_10_WITHIN_U128: u32 = 76; -const EMPIRIC_PRECISION_COEFFICIENT: usize = 1; pub fn zero_affordable_accounts_found( accounts: &Either, Vec>, @@ -35,16 +32,16 @@ where collection.iter().map(arranger).sum::().into() } -pub fn weights_total(weights_and_accounts: &[WeightedAccount]) -> u128 { +pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { sum_as(weights_and_accounts, |weighted_account| { weighted_account.weight }) } pub fn dump_unaffordable_accounts_by_txn_fee( - weighted_accounts_in_descending_order: Vec, + weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, -) -> Vec { +) -> Vec { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "keeping {} out of {} accounts", @@ -58,18 +55,25 @@ pub fn dump_unaffordable_accounts_by_txn_fee( } pub fn compute_mul_coefficient_preventing_fractional_numbers( - cw_masq_balance_minor: u128, - account_weights_total: u128, -) -> U256 { - let weight_digits_count = log_10(account_weights_total); - let cw_balance_digits_count = log_10(cw_masq_balance_minor); - let positive_only_difference = weight_digits_count.saturating_sub(cw_balance_digits_count); - let exponent = positive_only_difference + EMPIRIC_PRECISION_COEFFICIENT; - U256::from(10) - .checked_pow(exponent.into()) - .expect("impossible to reach given weights data type being u128") - // Bursting out of this cap is as though a fantasy. Even assuming some potential implementations - // of pure blockchains with no application-specific smart contract + cw_service_fee_balance_minor: u128, + largest_weight_in_the_set: u128, +) -> u128 { + let max_value = cw_service_fee_balance_minor.max(largest_weight_in_the_set); + u128::MAX / max_value +} + +pub fn find_largest_weight(weighted_accounts: &[WeightedPayable]) -> u128 { + let weights = weighted_accounts + .iter() + .map(|account| account.weight) + .collect::>(); + find_largest_u128(&weights) +} + +fn find_largest_u128(slice: &[u128]) -> u128 { + slice + .iter() + .fold(0, |largest_so_far, num| largest_so_far.max(*num)) } pub fn adjust_account_balance_if_outweighed( @@ -81,8 +85,8 @@ pub fn adjust_account_balance_if_outweighed( .proposed_adjusted_balance_minor > current_adjustment_info .non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei { possibly_outweighed_accounts_diagnostics(¤t_adjustment_info.non_finalized_account); @@ -91,8 +95,8 @@ pub fn adjust_account_balance_if_outweighed( .non_finalized_account .proposed_adjusted_balance_minor = current_adjustment_info .non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei; outweighed.push(current_adjustment_info); @@ -145,8 +149,8 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( ) -> ConsumingWalletExhaustingStatus { if status.remainder != 0 { let balance_gap_minor = non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei .checked_sub(non_finalized_account.proposed_adjusted_balance_minor) .unwrap_or_else(|| { @@ -155,8 +159,8 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( {}, original: {}", non_finalized_account.proposed_adjusted_balance_minor, non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .balance_wei ) }); @@ -216,28 +220,22 @@ impl ConsumingWalletExhaustingStatus { } pub fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, -) -> Vec { + unsorted: impl Iterator, +) -> Vec { unsorted .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) .collect() } pub fn drop_no_longer_needed_weights_away_from_accounts( - weights_and_accounts: Vec, + weights_and_accounts: Vec, ) -> Vec { weights_and_accounts .into_iter() - .map(|weighted_account| weighted_account.qualified_account.payable) + .map(|weighted_account| weighted_account.qualified_account.qualified_as) .collect() } -// Replace with std lib method log10() for u128 which will be introduced by Rust 1.67.0; this was -// written using 1.63.0 -pub fn log_10(num: u128) -> usize { - successors(Some(num), |&n| (n >= 10).then(|| n / 10)).count() -} - pub fn nonzero_positive(x: u128) -> u128 { if x == 0 { 1 @@ -248,13 +246,13 @@ pub fn nonzero_positive(x: u128) -> u128 { impl From for PayableAccount { fn from(qualified_payable: QualifiedPayableAccount) -> Self { - qualified_payable.payable + qualified_payable.qualified_as } } impl From for QualifiedPayableAccount { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).original_qualified_account + AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).qualified_payable } } @@ -264,6 +262,17 @@ impl From for AdjustedAccountBeforeFinalization { } } +impl From for AdjustedAccountBeforeFinalization { + fn from(weighted_account: WeightedPayable) -> Self { + let proposed_adjusted_balance_minor = + weighted_account.qualified_account.qualified_as.balance_wei; + AdjustedAccountBeforeFinalization { + qualified_payable: weighted_account.qualified_account, + proposed_adjusted_balance_minor, + } + } +} + impl From for PayableAccount { fn from(resolution_info: NonFinalizedAdjustmentWithResolution) -> Self { match resolution_info.adjustment_resolution { @@ -273,14 +282,14 @@ impl From for PayableAccount { .proposed_adjusted_balance_minor, ..resolution_info .non_finalized_adjustment - .original_qualified_account - .payable + .qualified_payable + .qualified_as }, AdjustmentResolution::Revert => { resolution_info .non_finalized_adjustment - .original_qualified_account - .payable + .qualified_payable + .qualified_as } } } @@ -290,36 +299,21 @@ impl From for PayableAccount { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedAccount, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, - log_10, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, - EMPIRIC_PRECISION_COEFFICIENT, MAX_EXPONENT_FOR_10_WITHIN_U128, - }; - use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, - PRESERVED_TEST_PAYMENT_THRESHOLDS, + find_largest_u128, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; use crate::accountant::test_utils::{ - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - make_payable_account, + make_non_guaranteed_qualified_payable, make_payable_account, }; use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use masq_lib::logger::Logger; - use masq_lib::utils::convert_collection; - use std::time::{Duration, SystemTime}; - use web3::types::U256; - - #[test] - fn constants_are_correct() { - assert_eq!(MAX_EXPONENT_FOR_10_WITHIN_U128, 76); - assert_eq!(EMPIRIC_PRECISION_COEFFICIENT, 8); - } + use std::time::SystemTime; #[test] fn found_zero_affordable_accounts_found_returns_true_for_non_finalized_accounts() { @@ -356,183 +350,59 @@ mod tests { } #[test] - fn log_10_works() { - [ - (4_565_u128, 4), - (1_666_777, 7), - (3, 1), - (123, 3), - (111_111_111_111_111_111, 18), - ] - .into_iter() - .for_each(|(num, expected_result)| assert_eq!(log_10(num), expected_result)) + fn find_largest_u128_begins_with_zero() { + let result = find_largest_u128(&[]); + + assert_eq!(result, 0) } #[test] - fn multiplication_coefficient_can_give_numbers_preventing_fractional_numbers() { - let final_weight = 5_000_000_000_000_u128; - let cw_balances = vec![ - 222_222_222_222_u128, - 100_000, - 123_456_789, - 5_555_000_000_000, - 5_000_555_000_000_000, - 1_000_000_000_000_000_000, // 1 MASQ - ]; + fn find_largest_u128_works() { + let result = find_largest_u128(&[45, 2, 456565, 0, 2, 456565, 456564]); - let result = cw_balances - .clone() - .into_iter() - .map(|cw_balance| { - compute_mul_coefficient_preventing_fractional_numbers(cw_balance, final_weight) - }) - .collect::>(); - - let expected_result: Vec = convert_collection(vec![ - 1_000_000_000_u128, - 1_000_000_000_000_000, - 1_000_000_000_000, - // The following values are the minimum. It turned out that it helps to reach better - // precision in the downstream computations - 100_000_000, - 100_000_000, - 100_000_000, - ]); - assert_eq!(result, expected_result) + assert_eq!(result, 456565) } #[test] - fn multiplication_coefficient_extreme_feeding_with_possible_but_only_little_realistic_values() { - // We cannot say by heart which of the evaluated weights from these parameters below will - // be bigger than another, and therefore we cannot line them up in an order - todo!("the comment is lie now"); - let accounts_as_months_and_balances = vec![ - (1, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), - (5, 10_u128.pow(18)), - (12, 10_u128.pow(18)), - (120, 10_u128.pow(20)), - (600, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR), - (1200, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR * 1000), - ]; - let (accounts_with_their_weights, reserved_initial_accounts_order_according_to_wallets) = - get_extreme_weights_and_initial_accounts_order(accounts_as_months_and_balances); - let cw_balance_in_minor = 1; // Minimal possible balance 1 wei - - let results = accounts_with_their_weights - .into_iter() - .map(|weighted_account| { - // Scenario simplification: we assume there is always just one account to process in a time - let computed_coefficient = compute_mul_coefficient_preventing_fractional_numbers( - cw_balance_in_minor, - weighted_account.weight, - ); - ( - computed_coefficient, - weighted_account.qualified_account.payable.wallet, - weighted_account.weight, - ) - }) - .collect::>(); + fn multiplication_coefficient_is_based_on_cw_balance_if_largest_then_the_largest_weight() { + let cw_service_fee_balance_minor = 12345678; + let largest_weight = 12345677; - let reserved_initial_accounts_order_according_to_wallets_iter = - reserved_initial_accounts_order_according_to_wallets - .iter() - .enumerate(); - let mul_coefficients_and_weights_in_the_same_order_as_original_inputs = results - .into_iter() - .map(|(computed_coefficient, account_wallet, account_weight)| { - let (idx, _) = reserved_initial_accounts_order_according_to_wallets_iter - .clone() - .find(|(_, wallet_ordered)| wallet_ordered == &&account_wallet) - .unwrap(); - (idx, computed_coefficient, account_weight) - }) - .sorted_by(|(idx_a, _, _), (idx_b, _, _)| Ord::cmp(&idx_b, &idx_a)) - .map(|(_, coefficient, weight)| (coefficient, weight)) - .collect::>(); - let templates_for_coefficients: Vec = convert_collection(vec![ - 100000000000000000000000000000000000000_u128, - 100000000000000000000000000000000000, - 100000000000000000000000000000000000, - 100000000000000000000000000000000, - 10000000000000000000000000000000, - 10000000000000000000000000000000, - 100000000000000000000000000000000000, - ]); - // I was trying to write these assertions so that it wouldn't require us to rewrite - // the expected values everytime someone pokes into the formulas. - check_relation_to_computed_weight_fairly_but_with_enough_benevolence( - &mul_coefficients_and_weights_in_the_same_order_as_original_inputs, - ); - compare_coefficients_to_templates( - &mul_coefficients_and_weights_in_the_same_order_as_original_inputs, - &templates_for_coefficients, + let result = compute_mul_coefficient_preventing_fractional_numbers( + cw_service_fee_balance_minor, + largest_weight, ); - } - fn get_extreme_weights_and_initial_accounts_order( - months_of_debt_and_balances: Vec<(usize, u128)>, - ) -> (Vec, Vec) { - let now = SystemTime::now(); - let accounts = make_extreme_payables(Either::Right(months_of_debt_and_balances), now); - let wallets_in_order = accounts - .iter() - .map(|account| account.wallet.clone()) - .collect(); - let qualified_accounts = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let subject = make_initialized_subject(now, None, None); - // The initial order is remembered because when the weight are applied the collection - // also gets sorted and will not necessarily have to match the initial order - let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_accounts); - (weights_and_accounts, wallets_in_order) + let expected_result = u128::MAX / cw_service_fee_balance_minor; + assert_eq!(result, expected_result) } - fn check_relation_to_computed_weight_fairly_but_with_enough_benevolence( - output: &[(U256, u128)], - ) { - output.iter().for_each(|(coefficient, corresponding_weight)| { - let coefficient_num_decimal_length = log_10(coefficient.as_u128()); - let weight_decimal_length = log_10(*corresponding_weight); - assert_eq!(coefficient_num_decimal_length, weight_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, - "coefficient with bad safety margin; should be {} but was {}, as one of this set {:?}", - coefficient_num_decimal_length, - weight_decimal_length + EMPIRIC_PRECISION_COEFFICIENT, - output - ); - - let expected_division_by_10_if_wrong = 10_u128.pow(coefficient_num_decimal_length as u32 - 1); - let experiment_result = corresponding_weight / 10; - match experiment_result == expected_division_by_10_if_wrong { - false => (), - true => match corresponding_weight % 10 { - 0 => panic!("the weight is a pure power of ten, such a suspicious result, \ - check it in {:?}", output), - _ => () - } - } - }) + #[test] + fn multiplication_coefficient_is_based_on_the_largest_weight_if_larger_then_cw_balance() { + let cw_service_fee_balance_minor = 12345677; + let largest_weight = 12345678; + + let result = compute_mul_coefficient_preventing_fractional_numbers( + cw_service_fee_balance_minor, + largest_weight, + ); + + let expected_result = u128::MAX / largest_weight; + assert_eq!(result, expected_result) } - fn compare_coefficients_to_templates(outputs: &[(U256, u128)], templates: &[U256]) { - assert_eq!( - outputs.len(), - templates.len(), - "count of actual values {:?} and templates don't match {:?}", - outputs, - templates + #[test] + fn multiplication_coefficient_computed_when_both_parameters_the_same() { + let cw_service_fee_balance_minor = 111111; + let largest_weight = 111111; + + let result = compute_mul_coefficient_preventing_fractional_numbers( + cw_service_fee_balance_minor, + largest_weight, ); - outputs - .iter() - .zip(templates.iter()) - .for_each(|((actual_coeff, _), expected_coeff)| { - assert_eq!( - actual_coeff, expected_coeff, - "actual coefficient {} does not match the expected one {} in the full set {:?}", - actual_coeff, expected_coeff, outputs - ) - }) + + let expected_result = u128::MAX / 111111; + assert_eq!(result, expected_result) } #[test] @@ -555,7 +425,7 @@ mod tests { let garbage_weight = 123456; let garbage_proposed_adjusted_balance_minor = 9_000_000_000; let unconfirmed_adjustment = UnconfirmedAdjustment::new( - WeightedAccount::new(qualified_payable, garbage_weight), + WeightedPayable::new(qualified_payable, garbage_weight), garbage_proposed_adjusted_balance_minor, ); let init = (vec![], vec![]); @@ -576,7 +446,7 @@ mod tests { let garbage_payment_threshold_intercept_minor = u128::MAX; let garbage_permanent_debt_allowed_wei = 123456789; let qualified_payable = QualifiedPayableAccount { - payable: PayableAccount { + qualified_as: PayableAccount { wallet: wallet.clone(), balance_wei: original_balance, last_paid_timestamp: garbage_last_paid_timestamp, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bb3df141d..cd605f3c5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -29,8 +29,8 @@ use crate::accountant::payment_adjuster::log_fns::{ use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::{ TreatInsignificantAccount, TreatOutweighedAccounts, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedAccount}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, adjust_account_balance_if_outweighed, drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, adjust_account_balance_if_outweighed, drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found, find_largest_weight}; use crate::accountant::payment_adjuster::preparatory_analyser::{PreparatoryAnalyzer}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; @@ -218,30 +218,25 @@ impl PaymentAdjusterReal { "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", &unresolved_qualified_accounts ); - eprintln!( - "Unallocated balance for this iteration: {}", - self.inner - .unallocated_cw_service_fee_balance_minor() - .separate_with_commas() - ); - if unresolved_qualified_accounts.len() == 1 { - let last_one = unresolved_qualified_accounts - .into_iter() - .next() - .expect("previous if stmt must be wrong"); - return adjustment_runner.adjust_last_one(self, last_one); - } + //TODO remove me + // if unresolved_qualified_accounts.len() == 1 { + // let last_one = unresolved_qualified_accounts + // .into_iter() + // .next() + // .expect("previous if stmt must be wrong"); + // return adjustment_runner.adjust_last_one(self, last_one); + // } let weights_and_accounts_sorted = self.calculate_weights_for_accounts(unresolved_qualified_accounts); - adjustment_runner.adjust_multiple(self, weights_and_accounts_sorted) + adjustment_runner.adjust_accounts(self, weights_and_accounts_sorted) } fn begin_with_adjustment_by_transaction_fee( &mut self, - weighted_accounts_in_descending_order: Vec, + weighted_accounts_in_descending_order: Vec, already_known_affordable_transaction_count: u16, ) -> Result< Either, Vec>, @@ -290,7 +285,7 @@ impl PaymentAdjusterReal { fn propose_possible_adjustment_recursively( &mut self, - weighed_accounts: Vec, + weighed_accounts: Vec, ) -> Vec { let current_iteration_result = self.perform_adjustment_by_service_fee(weighed_accounts); @@ -321,6 +316,8 @@ impl PaymentAdjusterReal { let here_decided_accounts = match special_case { TreatInsignificantAccount => { if remaining_undecided_accounts.is_empty() { + todo!("you should allow this... -> All accounts eliminated"); + // a) only one account can be eliminated in a single iteration, // b) if there is one last undecided account, it goes on through a shortcut, not reaching // out down here @@ -331,6 +328,8 @@ impl PaymentAdjusterReal { } TreatOutweighedAccounts(outweighed) => { if remaining_undecided_accounts.is_empty() { + todo!("this should be made impossible, in turn... by a short circuit within the adjustment runner"); + debug!(self.logger, "Every account outweighed (Probably excessive funds after preceding \ disqualification). Returning from recursion"); @@ -356,7 +355,7 @@ impl PaymentAdjusterReal { fn calculate_weights_for_accounts( &self, accounts: Vec, - ) -> Vec { + ) -> Vec { self.apply_criteria(self.calculators.as_slice(), accounts) } @@ -364,7 +363,7 @@ impl PaymentAdjusterReal { &self, criteria_calculators: &[Box], qualified_accounts: Vec, - ) -> Vec { + ) -> Vec { let weighted_accounts = qualified_accounts.into_iter().map(|payable| { let weight = criteria_calculators @@ -376,7 +375,7 @@ impl PaymentAdjusterReal { let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( - &payable.payable.wallet, + &payable.qualified_as.wallet, criterion_calculator.as_ref(), new_criterion, summed_up, @@ -385,7 +384,7 @@ impl PaymentAdjusterReal { summed_up }); - WeightedAccount::new(payable, weight) + WeightedPayable::new(payable, weight) }); sort_in_descendant_order_by_weights(weighted_accounts) @@ -393,11 +392,10 @@ impl PaymentAdjusterReal { fn perform_adjustment_by_service_fee( &self, - weighted_accounts: Vec, + weighted_accounts: Vec, ) -> AdjustmentIterationResult { - let weights_total = weights_total(&weighted_accounts); let non_finalized_adjusted_accounts = - self.compute_unconfirmed_adjustments(weighted_accounts, weights_total); + self.compute_unconfirmed_adjustments(weighted_accounts); let still_unchecked_for_disqualified = match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { @@ -417,14 +415,15 @@ impl PaymentAdjusterReal { fn compute_unconfirmed_adjustments( &self, - weighted_accounts: Vec, - weights_total: u128, + weighted_accounts: Vec, ) -> Vec { + let weights_total = weights_total(&weighted_accounts); + let largest_weight = find_largest_weight(&weighted_accounts); let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( cw_service_fee_balance, - weights_total, + largest_weight, ); let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( @@ -433,12 +432,8 @@ impl PaymentAdjusterReal { multiplication_coefficient, ); - let compute_proposed_adjusted_balance = |weight: u128| { - u128::try_from( - U256::from(weight) * proportional_cw_balance_fragment / multiplication_coefficient, - ) - .expect("mul coefficient computation worked, this must too") - }; + let compute_proposed_adjusted_balance = + |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; weighted_accounts .into_iter() @@ -459,12 +454,10 @@ impl PaymentAdjusterReal { fn compute_proportional_cw_fragment( cw_service_fee_balance: u128, weights_total: u128, - multiplication_coefficient: U256, - ) -> U256 { - let cw_service_fee_balance = U256::from(cw_service_fee_balance); - let weights_total = U256::from(weights_total); - + multiplication_coefficient: u128, + ) -> u128 { cw_service_fee_balance + // Considered safe due to the process of getting this coefficient .checked_mul(multiplication_coefficient) .unwrap_or_else(|| { panic!( @@ -491,8 +484,8 @@ impl PaymentAdjusterReal { let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { account_info .non_finalized_account - .original_qualified_account - .payable + .qualified_payable + .qualified_as .wallet != disqualified_account_wallet }); @@ -500,9 +493,7 @@ impl PaymentAdjusterReal { let remaining_reverted = remaining .map(|account_info| { //TODO maybe implement from like before - account_info - .non_finalized_account - .original_qualified_account + account_info.non_finalized_account.qualified_payable // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( // account_info.non_finalized_account, // AdjustmentResolution::Revert, @@ -571,7 +562,12 @@ impl PaymentAdjusterReal { self.logger.debug_enabled().then(|| { qualified_payables .iter() - .map(|payable| (payable.payable.wallet.clone(), payable.payable.balance_wei)) + .map(|payable| { + ( + payable.qualified_as.wallet.clone(), + payable.qualified_as.balance_wei, + ) + }) .collect::>() }) } @@ -593,21 +589,25 @@ impl PaymentAdjusterReal { last_payable: QualifiedPayableAccount, ) -> Option { let cw_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let proposed_adjusted_balance = - if last_payable.payable.balance_wei.checked_sub(cw_balance) == None { - last_payable.payable.balance_wei - } else { - diagnostics!( - "LAST REMAINING ACCOUNT", - "Balance adjusted to {} by exhausting the cw balance fully", - cw_balance - ); - + let proposed_adjusted_balance = if last_payable + .qualified_as + .balance_wei + .checked_sub(cw_balance) + == None + { + last_payable.qualified_as.balance_wei + } else { + diagnostics!( + "LAST REMAINING ACCOUNT", + "Balance adjusted to {} by exhausting the cw balance fully", cw_balance - }; + ); + + cw_balance + }; // TODO the Disqualification check really makes sense only if we assigned less than the full balance!! let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - WeightedAccount::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up + WeightedPayable::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up proposed_adjusted_balance, )]; @@ -690,10 +690,8 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total}; use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; - use crate::accountant::payment_adjuster::{ - Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, - }; - use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; + use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal}; + use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -726,10 +724,10 @@ mod tests { let test_name = "search_for_indispensable_adjustment_gives_negative_answer"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - // service fee balance > payments - let input_1 = make_test_input_for_initial_check( + // Service fee balance > payments + let input_1 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Right(vec![ + account_balances: Either::Right(vec![ gwei_to_wei::(85), gwei_to_wei::(15) - 1, ]), @@ -737,31 +735,31 @@ mod tests { }), None, ); - // service fee balance == payments - let input_2 = make_test_input_for_initial_check( + // Service fee balance == payments + let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Left(vec![85, 15]), + account_balances: Either::Left(vec![85, 15]), cw_balance_minor: gwei_to_wei(100_u64), }), None, ); // transaction fee balance > payments - let input_3 = make_test_input_for_initial_check( + let input_3 = make_input_for_initial_check_tests( None, - Some(TestConfigForTransactionFee { + Some(TestConfigForTransactionFees { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, - estimated_transaction_fee_units_limit_per_transaction: 53_000, + estimated_transaction_fee_units_per_transaction: 53_000, cw_transaction_fee_balance_major: (100 * 6 * 53_000) + 1, }), ); // transaction fee balance == payments - let input_4 = make_test_input_for_initial_check( + let input_4 = make_input_for_initial_check_tests( None, - Some(TestConfigForTransactionFee { + Some(TestConfigForTransactionFees { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, - estimated_transaction_fee_units_limit_per_transaction: 53_000, + estimated_transaction_fee_units_per_transaction: 53_000, cw_transaction_fee_balance_major: 100 * 6 * 53_000, }), ); @@ -789,12 +787,12 @@ mod tests { subject.logger = Logger::new(test_name); let number_of_accounts = 3; let service_fee_balances_config_opt = None; - let (qualified_payables, agent) = make_test_input_for_initial_check( + let (qualified_payables, agent) = make_input_for_initial_check_tests( service_fee_balances_config_opt, - Some(TestConfigForTransactionFee { + Some(TestConfigForTransactionFees { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, + estimated_transaction_fee_units_per_transaction: 55_000, cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, }), ); @@ -827,9 +825,9 @@ mod tests { let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; - let (qualified_payables, agent) = make_test_input_for_initial_check( + let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Right(vec![ + account_balances: Either::Right(vec![ gwei_to_wei::(85), gwei_to_wei::(15) + 1, ]), @@ -858,11 +856,11 @@ mod tests { let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low"; let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; // this would normally kick a serious error let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Left(vec![120, 300, 500]), + account_balances: Either::Left(vec![120, 300, 500]), cw_balance_minor: cw_service_fee_balance_minor, }); let (qualified_payables, agent) = - make_test_input_for_initial_check(service_fee_balances_config_opt, None); + make_input_for_initial_check_tests(service_fee_balances_config_opt, None); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); @@ -884,15 +882,15 @@ mod tests { fn not_enough_transaction_fee_balance_for_even_a_single_transaction() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; - let (qualified_payables, agent) = make_test_input_for_initial_check( + let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Left(vec![123]), - cw_balance_minor: 444, + account_balances: Either::Left(vec![123]), + cw_balance_minor: gwei_to_wei::(444), }), - Some(TestConfigForTransactionFee { + Some(TestConfigForTransactionFees { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, - estimated_transaction_fee_units_limit_per_transaction: 55_000, + estimated_transaction_fee_units_per_transaction: 55_000, cw_transaction_fee_balance_major: 54_000 * 100, }), ); @@ -987,11 +985,12 @@ mod tests { weighted_account.weight ); previous_weight = weighted_account.weight; - weighted_account.qualified_account.payable + weighted_account.qualified_account.qualified_as }) .collect::>(); assert_eq!(accounts_alone, vec![account_3, account_1, account_2]) } + #[test] fn minor_but_a_lot_aged_debt_is_prioritized_outweighed_and_stays_as_the_full_original_balance() { @@ -1031,9 +1030,8 @@ mod tests { // First, let's have an example of why this test is important let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables.clone()); - let weights_total = weights_total(&criteria_and_accounts); let unconfirmed_adjustments = - subject.compute_unconfirmed_adjustments(criteria_and_accounts, weights_total); + subject.compute_unconfirmed_adjustments(criteria_and_accounts); let proposed_adjusted_balance_2 = unconfirmed_adjustments[1] .non_finalized_account .proposed_adjusted_balance_minor; @@ -1051,7 +1049,7 @@ mod tests { let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!( - &first_returned_account.original_qualified_account, + &first_returned_account.qualified_payable, &qualified_payables[1] ); assert_eq!( @@ -1060,7 +1058,7 @@ mod tests { ); let second_returned_account = result.remove(0); assert_eq!( - &second_returned_account.original_qualified_account, + &second_returned_account.qualified_payable, &qualified_payables[0] ); let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; @@ -1304,7 +1302,7 @@ mod tests { assert_eq!( result, Some(AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable.clone(), + qualified_payable: qualified_payable.clone(), proposed_adjusted_balance_minor: cw_balance, }) ); @@ -1312,7 +1310,7 @@ mod tests { assert_eq!( *determine_limit_params, vec![( - qualified_payable.payable.balance_wei, + qualified_payable.qualified_as.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable .creditor_thresholds @@ -1337,7 +1335,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payable = QualifiedPayableAccount { - payable, + qualified_as: payable, payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, creditor_thresholds: CreditorThresholds { permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, @@ -1512,15 +1510,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, - ..qualified_account_1.payable + ..qualified_account_1.qualified_as }; let account_2_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_2, - ..qualified_account_2.payable + ..qualified_account_2.qualified_as }; let account_3_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_3, - ..qualified_account_3.payable + ..qualified_account_3.qualified_as }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] }; @@ -1614,7 +1612,7 @@ mod tests { // (it weights more if the balance is so small) assert_eq!( result.affordable_accounts, - vec![account_3.payable, account_2.payable] + vec![account_3.qualified_as, account_2.qualified_as] ); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); @@ -1704,9 +1702,9 @@ mod tests { let expected_accounts = { let account_2_adjusted = PayableAccount { balance_wei: 222_000_000_000_000, - ..account_2.payable + ..account_2.qualified_as }; - vec![account_3.payable, account_2_adjusted] + vec![account_3.qualified_as, account_2_adjusted] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1783,9 +1781,9 @@ mod tests { let expected_accounts = { let account_1_adjusted = PayableAccount { balance_wei: 272_000_000_000, - ..account_1.payable + ..account_1.qualified_as }; - vec![account_1_adjusted, account_2.payable] + vec![account_1_adjusted, account_2.qualified_as] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -2127,88 +2125,136 @@ mod tests { struct TestConfigForServiceFeeBalances { // Either gwei or wei - balances_of_accounts: Either, Vec>, + account_balances: Either, Vec>, cw_balance_minor: u128, } - struct TestConfigForTransactionFee { + struct TestConfigForTransactionFees { agreed_transaction_fee_per_computed_unit_major: u64, number_of_accounts: usize, - estimated_transaction_fee_units_limit_per_transaction: u64, + estimated_transaction_fee_units_per_transaction: u64, cw_transaction_fee_balance_major: u64, } - fn make_test_input_for_initial_check( + fn make_input_for_initial_check_tests( service_fee_balances_config_opt: Option, - transaction_fee_config_opt: Option, + transaction_fee_config_opt: Option, ) -> (Vec, Box) { - let service_fee_balances_setup = match service_fee_balances_config_opt { - Some(config) => config, - None => TestConfigForServiceFeeBalances { - balances_of_accounts: Either::Left(vec![1, 1]), - cw_balance_minor: u64::MAX as u128, - }, - }; + let service_fee_balances_config = + get_service_fee_balances_config(service_fee_balances_config_opt); + let balances_of_accounts_minor = + get_service_fee_balances(service_fee_balances_config.account_balances); + let accounts_count_from_sf_config = balances_of_accounts_minor.len(); + + let transaction_fee_config = + get_transaction_fee_config(transaction_fee_config_opt, accounts_count_from_sf_config); + + let payable_accounts = prepare_payable_accounts( + transaction_fee_config.number_of_accounts, + accounts_count_from_sf_config, + balances_of_accounts_minor, + ); + let qualified_payables = prepare_qualified_payables(payable_accounts); + + let blockchain_agent = make_agent( + transaction_fee_config.cw_transaction_fee_balance_major, + transaction_fee_config.estimated_transaction_fee_units_per_transaction, + transaction_fee_config.agreed_transaction_fee_per_computed_unit_major, + service_fee_balances_config.cw_balance_minor, + ); + + (qualified_payables, blockchain_agent) + } - let balances_of_accounts_minor = match service_fee_balances_setup.balances_of_accounts { + fn get_service_fee_balances_config( + service_fee_balances_config_opt: Option, + ) -> TestConfigForServiceFeeBalances { + service_fee_balances_config_opt.unwrap_or_else(|| TestConfigForServiceFeeBalances { + account_balances: Either::Left(vec![1, 1]), + cw_balance_minor: u64::MAX as u128, + }) + } + fn get_service_fee_balances(account_balances: Either, Vec>) -> Vec { + match account_balances { Either::Left(in_major) => in_major .into_iter() .map(|major| gwei_to_wei(major)) .collect(), Either::Right(in_minor) => in_minor, - }; + } + } - let accounts_count_from_sf_config = balances_of_accounts_minor.len(); + fn get_transaction_fee_config( + transaction_fee_config_opt: Option, + accounts_count_from_sf_config: usize, + ) -> TestConfigForTransactionFees { + transaction_fee_config_opt.unwrap_or(TestConfigForTransactionFees { + agreed_transaction_fee_per_computed_unit_major: 120, + number_of_accounts: accounts_count_from_sf_config, + estimated_transaction_fee_units_per_transaction: 55_000, + cw_transaction_fee_balance_major: u64::MAX, + }) + } - let ( - agreed_transaction_fee_price, - accounts_count_from_tf_config, - estimated_limit_for_transaction_fee_units_per_transaction, - cw_balance_transaction_fee_major, - ) = match transaction_fee_config_opt { - Some(conditions) => ( - conditions.agreed_transaction_fee_per_computed_unit_major, - conditions.number_of_accounts, - conditions.estimated_transaction_fee_units_limit_per_transaction, - conditions.cw_transaction_fee_balance_major, - ), - None => (120, accounts_count_from_sf_config, 55_000, u64::MAX), - }; + fn prepare_payable_accounts( + accounts_count_from_tf_config: usize, + accounts_count_from_sf_config: usize, + balances_of_accounts_minor: Vec, + ) -> Vec { + if accounts_count_from_tf_config != accounts_count_from_sf_config { + (0..accounts_count_from_tf_config) + .map(|idx| make_payable_account(idx as u64)) + .collect() + } else { + balances_of_accounts_minor + .into_iter() + .enumerate() + .map(|(idx, balance)| { + let mut account = make_payable_account(idx as u64); + account.balance_wei = balance; + account + }) + .collect() + } + } - let payable_accounts: Vec<_> = - if accounts_count_from_tf_config != accounts_count_from_sf_config { - (0..accounts_count_from_tf_config) - .map(|idx| make_payable_account(idx as u64)) - .collect() - } else { - balances_of_accounts_minor - .into_iter() - .enumerate() - .map(|(idx, balance)| { - let mut account = make_payable_account(idx as u64); - account.balance_wei = balance; - account - }) - .collect() - }; - let now = SystemTime::now(); - let qualified_payables = make_guaranteed_qualified_payables( - payable_accounts, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - ); + fn prepare_qualified_payables( + payable_accounts: Vec, + ) -> Vec { + payable_accounts + .into_iter() + .enumerate() + .map(|(idx, payable)| { + let balance = payable.balance_wei; + QualifiedPayableAccount { + qualified_as: payable, + payment_threshold_intercept_minor: (balance / 10) * 7, + creditor_thresholds: CreditorThresholds { + permanent_debt_allowed_wei: (balance / 10) * 7, + }, + } + }) + .collect() + } + + fn make_agent( + cw_balance_transaction_fee_major: u64, + estimated_transaction_fee_units_per_transaction: u64, + agreed_transaction_fee_price: u64, + cw_service_fee_balance_minor: u128, + ) -> Box { let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( - estimated_limit_for_transaction_fee_units_per_transaction - * agreed_transaction_fee_price, + estimated_transaction_fee_units_per_transaction * agreed_transaction_fee_price, ); + let blockchain_agent = BlockchainAgentMock::default() .transaction_fee_balance_minor_result(cw_transaction_fee_minor) - .service_fee_balance_minor_result(service_fee_balances_setup.cw_balance_minor) + .service_fee_balance_minor_result(cw_service_fee_balance_minor) .estimated_transaction_fee_per_transaction_minor_result( estimated_transaction_fee_per_transaction_minor, ); - (qualified_payables, Box::new(blockchain_agent)) + Box::new(blockchain_agent) } } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 46e0cbafc..42316c437 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -5,7 +5,7 @@ use crate::accountant::payment_adjuster::log_fns::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - TransactionCountsWithin16bits, WeightedAccount, + TransactionCountsWithin16bits, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::PaymentAdjusterError; @@ -15,6 +15,7 @@ use ethereum_types::U256; use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use std::cmp::Ordering; +use std::ops::Mul; pub struct PreparatoryAnalyzer {} @@ -85,14 +86,14 @@ impl PreparatoryAnalyzer { pub fn check_need_of_adjustment_by_service_fee( &self, disqualification_arbiter: &DisqualificationArbiter, - payables: Either<&[QualifiedPayableAccount], &[WeightedAccount]>, + payables: Either<&[QualifiedPayableAccount], &[WeightedPayable]>, cw_service_fee_balance_minor: u128, logger: &Logger, ) -> Result { let qualified_payables = Self::comb_qualified_payables(payables); let required_service_fee_sum: u128 = - sum_as(&qualified_payables, |qa| qa.payable.balance_wei); + sum_as(&qualified_payables, |qa| qa.qualified_as.balance_wei); if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) @@ -113,7 +114,7 @@ impl PreparatoryAnalyzer { } fn comb_qualified_payables<'payables>( - payables: Either<&'payables [QualifiedPayableAccount], &'payables [WeightedAccount]>, + payables: Either<&'payables [QualifiedPayableAccount], &'payables [WeightedPayable]>, ) -> Vec<&'payables QualifiedPayableAccount> { match payables { Either::Left(accounts) => accounts.iter().collect(), @@ -141,7 +142,7 @@ impl PreparatoryAnalyzer { Ok(()) } else { let total_amount_demanded_minor = - sum_as(qualified_payables, |qp| qp.payable.balance_wei); + sum_as(qualified_payables, |qp| qp.qualified_as.balance_wei); Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: qualified_payables.len(), @@ -159,13 +160,7 @@ impl PreparatoryAnalyzer { qualified_payables .iter() .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) - .fold(u128::MAX, |lowest_so_far, limit| { - match Ord::cmp(&lowest_so_far, &limit) { - Ordering::Less => lowest_so_far, - Ordering::Equal => lowest_so_far, - Ordering::Greater => limit, - } - }) + .fold(u128::MAX, |lowest_so_far, limit| lowest_so_far.min(limit)) } } @@ -233,12 +228,12 @@ mod tests { *determine_limit_params, vec![ ( - account_1.payable.balance_wei, + account_1.qualified_as.balance_wei, account_1.payment_threshold_intercept_minor, account_1.creditor_thresholds.permanent_debt_allowed_wei ), ( - account_2.payable.balance_wei, + account_2.qualified_as.balance_wei, account_2.payment_threshold_intercept_minor, account_2.creditor_thresholds.permanent_debt_allowed_wei ) @@ -249,9 +244,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.payable.balance_wei = 1_000_000_000; + account_1.qualified_as.balance_wei = 1_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.payable.balance_wei = 2_000_000_000; + account_2.qualified_as.balance_wei = 2_000_000_000; let cw_service_fee_balance = 750_000_001; let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(750_000_000) @@ -268,9 +263,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.payable.balance_wei = 2_000_000_000; + account_1.qualified_as.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.payable.balance_wei = 1_000_000_000; + account_2.qualified_as.balance_wei = 1_000_000_000; let cw_service_fee_balance = 750_000_000; let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(1_500_000_000) @@ -287,11 +282,11 @@ mod tests { #[test] fn adjustment_possibility_err_from_insufficient_balance_for_even_the_least_demanding_account() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.payable.balance_wei = 2_000_000_000; + account_1.qualified_as.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(222); - account_2.payable.balance_wei = 1_000_050_000; + account_2.qualified_as.balance_wei = 1_000_050_000; let mut account_3 = make_non_guaranteed_qualified_payable(333); - account_3.payable.balance_wei = 1_000_111_111; + account_3.qualified_as.balance_wei = 1_000_111_111; let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 69276ac59..ebba9df85 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -3,15 +3,12 @@ #![cfg(test)] use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, }; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::log_10; -use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use crate::accountant::QualifiedPayableAccount; @@ -21,11 +18,9 @@ use itertools::Either; use lazy_static::lazy_static; use masq_lib::constants::MASQ_TOTAL_SUPPLY; use masq_lib::logger::Logger; -use rusqlite::params; use std::cell::RefCell; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; -use websocket::header::q; lazy_static! { pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = @@ -119,10 +114,10 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_payable = make_non_guaranteed_qualified_payable(n); let proposed_adjusted_balance_minor = - (qualified_payable.payable.balance_wei / 2) + log_10(n as u128) as u128; + (qualified_payable.qualified_as.balance_wei / 2) * (n as f64).sqrt() as u128; UnconfirmedAdjustment { non_finalized_account: AdjustedAccountBeforeFinalization { - original_qualified_account: qualified_payable, + qualified_payable, proposed_adjusted_balance_minor, }, weight: (n as u128).pow(3), diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 94283c3eb..f74d71777 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -2194,7 +2194,7 @@ mod tests { .payable_exceeded_threshold(&payable, SystemTime::now()) .unwrap(); assert_eq!(result.len(), 1); - assert_eq!(&result[0].payable.wallet, &wallet); + assert_eq!(&result[0].qualified_as.wallet, &wallet); assert!(intercept_before >= result[0].payment_threshold_intercept_minor && result[0].payment_threshold_intercept_minor >= intercept_after, "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept_minor, intercept_before, intercept_after ) diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 76e506f16..92a0f7f45 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -177,7 +177,7 @@ pub mod payable_scanner_utils { qualified_accounts .iter() .map(|qualified_account| { - let account = &qualified_account.payable; + let account = &qualified_account.qualified_as; let p_age = now .duration_since(account.last_paid_timestamp) .expect("Payable time is corrupt"); @@ -651,7 +651,7 @@ mod tests { let now = to_time_t(SystemTime::now()); let qualified_payables_and_threshold_points = vec![ QualifiedPayableAccount { - payable: PayableAccount { + qualified_as: PayableAccount { wallet: make_wallet("wallet0"), balance_wei: gwei_to_wei(10_002_000_u64), last_paid_timestamp: from_time_t(now - 2678400), @@ -663,7 +663,7 @@ mod tests { }, }, QualifiedPayableAccount { - payable: PayableAccount { + qualified_as: PayableAccount { wallet: make_wallet("wallet1"), balance_wei: gwei_to_wei(999_999_999_u64), last_paid_timestamp: from_time_t(now - 86455), From 7f51d7b6f8bdd21c436d009fe41b61d6d6eb571a Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 4 Apr 2024 17:50:15 +0200 Subject: [PATCH 148/250] GH-711-b: interim commit --- .../payment_adjuster/adjustment_runners.rs | 3 + .../balance_and_age_calculator.rs | 4 + node/src/accountant/payment_adjuster/mod.rs | 268 ++++++++++-------- .../accountant/payment_adjuster/test_utils.rs | 9 +- node/src/accountant/test_utils.rs | 36 ++- 5 files changed, 195 insertions(+), 125 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 76e2569f9..dda860b1a 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -11,6 +11,7 @@ use itertools::Either; use masq_lib::utils::convert_collection; use std::vec; +// TODO review this comment // There are only two runners. They perform adjustment either by both the transaction and service // fee, or exclusively by the transaction fee. The idea is that the adjustment by the transaction // fee may ever appear in the initial iteration of the recursion. In any of the other iterations, @@ -102,9 +103,11 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { weighted_account.qualified_account.qualified_as.balance_wei }); + let unallocated_cw_balance = payment_adjuster .inner .unallocated_cw_service_fee_balance_minor(); + if check_sum <= unallocated_cw_balance { // Fast return after a direct conversion into the expected type return convert_collection(weighted_accounts); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index fcd8a972b..08e6d5128 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -20,6 +20,10 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { .expect("time traveller") .as_secs(); + eprintln!( + "{} - {}", + account.qualified_as.balance_wei, account.payment_threshold_intercept_minor + ); account.qualified_as.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128 } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index cd605f3c5..b56f6bded 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -206,6 +206,7 @@ impl PaymentAdjusterReal { Either::Right(finalized_accounts) => Ok(finalized_accounts), } } + fn calculate_criteria_and_propose_adjustments_recursively( &mut self, unresolved_qualified_accounts: Vec, @@ -413,6 +414,7 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) } + // TODO Should this become a helper? ...with which I can catch mid-results and assert on them? fn compute_unconfirmed_adjustments( &self, weighted_accounts: Vec, @@ -689,7 +691,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total}; - use crate::accountant::payment_adjuster::test_utils::{DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; + use crate::accountant::payment_adjuster::test_utils::{CriterionCalculatorMock, DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal}; use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; @@ -701,9 +703,12 @@ mod tests { use std::time::{Duration, SystemTime}; use std::{usize, vec}; use std::sync::{Arc, Mutex}; + use rand::rngs::mock; use thousands::Separable; use web3::types::U256; + use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInnerNull, PaymentAdjusterInnerReal}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -945,34 +950,71 @@ mod tests { } #[test] - fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { + fn payment_adjuster_calculators_expectedly_defaulted(){ + // First stage: BalanceAndAgeCalculator + let calculators_count = PaymentAdjusterReal::default().calculators.len(); + + let input_matrix:Vec<(QualifiedPayableAccount, u128)> = vec![ + + + + + + + ]; let now = SystemTime::now(); - let subject = make_initialized_subject(now, None, None); + let cw_service_fee_balance_minor = gwei_to_wei::(1_000_000); + let mut subject = make_initialized_subject(now,Some(cw_service_fee_balance_minor),None); let account_1 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), + wallet: make_wallet("abc"), + balance_wei: gwei_to_wei::(3_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), + wallet: make_wallet("def"), + balance_wei: gwei_to_wei::(1_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 444_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), + wallet: make_wallet("ghi"), + balance_wei: gwei_to_wei::(2_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(60_000)).unwrap(), pending_payable_opt: None, }; - let qualified_payables = make_guaranteed_qualified_payables( - vec![account_1.clone(), account_2.clone(), account_3.clone()], - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - ); + let payables = vec![account_1, account_2, account_3]; + let qualified_payables = + make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let criteria_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + + + subject.calculate_criteria_and_propose_adjustments_recursively() + } + + #[test] + fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { + let now = SystemTime::now(); + let subject = make_initialized_subject(now, None, None); + let past = now.checked_sub(Duration::from_secs(10)).unwrap(); + let mut payable_1 = make_non_guaranteed_qualified_payable(111); + payable_1.payment_threshold_intercept_minor = 500_000_000; + payable_1.qualified_as.balance_wei = 1_000_000_002; + payable_1.qualified_as.last_paid_timestamp = past; + let mut payable_2 = make_non_guaranteed_qualified_payable(222); + payable_2.payment_threshold_intercept_minor = 500_000_000; + payable_2.qualified_as.balance_wei = 1_000_000_001; + payable_2.qualified_as.last_paid_timestamp = past; + let mut payable_3 = make_non_guaranteed_qualified_payable(333); + payable_3.payment_threshold_intercept_minor = 500_000_000; + payable_3.qualified_as.balance_wei = 1_000_000_003; + payable_3.qualified_as.last_paid_timestamp = past; + + let criteria_and_accounts = subject.calculate_weights_for_accounts(vec![ + payable_1.clone(), + payable_2.clone(), + payable_3.clone(), + ]); let mut previous_weight = u128::MAX; let accounts_alone = criteria_and_accounts @@ -988,28 +1030,44 @@ mod tests { weighted_account.qualified_account.qualified_as }) .collect::>(); - assert_eq!(accounts_alone, vec![account_3, account_1, account_2]) + assert_eq!( + accounts_alone, + vec![ + payable_3.qualified_as, + payable_1.qualified_as, + payable_2.qualified_as + ] + ) } #[test] - fn minor_but_a_lot_aged_debt_is_prioritized_outweighed_and_stays_as_the_full_original_balance() + fn smaller_but_more_criteria_gaining_account_is_prioritized_outweighed_up_to_original_balance() { let now = SystemTime::now(); - let cw_service_fee_balance = 1_500_000_000_000_u128 - 25_000_000 - 1000; - let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance), None); - let balance_1 = 1_500_000_000_000; - let balance_2 = 25_000_000; - let wallet_1 = make_wallet("blah"); + let cw_service_fee_balance_minor = gwei_to_wei::(3_500_000); + let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); + let mock_calculator = CriterionCalculatorMock::default() + // Account 1, first iteration + .calculate_result(0) + // Account 2 in its only iteration, after which it is found outweighed and handled + // as a priority + .calculate_result(2_000_000_000_000_000) + // Account 1, second iteration + .calculate_result(0); + subject.calculators.push(Box::new(mock_calculator)); + let balance_1 = gwei_to_wei::(2_000_100); + let balance_2 = gwei_to_wei::(1_999_900); let account_1 = PayableAccount { - wallet: wallet_1, + wallet: make_wallet("abc"), balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5_500)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(200_001)).unwrap(), pending_payable_opt: None, }; + let wallet_2 = make_wallet("def"); let account_2 = PayableAccount { - wallet: make_wallet("argh"), + wallet: wallet_2.clone(), balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(20_000)).unwrap(), + last_paid_timestamp: now.checked_sub(Duration::from_secs(200_000)).unwrap(), pending_payable_opt: None, }; let qualified_payables = make_guaranteed_qualified_payables( @@ -1027,25 +1085,22 @@ mod tests { .left() .unwrap(); - // First, let's have an example of why this test is important - let criteria_and_accounts = - subject.calculate_weights_for_accounts(qualified_payables.clone()); - let unconfirmed_adjustments = - subject.compute_unconfirmed_adjustments(criteria_and_accounts); - let proposed_adjusted_balance_2 = unconfirmed_adjustments[1] - .non_finalized_account - .proposed_adjusted_balance_minor; - // The criteria sum of the second account grew very progressively due to the effect of the greater age; - // consequences would've been that redistributing the new balances according to the computed criteria would've - // attributed the second account with a larger amount to pay than it would've had before the test started; - // to prevent it, we set a rule that no account can ever demand more than 100% of itself - assert!( - proposed_adjusted_balance_2 > 10 * balance_2, - "we expected the proposed balance much bigger than the original which is {} but it was {}", + // Let's have an example to explain why this test is important. + // First, the mock must be renewed; the available cw balance updated to the original value. + let mock_calculator = CriterionCalculatorMock::default() + .calculate_result(0) + .calculate_result(2_000_000_000_000_000); + prove_hypothesis_of_proposed_adjusted_balance_be_outweighed( + subject, + now, + cw_service_fee_balance_minor, + mock_calculator, + qualified_payables.clone(), + wallet_2, balance_2, - proposed_adjusted_balance_2 + 1.5, ); - // So the assertion above shows the concern true. + // // So the assertion above showed the concern true. let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!( @@ -1061,78 +1116,59 @@ mod tests { &second_returned_account.qualified_payable, &qualified_payables[0] ); - let upper_limit = 1_500_000_000_000_u128 - 25_000_000 - 25_000_000 - 1000; - let lower_limit = (upper_limit * 9) / 10; - assert!( - lower_limit <= second_returned_account.proposed_adjusted_balance_minor - && second_returned_account.proposed_adjusted_balance_minor <= upper_limit, - "we expected the roughly adjusted account to be between {} and {} but was {}", - lower_limit, - upper_limit, - second_returned_account.proposed_adjusted_balance_minor + assert_eq!( + second_returned_account.proposed_adjusted_balance_minor, + 1_500_099_999_999_999 ); assert!(result.is_empty()); } - #[test] - fn outweighed_account_never_demands_more_than_cw_balance_because_disqualified_accounts_go_first( + fn prove_hypothesis_of_proposed_adjusted_balance_be_outweighed( + mut subject: PaymentAdjusterReal, + now: SystemTime, + cw_service_fee_balance_minor: u128, + mock_calculator: CriterionCalculatorMock, + accounts: Vec, + wallet_of_expected_outweighed: Wallet, + original_balance_of_outweighed_account: u128, + outweighed_by_multiple_of: f64, ) { - // NOTE that the same is true for more outweighed accounts that would require more than - // the whole cw balance together, therefore there is no such a test either. - // This test answers the question of what is happening when the cw service fee balance cannot - // cover the outweighed accounts, which is just a hypothesis we can never reach in - // the reality. - // If there are outweighed accounts some other accounts must be also around of which some - // are under the disqualification limit pointing to one that would definitely head to its - // disqualification. - // With enough money, the other account might not meet disqualification which means, though, - // the initial concern is still groundless: there must be enough money at the moment to - // cover the outweighed account if there is another one which is considered neither as - // outweighed or disqualified. - const SECONDS_IN_3_DAYS: u64 = 259_200; - let test_name = - "outweighed_account_never_demands_more_than_cw_balance_because_disqualified_accounts_go_first"; - let now = SystemTime::now(); - let consuming_wallet_balance = 1_000_000_000_000_u128 - 1; - let account_1 = PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 1_000_000_000_000, - last_paid_timestamp: now - // Greater age like this together with smaller balance usually causes the account to outweigh - .checked_sub(Duration::from_secs(SECONDS_IN_3_DAYS)) - .unwrap(), - pending_payable_opt: None, - }; - let balance_2 = 8_000_000_000_000_000; - let wallet_2 = make_wallet("booga"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }; - let accounts = vec![account_1.clone(), account_2]; - let mut qualified_payables = - make_guaranteed_qualified_payables(accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let subject = make_initialized_subject( + subject.calculators.pop().unwrap(); + subject.calculators.push(Box::new(mock_calculator)); + subject.inner = Box::new(PaymentAdjusterInnerReal::new( now, - Some(consuming_wallet_balance), - Some(Logger::new(test_name)), + None, + cw_service_fee_balance_minor, + )); + let criteria_and_accounts = subject.calculate_weights_for_accounts(accounts); + let unconfirmed_adjustments = + subject.compute_unconfirmed_adjustments(criteria_and_accounts); + // The results are sorted from the biggest weights down + let proposed_adjusted_balance = unconfirmed_adjustments[0] + .non_finalized_account + .proposed_adjusted_balance_minor; + assert_eq!( + unconfirmed_adjustments[0] + .non_finalized_account + .qualified_payable + .qualified_as + .wallet, + wallet_of_expected_outweighed + ); + // The weight of this account grew progressively due to the additional criterion added + // in to the sum. Consequences would've been that redistribution of the adjusted balances + // would've attributed this account with a larger amount to pay than it would've + // contained before the test started. To prevent that, we secure a rule that an account can + // never demand more than 100% of itself, ever + assert!( + proposed_adjusted_balance + > (outweighed_by_multiple_of * original_balance_of_outweighed_account as f64) + as u128, + "we expected the proposed balance clearly bigger than the original which is {} \ + but it was {}", + original_balance_of_outweighed_account.separate_with_commas(), + proposed_adjusted_balance.separate_with_commas() ); - let weighted_accounts = subject.calculate_weights_for_accounts(qualified_payables.clone()); - - let result = subject.perform_adjustment_by_service_fee(weighted_accounts); - - let remaining = match result { - AdjustmentIterationResult::SpecialTreatmentRequired { - case: TreatInsignificantAccount, - remaining_undecided_accounts: remaining, - } => remaining, - x => panic!("we expected to see a disqualified account but got: {:?}", x), - }; - // We eliminated (disqualified) the other account than which was going to qualify as outweighed - let expected_account = qualified_payables.remove(0); - assert_eq!(remaining, vec![expected_account]); } #[test] @@ -1141,20 +1177,20 @@ mod tests { let now = SystemTime::now(); let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: 3_000_000_000_000, + balance_wei: gwei_to_wei::(3_000_000), last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: 1_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), + balance_wei: gwei_to_wei::(1_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), pending_payable_opt: None, }; let account_3 = PayableAccount { wallet: make_wallet("ghi"), - balance_wei: 2_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + balance_wei: gwei_to_wei::(2_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(60_000)).unwrap(), pending_payable_opt: None, }; let payables = vec![account_1, account_2, account_3]; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index ebba9df85..72becab09 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -30,12 +30,12 @@ lazy_static! { pub fn make_initialized_subject( now: SystemTime, - cw_masq_balance_minor_opt: Option, + cw_service_fee_balance_minor_opt: Option, logger_opt: Option, ) -> PaymentAdjusterReal { - let cw_masq_balance_minor = cw_masq_balance_minor_opt.unwrap_or(0); + let cw_masq_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); let logger = logger_opt.unwrap_or(Logger::new("test")); - let mut subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterReal::default(); subject.logger = logger; subject.inner = Box::new(PaymentAdjusterInnerReal::new( now, @@ -78,7 +78,7 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO maturity_threshold_sec: 1_000, payment_grace_period_sec: 1_000, permanent_debt_allowed_gwei: 1_000_000, - threshold_interval_sec: 1_000_000, + threshold_interval_sec: 500_000, unban_below_gwei: 1_000_000, }; @@ -124,6 +124,7 @@ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustme } } +#[derive(Default)] pub struct CriterionCalculatorMock { calculate_results: RefCell>, } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 6fed49421..35c9a9c86 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1701,10 +1701,36 @@ pub fn make_guaranteed_qualified_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { + fn panic( + payable: &PayableAccount, + payment_thresholds: &PaymentThresholds, + now: SystemTime, + ) -> ! { + panic!( + "You intend to create qualified payables but their parameters not always make them qualify \ + as in: {:?} where the balance needs to get over {}", + payable, + PayableThresholdsGaugeReal::default().calculate_payout_threshold_in_gwei( + payment_thresholds, + SystemTime::now().duration_since(now).unwrap().as_secs() + ) + ) + } + let payable_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); - payables.into_iter().map(|payable|{ - let payment_threshold_intercept = payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) - .unwrap_or_else(||panic!("You intend to create qualified payables but their paramters not always make them qualify as in: {:?}", payable)); - QualifiedPayableAccount::new(payable, payment_threshold_intercept, CreditorThresholds::new(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei))) - }).collect() + payables + .into_iter() + .map(|payable| { + let payment_threshold_intercept = payable_inspector + .payable_exceeded_threshold(&payable, payment_thresholds, now) + .unwrap_or_else(|| panic(&payable, payment_thresholds, now)); + QualifiedPayableAccount::new( + payable, + payment_threshold_intercept, + CreditorThresholds::new(gwei_to_wei( + payment_thresholds.permanent_debt_allowed_gwei, + )), + ) + }) + .collect() } From bb6655c69f04aed6d8ec823b7e0e30e41ae7ac1b Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 7 Apr 2024 21:53:25 +0200 Subject: [PATCH 149/250] GH-711-b: big tests with real calculators finished --- node/src/accountant/payment_adjuster/service_fee_adjuster.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/service_fee_adjuster.rs diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs new file mode 100644 index 000000000..e69de29bb From c694686ae151e6a4a7438e00fc4082d3819bec55 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 7 Apr 2024 21:53:46 +0200 Subject: [PATCH 150/250] GH-711-b: big tests with real calculators finished --- .../disqualification_arbiter.rs | 8 +- node/src/accountant/payment_adjuster/mod.rs | 514 ++++++++++++++---- .../payment_adjuster/service_fee_adjuster.rs | 119 ++++ .../accountant/payment_adjuster/test_utils.rs | 46 +- 4 files changed, 571 insertions(+), 116 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 16f1a99b1..dbffa2020 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -202,6 +202,7 @@ mod tests { AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, DisqualificationGaugeMock, @@ -425,7 +426,7 @@ mod tests { let test_name = "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; let now = SystemTime::now(); - let cw_masq_balance = 200_000_000_000; + let cw_service_fee_balance_minor = 200_000_000_000; let mut payment_thresholds = PaymentThresholds::default(); payment_thresholds.permanent_debt_allowed_gwei = 10; payment_thresholds.maturity_threshold_sec = 1_000; @@ -434,7 +435,7 @@ mod tests { + payment_thresholds.threshold_interval_sec + 1; let logger = Logger::new(test_name); - let subject = make_initialized_subject(now, Some(cw_masq_balance), None); + let subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); let wallet_1 = make_wallet("abc"); let debt_age_1 = base_time_for_qualified + 1; let account_1 = PayableAccount { @@ -471,7 +472,8 @@ mod tests { let qualified_payables = make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let unconfirmed_adjustments = subject.compute_unconfirmed_adjustments(weights_and_accounts); + let unconfirmed_adjustments = AdjustmentComputer::default() + .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); let subject = DisqualificationArbiter::default(); let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b56f6bded..9ef8c47c3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -11,6 +11,7 @@ mod loading_test; mod log_fns; mod miscellaneous; mod preparatory_analyser; +mod service_fee_adjuster; #[cfg(test)] mod test_utils; @@ -45,8 +46,9 @@ use web3::types::U256; use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::{CriterionCalculator}; -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics, proposed_adjusted_balance_diagnostics}; -use crate::accountant::payment_adjuster::disqualification_arbiter::{DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal}; +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics}; +use crate::accountant::payment_adjuster::disqualification_arbiter::{DisqualificationArbiter, DisqualificationGauge}; +use crate::accountant::payment_adjuster::service_fee_adjuster::{ServiceFeeAdjuster, ServiceFeeAdjusterReal}; use crate::accountant::QualifiedPayableAccount; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -71,6 +73,7 @@ pub struct PaymentAdjusterReal { analyzer: PreparatoryAnalyzer, disqualification_arbiter: DisqualificationArbiter, inner: Box, + service_fee_adjuster: Box, calculators: Vec>, logger: Logger, } @@ -153,6 +156,7 @@ impl PaymentAdjusterReal { analyzer: PreparatoryAnalyzer::new(), disqualification_arbiter: DisqualificationArbiter::default(), inner: Box::new(PaymentAdjusterInnerNull {}), + service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], logger: Logger::new("PaymentAdjuster"), } @@ -288,7 +292,14 @@ impl PaymentAdjusterReal { &mut self, weighed_accounts: Vec, ) -> Vec { - let current_iteration_result = self.perform_adjustment_by_service_fee(weighed_accounts); + let unallocated_cw_service_fee_balance = + self.inner.unallocated_cw_service_fee_balance_minor(); + let logger = &self.logger; + let current_iteration_result = self.service_fee_adjuster.perform_adjustment_by_service_fee( + weighed_accounts, + unallocated_cw_service_fee_balance, + logger, + ); let recursion_results = self.resolve_current_iteration_result(current_iteration_result); @@ -390,68 +401,68 @@ impl PaymentAdjusterReal { sort_in_descendant_order_by_weights(weighted_accounts) } - - fn perform_adjustment_by_service_fee( - &self, - weighted_accounts: Vec, - ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = - self.compute_unconfirmed_adjustments(weighted_accounts); - - let still_unchecked_for_disqualified = - match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { - Either::Left(first_check_passing_accounts) => first_check_passing_accounts, - Either::Right(with_some_outweighed) => return with_some_outweighed, - }; - - let verified_accounts = match self - .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) - { - Either::Left(verified_accounts) => verified_accounts, - Either::Right(with_some_disqualified) => return with_some_disqualified, - }; - - AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) - } - - // TODO Should this become a helper? ...with which I can catch mid-results and assert on them? - fn compute_unconfirmed_adjustments( - &self, - weighted_accounts: Vec, - ) -> Vec { - let weights_total = weights_total(&weighted_accounts); - let largest_weight = find_largest_weight(&weighted_accounts); - let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - - let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( - cw_service_fee_balance, - largest_weight, - ); - - let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( - cw_service_fee_balance, - weights_total, - multiplication_coefficient, - ); - - let compute_proposed_adjusted_balance = - |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; - - weighted_accounts - .into_iter() - .map(|weighted_account| { - let proposed_adjusted_balance = - compute_proposed_adjusted_balance(weighted_account.weight); - - proposed_adjusted_balance_diagnostics( - &weighted_account.qualified_account, - proposed_adjusted_balance, - ); - - UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) - }) - .collect() - } + // + // fn perform_adjustment_by_service_fee( + // &self, + // weighted_accounts: Vec, + // ) -> AdjustmentIterationResult { + // let non_finalized_adjusted_accounts = + // self.compute_unconfirmed_adjustments(weighted_accounts); + // + // let still_unchecked_for_disqualified = + // match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { + // Either::Left(first_check_passing_accounts) => first_check_passing_accounts, + // Either::Right(with_some_outweighed) => return with_some_outweighed, + // }; + // + // let verified_accounts = match self + // .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) + // { + // Either::Left(verified_accounts) => verified_accounts, + // Either::Right(with_some_disqualified) => return with_some_disqualified, + // }; + // + // AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) + // } + + // // TODO Should this become a helper? ...with which I can catch mid-results and assert on them? + // fn compute_unconfirmed_adjustments( + // &self, + // weighted_accounts: Vec, + // ) -> Vec { + // let weights_total = weights_total(&weighted_accounts); + // let largest_weight = find_largest_weight(&weighted_accounts); + // let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); + // + // let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( + // cw_service_fee_balance, + // largest_weight, + // ); + // + // let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( + // cw_service_fee_balance, + // weights_total, + // multiplication_coefficient, + // ); + // + // let compute_proposed_adjusted_balance = + // |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; + // + // weighted_accounts + // .into_iter() + // .map(|weighted_account| { + // let proposed_adjusted_balance = + // compute_proposed_adjusted_balance(weighted_account.weight); + // + // proposed_adjusted_balance_diagnostics( + // &weighted_account.qualified_account, + // proposed_adjusted_balance, + // ); + // + // UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) + // }) + // .collect() + // } fn compute_proportional_cw_fragment( cw_service_fee_balance: u128, @@ -688,10 +699,10 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment}; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total}; - use crate::accountant::payment_adjuster::test_utils::{CriterionCalculatorMock, DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS}; + use crate::accountant::payment_adjuster::test_utils::{CriterionCalculatorMock, DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, ServiceFeeAdjusterMock}; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal}; use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; @@ -702,13 +713,17 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; + use std::collections::HashMap; + use std::iter::zip; use std::sync::{Arc, Mutex}; + use lazy_static::lazy_static; use rand::rngs::mock; use thousands::Separable; use web3::types::U256; use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInnerNull, PaymentAdjusterInnerReal}; + use crate::accountant::payment_adjuster::service_fee_adjuster::{AdjustmentComputer, ServiceFeeAdjusterReal}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -949,49 +964,6 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } - #[test] - fn payment_adjuster_calculators_expectedly_defaulted(){ - // First stage: BalanceAndAgeCalculator - let calculators_count = PaymentAdjusterReal::default().calculators.len(); - - let input_matrix:Vec<(QualifiedPayableAccount, u128)> = vec![ - - - - - - - ]; - let now = SystemTime::now(); - let cw_service_fee_balance_minor = gwei_to_wei::(1_000_000); - let mut subject = make_initialized_subject(now,Some(cw_service_fee_balance_minor),None); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: gwei_to_wei::(3_000_000), - last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: make_wallet("def"), - balance_wei: gwei_to_wei::(1_000_000), - last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), - pending_payable_opt: None, - }; - let account_3 = PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: gwei_to_wei::(2_000_000), - last_paid_timestamp: now.checked_sub(Duration::from_secs(60_000)).unwrap(), - pending_payable_opt: None, - }; - let payables = vec![account_1, account_2, account_3]; - let qualified_payables = - make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - - - - subject.calculate_criteria_and_propose_adjustments_recursively() - } - #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { let now = SystemTime::now(); @@ -1141,8 +1113,8 @@ mod tests { cw_service_fee_balance_minor, )); let criteria_and_accounts = subject.calculate_weights_for_accounts(accounts); - let unconfirmed_adjustments = - subject.compute_unconfirmed_adjustments(criteria_and_accounts); + let unconfirmed_adjustments = AdjustmentComputer::default() + .compute_unconfirmed_adjustments(criteria_and_accounts, cw_service_fee_balance_minor); // The results are sorted from the biggest weights down let proposed_adjusted_balance = unconfirmed_adjustments[0] .non_finalized_account @@ -2293,4 +2265,322 @@ mod tests { Box::new(blockchain_agent) } + + // The following tests together prove the use of correct calculators in the production code + + #[test] + fn each_of_defaulted_calculators_returns_different_value() { + let now = SystemTime::now(); + let payment_adjuster = PaymentAdjusterReal::default(); + let qualified_payable = QualifiedPayableAccount { + qualified_as: PayableAccount { + wallet: make_wallet("abc"), + balance_wei: gwei_to_wei::(444_666_888), + last_paid_timestamp: now.checked_sub(Duration::from_secs(123_000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept_minor: gwei_to_wei::(20_000), + creditor_thresholds: CreditorThresholds::new(gwei_to_wei::(10_000)), + }; + let cw_service_fee_balance_minor = gwei_to_wei::(3_000); + let context = PaymentAdjusterInnerReal::new(now, None, cw_service_fee_balance_minor); + let _ = payment_adjuster + .calculators + .into_iter() + .map(|calculator| calculator.calculate(&qualified_payable, &context)) + .fold(0, |previous_result, current_result| { + let min = (current_result * 97) / 100; + let max = (current_result * 97) / 100; + assert_ne!(current_result, 0); + assert!(min <= previous_result || previous_result <= max); + current_result + }); + } + + type InputMatrixConfigurator = fn( + (QualifiedPayableAccount, QualifiedPayableAccount, SystemTime), + ) -> Vec<[(QualifiedPayableAccount, u128); 2]>; + + #[test] + fn defaulted_calculators_react_on_correct_params() { + // When adding a test case for a new calculator, you need to make an array of inputs. Don't + // create brand-new accounts but clone the provided nominal accounts and modify them + // accordingly. Modify only those parameters that affect your calculator. + // It's recommended to orientate the modifications rather positively (additions), because + // there is a smaller chance you would run into some limit + let input_matrix: InputMatrixConfigurator = + |(nominal_account_1, nominal_account_2, now)| { + vec![ + // First stage: BalanceAndAgeCalculator + { + let mut account_1 = nominal_account_1; + account_1.qualified_as.balance_wei += 123_456_789; + let mut account_2 = nominal_account_2; + account_2.qualified_as.last_paid_timestamp = account_2 + .qualified_as + .last_paid_timestamp + .checked_sub(Duration::from_secs(4_000)) + .unwrap(); + [(account_1, 8000000123466789), (account_2, 8000000000014000)] + }, + ] + }; + // This is the value that is computed if the account stays unmodified. Same for both nominal + // accounts. + let current_nominal_weight = 8000000000010000; + + test_calculators_reactivity(input_matrix, current_nominal_weight) + } + + #[derive(Clone, Copy)] + struct TemplateComputedWeight { + common_weight: u128, + } + + struct SingleAccountInput { + account: QualifiedPayableAccount, + assertion_value: AssertionValue, + } + + struct AssertionValue { + wallet_to_match_result_with: Wallet, + expected_computed_weight: u128, + } + + fn test_calculators_reactivity( + input_matrix_configurator: InputMatrixConfigurator, + nominal_weight: u128, + ) { + let defaulted_payment_adjuster = PaymentAdjusterReal::default(); + let calculators_count = defaulted_payment_adjuster.calculators.len(); + let now = SystemTime::now(); + let cw_service_fee_balance_minor = gwei_to_wei::(1_000_000); + let (template_accounts, template_computed_weight) = + prepare_nominal_data_before_loading_actual_test_input( + now, + cw_service_fee_balance_minor, + ); + assert_eq!(template_computed_weight.common_weight, nominal_weight); + let mut template_accounts = template_accounts.to_vec(); + let mut pop_account = || template_accounts.remove(0); + let nominal_account_1 = pop_account(); + let nominal_account_2 = pop_account(); + let input_matrix = input_matrix_configurator((nominal_account_1, nominal_account_2, now)); + assert_eq!( + input_matrix.len(), + calculators_count, + "If you've recently added in a new \ + calculator, you should add a single test case for it this test. See the input matrix, \ + it is the place where you should use the two accounts you can clone. Make sure you \ + modify only those parameters processed by your new calculator " + ); + test_accounts_from_input_matrix( + input_matrix, + defaulted_payment_adjuster, + now, + cw_service_fee_balance_minor, + template_computed_weight, + ) + } + + fn prepare_nominal_data_before_loading_actual_test_input( + now: SystemTime, + cw_service_fee_balance_minor: u128, + ) -> ([QualifiedPayableAccount; 2], TemplateComputedWeight) { + let template_accounts = initialize_template_accounts(now); + let template_weight = compute_common_weight_for_templates( + template_accounts.clone(), + now, + cw_service_fee_balance_minor, + ); + (template_accounts, template_weight) + } + + fn initialize_template_accounts(now: SystemTime) -> [QualifiedPayableAccount; 2] { + let make_qualified_payable = |wallet| QualifiedPayableAccount { + qualified_as: PayableAccount { + wallet, + balance_wei: gwei_to_wei::(20_000_000), + last_paid_timestamp: now.checked_sub(Duration::from_secs(10_000)).unwrap(), + pending_payable_opt: None, + }, + payment_threshold_intercept_minor: gwei_to_wei::(12_000_000), + creditor_thresholds: CreditorThresholds::new(gwei_to_wei::(1_000_000)), + }; + + [ + make_qualified_payable(make_wallet("abc")), + make_qualified_payable(make_wallet("def")), + ] + } + + fn compute_common_weight_for_templates( + template_accounts: [QualifiedPayableAccount; 2], + now: SystemTime, + cw_service_fee_balance_minor: u128, + ) -> TemplateComputedWeight { + let template_results = exercise_production_code_to_get_weighted_accounts( + template_accounts.to_vec(), + now, + cw_service_fee_balance_minor, + ); + let templates_common_weight = template_results + .iter() + .map(|account| account.weight) + .reduce(|previous, current| { + assert_eq!(previous, current); + current + }) + .unwrap(); + // Formal test if the value is different from zero, + // and ideally much bigger than that + assert!(1_000_000_000_000 < templates_common_weight); + TemplateComputedWeight { + common_weight: templates_common_weight, + } + } + + fn exercise_production_code_to_get_weighted_accounts( + qualified_payables: Vec, + now: SystemTime, + cw_service_fee_balance_minor: u128, + ) -> Vec { + let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); + let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); + let service_fee_adjuster_mock = ServiceFeeAdjusterMock::default() + // We use this container to intercept those values we are after + .perform_adjustment_by_service_fee_params(&perform_adjustment_by_service_fee_params_arc) + // This is just a sentinel for an actual result. + // We care only for the params + .perform_adjustment_by_service_fee_result( + AdjustmentIterationResult::AllAccountsProcessed(vec![]), + ); + subject.service_fee_adjuster = Box::new(service_fee_adjuster_mock); + + let result = subject.run_adjustment(qualified_payables.to_vec()); + + less_important_constant_assertions_and_weighted_accounts_extraction( + result, + perform_adjustment_by_service_fee_params_arc, + cw_service_fee_balance_minor, + ) + } + + fn less_important_constant_assertions_and_weighted_accounts_extraction( + actual_result: Result, PaymentAdjusterError>, + perform_adjustment_by_service_fee_params_arc: Arc, u128)>>>, + cw_service_fee_balance_minor: u128, + ) -> Vec { + // This error should be ignored, as it has no real meaning. + // It allows to halt the code executions without a dive in the recursion + assert_eq!( + actual_result, + Err(PaymentAdjusterError::AllAccountsEliminated) + ); + let mut perform_adjustment_by_service_fee_params = + perform_adjustment_by_service_fee_params_arc.lock().unwrap(); + let (weighted_accounts, captured_cw_service_fee_balance_minor) = + perform_adjustment_by_service_fee_params.remove(0); + assert_eq!( + captured_cw_service_fee_balance_minor, + cw_service_fee_balance_minor + ); + assert!(perform_adjustment_by_service_fee_params.is_empty()); + weighted_accounts + } + + fn test_accounts_from_input_matrix( + input_matrix: Vec<[(QualifiedPayableAccount, u128); 2]>, + defaulted_payment_adjuster: PaymentAdjusterReal, + now: SystemTime, + cw_service_fee_balance_minor: u128, + template_computed_weight: TemplateComputedWeight, + ) { + fn prepare_args_expected_weights_for_comparison( + (qualified_payable, expected_computed_payable): (QualifiedPayableAccount, u128), + ) -> (QualifiedPayableAccount, (Wallet, u128)) { + let wallet = qualified_payable.qualified_as.wallet.clone(); + (qualified_payable, (wallet, expected_computed_payable)) + } + + input_matrix + .into_iter() + .map(|test_case| { + test_case + .into_iter() + .map(prepare_args_expected_weights_for_comparison) + .collect::>() + }) + .zip(defaulted_payment_adjuster.calculators.into_iter()) + .for_each( + |((qualified_payments_and_expected_computed_weights), calculator)| { + let (qualified_payments, expected_computed_weights): (Vec<_>, Vec<_>) = + qualified_payments_and_expected_computed_weights + .into_iter() + .unzip(); + + let weighted_accounts = exercise_production_code_to_get_weighted_accounts( + qualified_payments, + now, + cw_service_fee_balance_minor, + ); + + assert_results( + weighted_accounts, + expected_computed_weights, + template_computed_weight, + ) + }, + ); + } + + fn make_comparison_hashmap( + weighted_accounts: Vec, + ) -> HashMap { + let feeding_iterator = weighted_accounts.into_iter().map(|account| { + ( + account.qualified_account.qualified_as.wallet.clone(), + account, + ) + }); + HashMap::from_iter(feeding_iterator) + } + + fn assert_results( + weighted_accounts: Vec, + expected_computed_weights: Vec<(Wallet, u128)>, + template_computed_weight: TemplateComputedWeight, + ) { + let weighted_accounts_as_hash_map = make_comparison_hashmap(weighted_accounts); + expected_computed_weights.into_iter().fold( + 0, + |previous_account_actual_weight, (account_wallet, expected_computed_weight)| { + let actual_account = weighted_accounts_as_hash_map + .get(&account_wallet) + .unwrap_or_else(|| { + panic!("Account for wallet {:?} disappeared", account_wallet) + }); + assert_ne!( + actual_account.weight, template_computed_weight.common_weight, + "Weight is exactly the same as that one from the template. The inputs \ + (modifications in the template accounts) are supposed to cause the weight to \ + evaluated differently." + ); + assert_eq!( + actual_account.weight, + expected_computed_weight, + "Computed weight {} differs from what was expected {}", + actual_account.weight.separate_with_commas(), + expected_computed_weight.separate_with_commas() + ); + assert_ne!( + actual_account.weight, previous_account_actual_weight, + "You were expected to prepare two accounts with at least slightly \ + different parameters. Therefore, the evenness of their weights is \ + highly improbable and suspicious." + ); + actual_account.weight + }, + ); + } } diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index e69de29bb..7f2c4caa5 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -0,0 +1,119 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, +}; +use masq_lib::logger::Logger; + +pub trait ServiceFeeAdjuster { + fn perform_adjustment_by_service_fee( + &self, + weighted_accounts: Vec, + unallocated_cw_service_fee_balance_minor: u128, + logger: &Logger, + ) -> AdjustmentIterationResult; +} + +pub struct ServiceFeeAdjusterReal { + adjustment_computer: AdjustmentComputer, +} + +impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { + fn perform_adjustment_by_service_fee( + &self, + weighted_accounts: Vec, + cw_service_fee_balance_minor: u128, + logger: &Logger, + ) -> AdjustmentIterationResult { + todo!() + } +} + +impl Default for ServiceFeeAdjusterReal { + fn default() -> Self { + Self::new() + } +} + +impl ServiceFeeAdjusterReal { + fn new() -> Self { + Self { + adjustment_computer: Default::default(), + } + } +} + +#[derive(Default)] +pub struct AdjustmentComputer {} + +impl AdjustmentComputer { + pub fn compute_unconfirmed_adjustments( + &self, + weighted_accounts: Vec, + unallocated_cw_service_fee_balance_minor: u128, + ) -> Vec { + todo!() + } +} + +// fn perform_adjustment_by_service_fee( +// &self, +// weighted_accounts: Vec, +// ) -> AdjustmentIterationResult { +// let non_finalized_adjusted_accounts = +// self.compute_unconfirmed_adjustments(weighted_accounts); +// +// let still_unchecked_for_disqualified = +// match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { +// Either::Left(first_check_passing_accounts) => first_check_passing_accounts, +// Either::Right(with_some_outweighed) => return with_some_outweighed, +// }; +// +// let verified_accounts = match self +// .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) +// { +// Either::Left(verified_accounts) => verified_accounts, +// Either::Right(with_some_disqualified) => return with_some_disqualified, +// }; +// +// AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) +// } + +// TODO Should this become a helper? ...with which I can catch mid-results and assert on them? +// +// fn compute_unconfirmed_adjustments( +// &self, +// weighted_accounts: Vec, +// ) -> Vec { +// let weights_total = weights_total(&weighted_accounts); +// let largest_weight = find_largest_weight(&weighted_accounts); +// let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); +// +// let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( +// cw_service_fee_balance, +// largest_weight, +// ); +// +// let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( +// cw_service_fee_balance, +// weights_total, +// multiplication_coefficient, +// ); +// let compute_proposed_adjusted_balance = +// |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; +// +// weighted_accounts +// .into_iter() +// .map(|weighted_account| { +// let proposed_adjusted_balance = +// compute_proposed_adjusted_balance(weighted_account.weight); +// +// proposed_adjusted_balance_diagnostics( +// &weighted_account.qualified_account, +// proposed_adjusted_balance, +// ); +// +// UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) +// }) +// .collect() +// } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 72becab09..c94c98c18 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -7,8 +7,10 @@ use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalcula use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, + WeightedPayable, }; +use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use crate::accountant::QualifiedPayableAccount; @@ -184,3 +186,45 @@ impl DisqualificationGaugeMock { self } } + +#[derive(Default)] +pub struct ServiceFeeAdjusterMock { + perform_adjustment_by_service_fee_params: Arc, u128)>>>, + perform_adjustment_by_service_fee_results: RefCell>, +} +impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { + fn perform_adjustment_by_service_fee( + &self, + weighted_accounts: Vec, + unallocated_cw_service_fee_balance_minor: u128, + _logger: &Logger, + ) -> AdjustmentIterationResult { + self.perform_adjustment_by_service_fee_params + .lock() + .unwrap() + .push((weighted_accounts, unallocated_cw_service_fee_balance_minor)); + self.perform_adjustment_by_service_fee_results + .borrow_mut() + .remove(0) + } +} + +impl ServiceFeeAdjusterMock { + pub fn perform_adjustment_by_service_fee_params( + mut self, + params: &Arc, u128)>>>, + ) -> Self { + self.perform_adjustment_by_service_fee_params = params.clone(); + self + } + + pub fn perform_adjustment_by_service_fee_result( + self, + result: AdjustmentIterationResult, + ) -> Self { + self.perform_adjustment_by_service_fee_results + .borrow_mut() + .push(result); + self + } +} From 8bbd653a5b1d59f5f48cb4b5cc9615abce89d7ab Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 8 Apr 2024 00:33:11 +0200 Subject: [PATCH 151/250] GH-711-b: another bunch of tests fixed --- .../payment_adjuster/adjustment_runners.rs | 10 +- .../disqualification_arbiter.rs | 137 +++--- .../miscellaneous/data_structures.rs | 10 +- node/src/accountant/payment_adjuster/mod.rs | 449 +++++++----------- .../payment_adjuster/service_fee_adjuster.rs | 154 +++++- .../accountant/payment_adjuster/test_utils.rs | 32 +- 6 files changed, 416 insertions(+), 376 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index dda860b1a..fe77c70c1 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -200,7 +200,7 @@ mod tests { now: SystemTime, service_fee_balance: u128, ) -> PaymentAdjusterReal { - make_initialized_subject(now, Some(service_fee_balance), None) + make_initialized_subject(Some(now), Some(service_fee_balance), None, None) } fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { @@ -217,8 +217,8 @@ mod tests { payable_2: WeightedPayable, cw_service_fee_balance_minor: u128, ) { - // The disqualification doesn't take part in here, it is just an explanation for - // those who wonder why the implied surplus may happen + // The disqualification doesn't take part in here, it is just an explanation for those who + // wonder why the implied surplus may happen let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor); let initial_balance_minor_1 = payable_1.qualified_account.qualified_as.balance_wei; @@ -246,7 +246,7 @@ mod tests { } #[test] - fn service_fee_only_runner_handles_surplus_even_to_cw_after_dql_in_previous_iteration() { + fn service_fee_only_runner_cw_balance_equals_requested_money_after_dql_in_previous_iteration() { let cw_service_fee_balance_minor = 10_000_000_000; let payable_1 = make_weighed_payable(111, 5_000_000_000); let payable_2 = make_weighed_payable(222, 5_000_000_000); @@ -259,7 +259,7 @@ mod tests { } #[test] - fn service_fee_only_runner_handles_surplus_incurred_after_disqualification_in_previous_iteration( + fn service_fee_only_runner_handles_means_bigger_requested_money_after_dql_in_previous_iteration( ) { let cw_service_fee_balance_minor = 10_000_000_000; let payable_1 = make_weighed_payable(111, 5_000_000_000); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index dbffa2020..95695d45a 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -423,74 +423,75 @@ mod tests { #[test] fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { - let test_name = - "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; - let now = SystemTime::now(); - let cw_service_fee_balance_minor = 200_000_000_000; - let mut payment_thresholds = PaymentThresholds::default(); - payment_thresholds.permanent_debt_allowed_gwei = 10; - payment_thresholds.maturity_threshold_sec = 1_000; - payment_thresholds.threshold_interval_sec = 10_000; - let base_time_for_qualified = payment_thresholds.maturity_threshold_sec - + payment_thresholds.threshold_interval_sec - + 1; - let logger = Logger::new(test_name); - let subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); - let wallet_1 = make_wallet("abc"); - let debt_age_1 = base_time_for_qualified + 1; - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_1)).unwrap(), - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let debt_age_2 = base_time_for_qualified + 3; - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_2)).unwrap(), - pending_payable_opt: None, - }; - let wallet_3 = make_wallet("ghi"); - let debt_age_3 = base_time_for_qualified; - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_3)).unwrap(), - pending_payable_opt: None, - }; - let wallet_4 = make_wallet("jkl"); - let debt_age_4 = base_time_for_qualified + 2; - let account_4 = PayableAccount { - wallet: wallet_4.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_4)).unwrap(), - pending_payable_opt: None, - }; - let accounts = vec![account_1, account_2, account_3, account_4]; - let qualified_payables = - make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); - let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); - let subject = DisqualificationArbiter::default(); - - let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - &logger, - ); - - unconfirmed_adjustments.iter().for_each(|payable| { - // Condition of disqualification at the horizontal threshold - assert!( - payable - .non_finalized_account - .proposed_adjusted_balance_minor - < 120_000_000_000 - ) - }); - assert_eq!(result, Some(wallet_3)); + todo!() + // let test_name = + // "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; + // let now = SystemTime::now(); + // let cw_service_fee_balance_minor = 200_000_000_000; + // let mut payment_thresholds = PaymentThresholds::default(); + // payment_thresholds.permanent_debt_allowed_gwei = 10; + // payment_thresholds.maturity_threshold_sec = 1_000; + // payment_thresholds.threshold_interval_sec = 10_000; + // let base_time_for_qualified = payment_thresholds.maturity_threshold_sec + // + payment_thresholds.threshold_interval_sec + // + 1; + // let logger = Logger::new(test_name); + // let subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None, None); + // let wallet_1 = make_wallet("abc"); + // let debt_age_1 = base_time_for_qualified + 1; + // let account_1 = PayableAccount { + // wallet: wallet_1.clone(), + // balance_wei: 120_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_1)).unwrap(), + // pending_payable_opt: None, + // }; + // let wallet_2 = make_wallet("def"); + // let debt_age_2 = base_time_for_qualified + 3; + // let account_2 = PayableAccount { + // wallet: wallet_2.clone(), + // balance_wei: 120_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_2)).unwrap(), + // pending_payable_opt: None, + // }; + // let wallet_3 = make_wallet("ghi"); + // let debt_age_3 = base_time_for_qualified; + // let account_3 = PayableAccount { + // wallet: wallet_3.clone(), + // balance_wei: 120_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_3)).unwrap(), + // pending_payable_opt: None, + // }; + // let wallet_4 = make_wallet("jkl"); + // let debt_age_4 = base_time_for_qualified + 2; + // let account_4 = PayableAccount { + // wallet: wallet_4.clone(), + // balance_wei: 120_000_000_000, + // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_4)).unwrap(), + // pending_payable_opt: None, + // }; + // let accounts = vec![account_1, account_2, account_3, account_4]; + // let qualified_payables = + // make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); + // let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + // let unconfirmed_adjustments = AdjustmentComputer::default() + // .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); + // let subject = DisqualificationArbiter::default(); + // + // let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( + // &unconfirmed_adjustments, + // &logger, + // ); + // + // unconfirmed_adjustments.iter().for_each(|payable| { + // // Condition of disqualification at the horizontal threshold + // assert!( + // payable + // .non_finalized_account + // .proposed_adjusted_balance_minor + // < 120_000_000_000 + // ) + // }); + // assert_eq!(result, Some(wallet_3)); } fn make_unconfirmed_adjustments_and_expected_test_result( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 731997452..bd8e162d9 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -21,8 +21,8 @@ impl WeightedPayable { #[derive(Debug)] pub enum AdjustmentIterationResult { AllAccountsProcessed(Vec), - SpecialTreatmentRequired { - case: RequiredSpecialTreatment, + IterationWithSpecialHandling { + case: SpecialHandling, remaining_undecided_accounts: Vec, }, } @@ -52,9 +52,9 @@ impl RecursionResults { } #[derive(Debug)] -pub enum RequiredSpecialTreatment { - TreatInsignificantAccount, - TreatOutweighedAccounts(Vec), +pub enum SpecialHandling { + InsignificantAccountEliminated, + OutweighedAccounts(Vec), } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 9ef8c47c3..76ef13273 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -17,22 +17,40 @@ mod test_utils; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, + AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, +}; +use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; +use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; +use crate::accountant::payment_adjuster::diagnostics::{collection_diagnostics, diagnostics}; +use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, }; -use crate::accountant::payment_adjuster::diagnostics::{diagnostics, collection_diagnostics}; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::log_fns::{ - accounts_before_and_after_debug, - log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, + accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, +}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ + InsignificantAccountEliminated, OutweighedAccounts, +}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, + UnconfirmedAdjustment, WeightedPayable, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::{ - TreatInsignificantAccount, TreatOutweighedAccounts, +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, + exhaust_cw_till_the_last_drop, sort_in_descendant_order_by_weights, sum_as, + zero_affordable_accounts_found, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, UnconfirmedAdjustment, WeightedPayable}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total, exhaust_cw_till_the_last_drop, adjust_account_balance_if_outweighed, drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, sum_as, compute_mul_coefficient_preventing_fractional_numbers, sort_in_descendant_order_by_weights, zero_affordable_accounts_found, find_largest_weight}; -use crate::accountant::payment_adjuster::preparatory_analyser::{PreparatoryAnalyzer}; +use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; +use crate::accountant::payment_adjuster::service_fee_adjuster::{ + ServiceFeeAdjuster, ServiceFeeAdjusterReal, +}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; +use crate::accountant::QualifiedPayableAccount; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -43,15 +61,6 @@ use std::fmt::{Display, Formatter}; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; -use masq_lib::utils::convert_collection; -use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; -use crate::accountant::payment_adjuster::criterion_calculators::{CriterionCalculator}; -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{calculated_criterion_and_weight_diagnostics}; -use crate::accountant::payment_adjuster::disqualification_arbiter::{DisqualificationArbiter, DisqualificationGauge}; -use crate::accountant::payment_adjuster::service_fee_adjuster::{ServiceFeeAdjuster, ServiceFeeAdjusterReal}; -use crate::accountant::QualifiedPayableAccount; -use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; -use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( @@ -294,9 +303,12 @@ impl PaymentAdjusterReal { ) -> Vec { let unallocated_cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); + let disqualification_arbiter = &self.disqualification_arbiter; let logger = &self.logger; + let current_iteration_result = self.service_fee_adjuster.perform_adjustment_by_service_fee( weighed_accounts, + disqualification_arbiter, unallocated_cw_service_fee_balance, logger, ); @@ -321,31 +333,26 @@ impl PaymentAdjusterReal { AdjustmentIterationResult::AllAccountsProcessed(decided_accounts) => { RecursionResults::new(decided_accounts, vec![]) } - AdjustmentIterationResult::SpecialTreatmentRequired { - case: special_case, + AdjustmentIterationResult::IterationWithSpecialHandling { + case, remaining_undecided_accounts, } => { - let here_decided_accounts = match special_case { - TreatInsignificantAccount => { + let here_decided_accounts = match case { + InsignificantAccountEliminated => { if remaining_undecided_accounts.is_empty() { - todo!("you should allow this... -> All accounts eliminated"); - - // a) only one account can be eliminated in a single iteration, - // b) if there is one last undecided account, it goes on through a shortcut, not reaching - // out down here - unreachable!("Not possible by original design") + return RecursionResults::new(vec![], vec![]); } vec![] } - TreatOutweighedAccounts(outweighed) => { + OutweighedAccounts(outweighed) => { if remaining_undecided_accounts.is_empty() { - todo!("this should be made impossible, in turn... by a short circuit within the adjustment runner"); - - debug!(self.logger, "Every account outweighed (Probably excessive funds after preceding \ - disqualification). Returning from recursion"); - - return RecursionResults::new(outweighed, vec![]); + // The only known reason for this would be an account disqualification, + // after which the unallocated cw balance begins to suffice for the rest + // of those unresolved accounts. + // Because it is definitely possible, there is a check aimed at this + // in the AdjustmentRunner's adjust_accounts() + unreachable!("This shouldn't be possible due to a preceding check"); } self.adjust_remaining_unallocated_cw_balance_down(&outweighed); @@ -401,154 +408,6 @@ impl PaymentAdjusterReal { sort_in_descendant_order_by_weights(weighted_accounts) } - // - // fn perform_adjustment_by_service_fee( - // &self, - // weighted_accounts: Vec, - // ) -> AdjustmentIterationResult { - // let non_finalized_adjusted_accounts = - // self.compute_unconfirmed_adjustments(weighted_accounts); - // - // let still_unchecked_for_disqualified = - // match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { - // Either::Left(first_check_passing_accounts) => first_check_passing_accounts, - // Either::Right(with_some_outweighed) => return with_some_outweighed, - // }; - // - // let verified_accounts = match self - // .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) - // { - // Either::Left(verified_accounts) => verified_accounts, - // Either::Right(with_some_disqualified) => return with_some_disqualified, - // }; - // - // AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) - // } - - // // TODO Should this become a helper? ...with which I can catch mid-results and assert on them? - // fn compute_unconfirmed_adjustments( - // &self, - // weighted_accounts: Vec, - // ) -> Vec { - // let weights_total = weights_total(&weighted_accounts); - // let largest_weight = find_largest_weight(&weighted_accounts); - // let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - // - // let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( - // cw_service_fee_balance, - // largest_weight, - // ); - // - // let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( - // cw_service_fee_balance, - // weights_total, - // multiplication_coefficient, - // ); - // - // let compute_proposed_adjusted_balance = - // |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; - // - // weighted_accounts - // .into_iter() - // .map(|weighted_account| { - // let proposed_adjusted_balance = - // compute_proposed_adjusted_balance(weighted_account.weight); - // - // proposed_adjusted_balance_diagnostics( - // &weighted_account.qualified_account, - // proposed_adjusted_balance, - // ); - // - // UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) - // }) - // .collect() - // } - - fn compute_proportional_cw_fragment( - cw_service_fee_balance: u128, - weights_total: u128, - multiplication_coefficient: u128, - ) -> u128 { - cw_service_fee_balance - // Considered safe due to the process of getting this coefficient - .checked_mul(multiplication_coefficient) - .unwrap_or_else(|| { - panic!( - "mul overflow from {} * {}", - weights_total, multiplication_coefficient - ) - }) - .checked_div(weights_total) - .expect("div overflow") - } - - fn consider_account_disqualification( - &self, - unconfirmed_adjustments: Vec, - logger: &Logger, - ) -> Either, AdjustmentIterationResult> { - if let Some(disqualified_account_wallet) = self - .disqualification_arbiter - .try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - logger, - ) - { - let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { - account_info - .non_finalized_account - .qualified_payable - .qualified_as - .wallet - != disqualified_account_wallet - }); - - let remaining_reverted = remaining - .map(|account_info| { - //TODO maybe implement from like before - account_info.non_finalized_account.qualified_payable - // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( - // account_info.non_finalized_account, - // AdjustmentResolution::Revert, - // )) - }) - .collect(); - - Either::Right(AdjustmentIterationResult::SpecialTreatmentRequired { - case: TreatInsignificantAccount, - remaining_undecided_accounts: remaining_reverted, - }) - } else { - Either::Left(convert_collection(unconfirmed_adjustments)) - } - } - - // The term "outweighed account" comes from a phenomenon with account weight increasing - // significantly based on a different parameter than the debt size. Untreated, we would which - // grant the account (much) more money than what the accountancy has recorded for it. - fn handle_possibly_outweighed_accounts( - &self, - unconfirmed_adjustments: Vec, - ) -> Either, AdjustmentIterationResult> { - let init = (vec![], vec![]); - - let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments - .into_iter() - .fold(init, adjust_account_balance_if_outweighed); - - if outweighed.is_empty() { - Either::Left(properly_adjusted_accounts) - } else { - let remaining_undecided_accounts: Vec = - convert_collection(properly_adjusted_accounts); - let pre_processed_outweighed: Vec = - convert_collection(outweighed); - Either::Right(AdjustmentIterationResult::SpecialTreatmentRequired { - case: TreatOutweighedAccounts(pre_processed_outweighed), - remaining_undecided_accounts, - }) - } - } fn adjust_remaining_unallocated_cw_balance_down( &mut self, @@ -699,35 +558,56 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RequiredSpecialTreatment, WeightedPayable}; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::RequiredSpecialTreatment::TreatInsignificantAccount; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{weights_total}; - use crate::accountant::payment_adjuster::test_utils::{CriterionCalculatorMock, DisqualificationGaugeMock, make_extreme_payables, make_initialized_subject, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, ServiceFeeAdjusterMock}; - use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal}; - use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account}; - use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, ResponseSkeleton}; + use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::inner::{ + PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ + InsignificantAccountEliminated, OutweighedAccounts, + }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, SpecialHandling, + WeightedPayable, + }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::service_fee_adjuster::{ + AdjustmentComputer, ServiceFeeAdjusterReal, + }; + use crate::accountant::payment_adjuster::test_utils::{ + make_extreme_payables, make_initialized_subject, make_qualified_payable_by_wallet, + multiple_by_billion, CriterionCalculatorMock, DisqualificationGaugeMock, + ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, + PRESERVED_TEST_PAYMENT_THRESHOLDS, + }; + use crate::accountant::payment_adjuster::{ + Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + }; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; + use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + make_payable_account, + }; + use crate::accountant::{ + gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, + }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; + use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use itertools::Either; + use lazy_static::lazy_static; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use std::time::{Duration, SystemTime}; - use std::{usize, vec}; + use rand::rngs::mock; use std::collections::HashMap; use std::iter::zip; use std::sync::{Arc, Mutex}; - use lazy_static::lazy_static; - use rand::rngs::mock; + use std::time::{Duration, SystemTime}; + use std::{usize, vec}; use thousands::Separable; use web3::types::U256; - use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInnerNull, PaymentAdjusterInnerReal}; - use crate::accountant::payment_adjuster::service_fee_adjuster::{AdjustmentComputer, ServiceFeeAdjusterReal}; - use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; - use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; - use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; #[test] #[should_panic(expected = "Broken code: Called the null implementation of \ @@ -966,27 +846,22 @@ mod tests { #[test] fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { - let now = SystemTime::now(); - let subject = make_initialized_subject(now, None, None); - let past = now.checked_sub(Duration::from_secs(10)).unwrap(); - let mut payable_1 = make_non_guaranteed_qualified_payable(111); - payable_1.payment_threshold_intercept_minor = 500_000_000; - payable_1.qualified_as.balance_wei = 1_000_000_002; - payable_1.qualified_as.last_paid_timestamp = past; - let mut payable_2 = make_non_guaranteed_qualified_payable(222); - payable_2.payment_threshold_intercept_minor = 500_000_000; - payable_2.qualified_as.balance_wei = 1_000_000_001; - payable_2.qualified_as.last_paid_timestamp = past; - let mut payable_3 = make_non_guaranteed_qualified_payable(333); - payable_3.payment_threshold_intercept_minor = 500_000_000; - payable_3.qualified_as.balance_wei = 1_000_000_003; - payable_3.qualified_as.last_paid_timestamp = past; - - let criteria_and_accounts = subject.calculate_weights_for_accounts(vec![ - payable_1.clone(), - payable_2.clone(), - payable_3.clone(), - ]); + let calculator = CriterionCalculatorMock::default() + .calculate_result(1_000_000_002) + .calculate_result(1_000_000_001) + .calculate_result(1_000_000_003); + let subject = make_initialized_subject(None, None, Some(calculator), None); + let make_account = |n: u64| { + let account = make_non_guaranteed_qualified_payable(n); + let wallet = account.qualified_as.wallet.clone(); + (wallet, account) + }; + let (wallet_1, payable_1) = make_account(111); + let (wallet_2, payable_2) = make_account(222); + let (wallet_3, payable_3) = make_account(333); + + let criteria_and_accounts = + subject.calculate_weights_for_accounts(vec![payable_1, payable_2, payable_3]); let mut previous_weight = u128::MAX; let accounts_alone = criteria_and_accounts @@ -999,54 +874,43 @@ mod tests { weighted_account.weight ); previous_weight = weighted_account.weight; - weighted_account.qualified_account.qualified_as + weighted_account.qualified_account.qualified_as.wallet }) - .collect::>(); - assert_eq!( - accounts_alone, - vec![ - payable_3.qualified_as, - payable_1.qualified_as, - payable_2.qualified_as - ] - ) + .collect::>(); + assert_eq!(accounts_alone, vec![wallet_3, wallet_1, wallet_2]) } #[test] - fn smaller_but_more_criteria_gaining_account_is_prioritized_outweighed_up_to_original_balance() - { + fn tinier_but_larger_in_weight_account_is_prioritized_outweighed_up_to_its_original_balance() { let now = SystemTime::now(); - let cw_service_fee_balance_minor = gwei_to_wei::(3_500_000); - let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); - let mock_calculator = CriterionCalculatorMock::default() + let cw_service_fee_balance_minor = multiple_by_billion(3_500_000); + let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); + let calculator_mock = CriterionCalculatorMock::default() // Account 1, first iteration - .calculate_result(0) + .calculate_result(multiple_by_billion(2_000_100)) // Account 2 in its only iteration, after which it is found outweighed and handled // as a priority - .calculate_result(2_000_000_000_000_000) + .calculate_result(multiple_by_billion(3_999_900)) // Account 1, second iteration - .calculate_result(0); - subject.calculators.push(Box::new(mock_calculator)); - let balance_1 = gwei_to_wei::(2_000_100); - let balance_2 = gwei_to_wei::(1_999_900); - let account_1 = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(200_001)).unwrap(), - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(200_000)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payables = make_guaranteed_qualified_payables( - vec![account_1.clone(), account_2.clone()], - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, + .calculate_result(multiple_by_billion(2_000_100)); + let mut subject = make_initialized_subject( + None, + Some(cw_service_fee_balance_minor), + Some(calculator_mock), + None, ); + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(cw_service_fee_balance_minor / 2) + .determine_limit_params(&determine_limit_params_arc); + subject.disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let mut account_1 = make_qualified_payable_by_wallet("abc"); + account_1.qualified_as.balance_wei = multiple_by_billion(3_000_000); + let mut account_2 = make_qualified_payable_by_wallet("def"); + let wallet_2 = account_2.qualified_as.wallet.clone(); + let balance_2 = multiple_by_billion(1_000_000); + account_2.qualified_as.balance_wei = balance_2; + let qualified_payables = vec![account_1, account_2]; let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( @@ -1060,17 +924,16 @@ mod tests { // Let's have an example to explain why this test is important. // First, the mock must be renewed; the available cw balance updated to the original value. let mock_calculator = CriterionCalculatorMock::default() - .calculate_result(0) - .calculate_result(2_000_000_000_000_000); - prove_hypothesis_of_proposed_adjusted_balance_be_outweighed( + .calculate_result(multiple_by_billion(2_000_100)) + .calculate_result(multiple_by_billion(3_999_900)); + subject.calculators = vec![Box::new(mock_calculator)]; + prove_that_proposed_adjusted_balance_would_exceed_the_original_value( subject, - now, cw_service_fee_balance_minor, - mock_calculator, qualified_payables.clone(), wallet_2, balance_2, - 1.5, + 2.3, ); // // So the assertion above showed the concern true. let first_returned_account = result.remove(0); @@ -1090,25 +953,21 @@ mod tests { ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 1_500_099_999_999_999 + 2499999999999999 ); assert!(result.is_empty()); } - fn prove_hypothesis_of_proposed_adjusted_balance_be_outweighed( + fn prove_that_proposed_adjusted_balance_would_exceed_the_original_value( mut subject: PaymentAdjusterReal, - now: SystemTime, cw_service_fee_balance_minor: u128, - mock_calculator: CriterionCalculatorMock, accounts: Vec, wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, outweighed_by_multiple_of: f64, ) { - subject.calculators.pop().unwrap(); - subject.calculators.push(Box::new(mock_calculator)); subject.inner = Box::new(PaymentAdjusterInnerReal::new( - now, + SystemTime::now(), None, cw_service_fee_balance_minor, )); @@ -1147,22 +1006,25 @@ mod tests { fn adjustment_started_but_all_accounts_were_eliminated_anyway() { let test_name = "adjustment_started_but_all_accounts_were_eliminated_anyway"; let now = SystemTime::now(); + let balance_1 = multiple_by_billion(3_000_000); let account_1 = PayableAccount { wallet: make_wallet("abc"), - balance_wei: gwei_to_wei::(3_000_000), + balance_wei: balance_1, last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; + let balance_2 = multiple_by_billion(2_000_000); let account_2 = PayableAccount { wallet: make_wallet("def"), - balance_wei: gwei_to_wei::(1_000_000), - last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), + balance_wei: balance_2, + last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), pending_payable_opt: None, }; + let balance_3 = multiple_by_billion(5_000_000); let account_3 = PayableAccount { wallet: make_wallet("ghi"), - balance_wei: gwei_to_wei::(2_000_000), - last_paid_timestamp: now.checked_sub(Duration::from_secs(60_000)).unwrap(), + balance_wei: balance_3, + last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), pending_payable_opt: None, }; let payables = vec![account_1, account_2, account_3]; @@ -1171,11 +1033,16 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let service_fee_balance_in_minor_units = todo!(); - // ((1_000_000_000_000 - // * EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.multiplier) - // / EXCESSIVE_DEBT_PART_INSIGNIFICANCE_RATIO.divisor) - // - 1; + let service_fee_balance_in_minor_units = balance_2; + let disqualification_arbiter = &subject.disqualification_arbiter; + let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( + disqualification_arbiter, + Either::Left(&qualified_payables), + service_fee_balance_in_minor_units, + &subject.logger, + ); + // If concluded that it has no point going off at all we would get an error here + assert_eq!(analysis_result, Ok(true)); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1200,12 +1067,15 @@ mod tests { #[test] #[should_panic( - expected = "internal error: entered unreachable code: Not possible by original design" + expected = "internal error: entered unreachable code: This shouldn't be possible due to a preceding check" )] - fn disqualified_account_with_no_remaining_accounts_is_not_possible() { + fn outweighed_accounts_with_no_remaining_accounts_is_not_possible() { let mut subject = PaymentAdjusterReal::new(); - let iteration_result = AdjustmentIterationResult::SpecialTreatmentRequired { - case: RequiredSpecialTreatment::TreatInsignificantAccount, + let iteration_result = AdjustmentIterationResult::IterationWithSpecialHandling { + case: OutweighedAccounts(vec![AdjustedAccountBeforeFinalization::new( + make_qualified_payable_by_wallet("abc"), + 123456, + )]), remaining_undecided_accounts: vec![], }; @@ -2445,7 +2315,8 @@ mod tests { now: SystemTime, cw_service_fee_balance_minor: u128, ) -> Vec { - let mut subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None); + let mut subject = + make_initialized_subject(Some(now), Some(cw_service_fee_balance_minor), None, None); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); let service_fee_adjuster_mock = ServiceFeeAdjusterMock::default() // We use this container to intercept those values we are after diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 7f2c4caa5..2c5ba4711 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,14 +1,28 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::proposed_adjusted_balance_diagnostics; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ + InsignificantAccountEliminated, OutweighedAccounts, +}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, + WeightedPayable, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, + find_largest_weight, weights_total, }; +use crate::accountant::QualifiedPayableAccount; +use itertools::Either; use masq_lib::logger::Logger; +use masq_lib::utils::convert_collection; pub trait ServiceFeeAdjuster { fn perform_adjustment_by_service_fee( &self, weighted_accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult; @@ -22,10 +36,30 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { fn perform_adjustment_by_service_fee( &self, weighted_accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - todo!() + let non_finalized_adjusted_accounts = self + .adjustment_computer + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + + let still_unchecked_for_disqualified = + match Self::handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { + Either::Left(first_check_passing_accounts) => first_check_passing_accounts, + Either::Right(with_some_outweighed) => return with_some_outweighed, + }; + + let verified_accounts = match Self::consider_account_disqualification( + disqualification_arbiter, + still_unchecked_for_disqualified, + logger, + ) { + Either::Left(verified_accounts) => verified_accounts, + Either::Right(with_some_disqualified) => return with_some_disqualified, + }; + + AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) } } @@ -41,6 +75,72 @@ impl ServiceFeeAdjusterReal { adjustment_computer: Default::default(), } } + + // The term "outweighed account" comes from a phenomenon with account weight increasing + // significantly based on a different parameter than the debt size. Untreated, we would which + // grant the account (much) more money than what the accountancy has recorded for it. + fn handle_possibly_outweighed_accounts( + unconfirmed_adjustments: Vec, + ) -> Either, AdjustmentIterationResult> { + let init = (vec![], vec![]); + + let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments + .into_iter() + .fold(init, adjust_account_balance_if_outweighed); + + if outweighed.is_empty() { + Either::Left(properly_adjusted_accounts) + } else { + let remaining_undecided_accounts: Vec = + convert_collection(properly_adjusted_accounts); + let pre_processed_outweighed: Vec = + convert_collection(outweighed); + Either::Right(AdjustmentIterationResult::IterationWithSpecialHandling { + case: OutweighedAccounts(pre_processed_outweighed), + remaining_undecided_accounts, + }) + } + } + + fn consider_account_disqualification( + disqualification_arbiter: &DisqualificationArbiter, + unconfirmed_adjustments: Vec, + logger: &Logger, + ) -> Either, AdjustmentIterationResult> { + if let Some(disqualified_account_wallet) = disqualification_arbiter + .try_finding_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + logger, + ) + { + let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { + account_info + .non_finalized_account + .qualified_payable + .qualified_as + .wallet + != disqualified_account_wallet + }); + + let remaining_reverted = remaining + .map(|account_info| { + //TODO maybe implement from like before + account_info.non_finalized_account.qualified_payable + // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( + // account_info.non_finalized_account, + // AdjustmentResolution::Revert, + // )) + }) + .collect(); + + Either::Right(AdjustmentIterationResult::IterationWithSpecialHandling { + case: InsignificantAccountEliminated, + remaining_undecided_accounts: remaining_reverted, + }) + } else { + Either::Left(convert_collection(unconfirmed_adjustments)) + } + } } #[derive(Default)] @@ -52,7 +152,55 @@ impl AdjustmentComputer { weighted_accounts: Vec, unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { - todo!() + let weights_total = weights_total(&weighted_accounts); + let largest_weight = find_largest_weight(&weighted_accounts); + let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; + + let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( + cw_service_fee_balance, + largest_weight, + ); + + let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( + cw_service_fee_balance, + weights_total, + multiplication_coefficient, + ); + let compute_proposed_adjusted_balance = + |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; + + weighted_accounts + .into_iter() + .map(|weighted_account| { + let proposed_adjusted_balance = + compute_proposed_adjusted_balance(weighted_account.weight); + + proposed_adjusted_balance_diagnostics( + &weighted_account.qualified_account, + proposed_adjusted_balance, + ); + + UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) + }) + .collect() + } + + fn compute_proportional_cw_fragment( + cw_service_fee_balance: u128, + weights_total: u128, + multiplication_coefficient: u128, + ) -> u128 { + cw_service_fee_balance + // Considered safe due to the process of getting this coefficient + .checked_mul(multiplication_coefficient) + .unwrap_or_else(|| { + panic!( + "mul overflow from {} * {}", + weights_total, multiplication_coefficient + ) + }) + .checked_div(weights_total) + .expect("div overflow") } } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index c94c98c18..d6103c567 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -4,7 +4,9 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; -use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationGauge; +use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, +}; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, @@ -13,8 +15,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{gwei_to_wei, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; +use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; use lazy_static::lazy_static; @@ -31,19 +34,23 @@ lazy_static! { } pub fn make_initialized_subject( - now: SystemTime, + now_opt: Option, cw_service_fee_balance_minor_opt: Option, + criterion_calculator_mock_opt: Option, logger_opt: Option, ) -> PaymentAdjusterReal { - let cw_masq_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); + let cw_service_fee_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); let logger = logger_opt.unwrap_or(Logger::new("test")); let mut subject = PaymentAdjusterReal::default(); subject.logger = logger; subject.inner = Box::new(PaymentAdjusterInnerReal::new( - now, + now_opt.unwrap_or(SystemTime::now()), None, - cw_masq_balance_minor, + cw_service_fee_balance_minor, )); + if let Some(calculator) = criterion_calculator_mock_opt { + subject.calculators = vec![Box::new(calculator)] + } subject } @@ -196,6 +203,7 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { fn perform_adjustment_by_service_fee( &self, weighted_accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, _logger: &Logger, ) -> AdjustmentIterationResult { @@ -228,3 +236,15 @@ impl ServiceFeeAdjusterMock { self } } + +pub fn multiple_by_billion(num: u128) -> u128 { + num * 10_u128.pow(9) +} + +pub fn make_qualified_payable_by_wallet(wallet_address_segment: &str) -> QualifiedPayableAccount { + let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); + let wallet = make_wallet(wallet_address_segment); + let mut account = make_non_guaranteed_qualified_payable(num); + account.qualified_as.wallet = wallet; + account +} From e479e7ffe872ae2068916abd81b4179322b047e3 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 8 Apr 2024 11:56:24 +0200 Subject: [PATCH 152/250] GH-711-b: improving loading tests and one more unit test fixed --- .../payment_adjuster/loading_test/mod.rs | 73 ++++++++++++------- node/src/accountant/payment_adjuster/mod.rs | 34 ++++++--- node/src/accountant/test_utils.rs | 44 +++++++---- node/src/sub_lib/blockchain_bridge.rs | 4 +- 4 files changed, 100 insertions(+), 55 deletions(-) diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index a728f0d61..14abede25 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -1,7 +1,5 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -#![cfg(feature = "occasional_test")] - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; @@ -23,6 +21,9 @@ use std::io::Write; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; +use crate::accountant::QualifiedPayableAccount; +use crate::accountant::test_utils::{make_guaranteed_qualified_payables, try_making_guaranteed_qualified_payables}; #[test] fn loading_test_with_randomized_params() { @@ -40,7 +41,7 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 500; + let number_of_requested_scenarios = 50; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let test_overall_output_collector = TestOverallOutputCollector::default(); @@ -56,13 +57,17 @@ fn loading_test_with_randomized_params() { let first_stage_output = scenarios .into_iter() .fold(init, |mut output_collector, scenario|{ - // We watch only the service fee balance check, transaction fee can be added but it doesn't - // interact with the potential error 'AllAccountsEliminated' whose occurrence rate is interesting - // compared to the number of cases the initial check let the adjustment procedure go on - let initial_check_result = subject.search_for_indispensable_adjustment(&scenario.qualified_payables, &*scenario.agent); + // We watch only the service fee balance check, transaction fee can be added, but it + // doesn't interact with the potential error 'AllAccountsEliminated' whose occurrence + // rate is interesting compared to how many times the initial check lets the adjustment + // procedure go on + let initial_check_result = + subject + .search_for_indispensable_adjustment(&scenario.qualified_payables, &*scenario.agent); let allowed_scenario_opt = match initial_check_result{ Ok(adjustment_opt) => {match adjustment_opt{ - None => panic!("Wrong test setup. This test is designed to generate scenarios with balances always insufficient in some way!"), + None => panic!("Wrong test setup. This test is designed to generate scenarios \ + with balances always insufficient in some way!"), Some(_) => () }; Some(scenario)} @@ -116,18 +121,23 @@ fn generate_scenarios( number_of_scenarios: usize, ) -> Vec { (0..number_of_scenarios) - .map(|_| make_single_scenario(gn, now)) + .flat_map(|_| try_making_single_valid_scenario(gn, now)) .collect() } -fn make_single_scenario(gn: &mut ThreadRng, now: SystemTime) -> PreparedAdjustment { - let (cw_service_fee_balance, qualified_payables) = make_qualified_payables(gn, now); +fn try_making_single_valid_scenario(gn: &mut ThreadRng, now: SystemTime) -> Option { + let (cw_service_fee_balance, payables) = make_payables(gn, now); + let payables_len = payables.len(); + let qualified_payables = try_making_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS,now, false); + if payables_len != qualified_payables.len(){ + return None + } let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, qualified_payables.len()); - PreparedAdjustment::new(qualified_payables, Box::new(agent), None, adjustment) + Some(PreparedAdjustment::new(qualified_payables, Box::new(agent), None, adjustment)) } -fn make_qualified_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { +fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { let accounts_count = generate_non_zero_usize(gn, 20) + 1; let accounts = (0..accounts_count) .map(|idx| { @@ -241,14 +251,14 @@ struct FailedAdjustment { adjuster_error: PaymentAdjusterError, } -fn preserve_account_infos(accounts: &[PayableAccount], now: SystemTime) -> Vec { +fn preserve_account_infos(accounts: &[QualifiedPayableAccount], now: SystemTime) -> Vec { accounts .iter() .map(|account| AccountInfo { - wallet: account.wallet.clone(), - initially_requested_service_fee_minor: account.balance_wei, + wallet: account.qualified_as.wallet.clone(), + initially_requested_service_fee_minor: account.qualified_as.balance_wei, debt_age_s: now - .duration_since(account.last_paid_timestamp) + .duration_since(account.qualified_as.last_paid_timestamp) .unwrap() .as_secs(), }) @@ -273,9 +283,10 @@ fn render_results_to_file_and_attempt_basic_assertions( + test_overall_output_collector.oks + test_overall_output_collector.all_accounts_eliminated + test_overall_output_collector.insufficient_service_fee_balance; - write_in_test_overall_output_to_file( + write_brief_test_summary_into_file( &mut file, &test_overall_output_collector, + number_of_requested_scenarios, total_scenarios_evaluated, ); @@ -291,7 +302,9 @@ fn render_results_to_file_and_attempt_basic_assertions( - ((test_overall_output_collector.scenarios_eliminated_before_adjustment_started * 100) / total_scenarios_evaluated); let required_pass_rate = 80; - assert!(entry_check_pass_rate >= required_pass_rate, "Not at least {}% from {} the scenarios generated for this test allows PaymentAdjuster to continue doing its job and ends too early. Instead only {}%. Setup of the test might be needed",required_pass_rate, total_scenarios_evaluated, entry_check_pass_rate); + assert!(entry_check_pass_rate >= required_pass_rate, "Not at least {}% from {} the scenarios \ + generated for this test allows PaymentAdjuster to continue doing its job and ends too early. \ + Instead only {}%. Setup of the test might be needed",required_pass_rate, total_scenarios_evaluated, entry_check_pass_rate); let ok_adjustment_percentage = (test_overall_output_collector.oks * 100) / (total_scenarios_evaluated - test_overall_output_collector.scenarios_eliminated_before_adjustment_started); @@ -308,25 +321,29 @@ fn render_results_to_file_and_attempt_basic_assertions( fn introduction(file: &mut File) { write_thick_dividing_line(file); write_thick_dividing_line(file); - file.write(b"For a brief overview of this formatted test output look at the end\n") + file.write(b"You may proceed to a brief summary at the tail of this test output\n") .unwrap(); write_thick_dividing_line(file); write_thick_dividing_line(file) } -fn write_in_test_overall_output_to_file( +fn write_brief_test_summary_into_file( file: &mut File, test_overall_output_collector: &TestOverallOutputCollector, - total_of_scenarios_eliminated: usize, + number_of_requested_scenarios: usize, + total_of_scenarios_evaluated: usize, ) { write_thick_dividing_line(file); file.write_fmt(format_args!( - "Total scenarios generated: {}\n\ - Scenarios caught by the entry check: {}\n\ - Ok scenarios: {}\n\ - Scenarios with 'AllAccountsEliminated': {}\n\ - Scenarios with late insufficient balance errors: {}", - total_of_scenarios_eliminated, + "Scenarios \n\ + Requested: {}\n\ + Actually evaluated: {}\n\ + Caught by the entry check: {}\n\ + Successful: {}\n\ + With 'AllAccountsEliminated': {}\n\ + With late insufficient balance errors: {}", + number_of_requested_scenarios, + total_of_scenarios_evaluated, test_overall_output_collector.scenarios_eliminated_before_adjustment_started, test_overall_output_collector.oks, test_overall_output_collector.all_accounts_eliminated, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 76ef13273..78c185360 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -905,7 +905,10 @@ mod tests { subject.disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let mut account_1 = make_qualified_payable_by_wallet("abc"); - account_1.qualified_as.balance_wei = multiple_by_billion(3_000_000); + let balance_1 = multiple_by_billion(3_000_000); + account_1.qualified_as.balance_wei = balance_1; + let threshold_intercept_minor = account_1.payment_threshold_intercept_minor; + let permanent_debt_allowed_minor = account_1.creditor_thresholds.permanent_debt_allowed_wei; let mut account_2 = make_qualified_payable_by_wallet("def"); let wallet_2 = account_2.qualified_as.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); @@ -956,6 +959,8 @@ mod tests { 2499999999999999 ); assert!(result.is_empty()); + let determine_limit_params= determine_limit_params_arc.lock().unwrap(); + assert_eq!(*determine_limit_params, vec![(balance_1, threshold_intercept_minor, permanent_debt_allowed_minor)]) } fn prove_that_proposed_adjusted_balance_would_exceed_the_original_value( @@ -1041,7 +1046,8 @@ mod tests { service_fee_balance_in_minor_units, &subject.logger, ); - // If concluded that it has no point going off at all we would get an error here + // If concluded at the entry into the PaymentAdjuster that it has no point going off + // because away the least demanding account cannot be satisfied we would get an error here assert_eq!(analysis_result, Ok(true)); let agent = { let mock = BlockchainAgentMock::default() @@ -1083,26 +1089,29 @@ mod tests { } #[test] - fn account_disqualification_causes_all_other_accounts_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them( + fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them( ) { + // Tests that a condition to short-circuit through is integrated for situations when + // a disqualification frees means for other accounts and there is suddenly more to give + // than how much the remaining accounts demand init_test_logging(); - let test_name = "account_disqualification_causes_all_other_accounts_to_seem_outweighed_as_cw_balance_becomes_excessive_for_them"; + let test_name = "account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); - let balance_1 = 80_000_000_000_000_000_000; + let balance_1 = multiple_by_billion(80_000_000_000); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, last_paid_timestamp: now.checked_sub(Duration::from_secs(24_000)).unwrap(), pending_payable_opt: None, }; - let balance_2 = 60_000_000_000_000_000_000; + let balance_2 = multiple_by_billion(60_000_000_000); let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, last_paid_timestamp: now.checked_sub(Duration::from_secs(200_000)).unwrap(), pending_payable_opt: None, }; - let balance_3 = 40_000_000_000_000_000_000; + let balance_3 = multiple_by_billion(40_000_000_000); let account_3 = PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, @@ -1112,7 +1121,12 @@ mod tests { let payables = vec![account_1, account_2.clone(), account_3.clone()]; let qualified_payables = make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(0) + .calculate_result(multiple_by_billion(30_000_000_000)) + .calculate_result(multiple_by_billion(30_000_000_000)); let mut subject = PaymentAdjusterReal::new(); + subject.calculators.push(Box::new(calculator_mock)); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); let accounts_sum: u128 = balance_1 + balance_2 + balance_3; @@ -1135,11 +1149,7 @@ mod tests { let expected_affordable_accounts = { vec![account_2, account_3] }; assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); - assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {test_name}: Every account outweighed (Probably \ - excessive funds after preceding disqualification). Returning from recursion" - )); + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp) } fn prepare_subject( diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 35c9a9c86..e67d514c5 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1700,6 +1700,15 @@ pub fn make_guaranteed_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, now: SystemTime, +) -> Vec { + try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) +} + +pub fn try_making_guaranteed_qualified_payables( + payables: Vec, + payment_thresholds: &PaymentThresholds, + now: SystemTime, + should_panic: bool, ) -> Vec { fn panic( payable: &PayableAccount, @@ -1707,7 +1716,7 @@ pub fn make_guaranteed_qualified_payables( now: SystemTime, ) -> ! { panic!( - "You intend to create qualified payables but their parameters not always make them qualify \ + "You intend to create qualified payables but their parameters not always make them qualify \ as in: {:?} where the balance needs to get over {}", payable, PayableThresholdsGaugeReal::default().calculate_payout_threshold_in_gwei( @@ -1720,17 +1729,26 @@ pub fn make_guaranteed_qualified_payables( let payable_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); payables .into_iter() - .map(|payable| { - let payment_threshold_intercept = payable_inspector - .payable_exceeded_threshold(&payable, payment_thresholds, now) - .unwrap_or_else(|| panic(&payable, payment_thresholds, now)); - QualifiedPayableAccount::new( - payable, - payment_threshold_intercept, - CreditorThresholds::new(gwei_to_wei( - payment_thresholds.permanent_debt_allowed_gwei, - )), - ) - }) + .flat_map(|payable| + match payable_inspector + .payable_exceeded_threshold(&payable, payment_thresholds, now) { + Some(payment_threshold_intercept) => { + Some(QualifiedPayableAccount::new( + payable, + payment_threshold_intercept, + CreditorThresholds::new(gwei_to_wei( + payment_thresholds.permanent_debt_allowed_gwei, + )), + )) + }, + None => if should_panic { + panic(&payable, &payment_thresholds, now) + } else { + None + } + } + ) .collect() } + + diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index ade747f6a..4ebaa0390 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -57,8 +57,8 @@ impl OutboundPaymentsInstructions { response_skeleton_opt: Option, ) -> Self { let affordable_accounts = match accounts { - Either::Left(qualified_account) => convert_collection(qualified_account), - Either::Right(adjusted_account) => todo!(), + Either::Left(qualified_accounts) => convert_collection(qualified_accounts), + Either::Right(adjusted_accounts) => adjusted_accounts, }; Self { From dd305a464e09ee53fbf721a5b6b7b6f6370593a0 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 8 Apr 2024 21:25:53 +0200 Subject: [PATCH 153/250] GH-711-b: recomputing the weights in every iteration not needed anymore --- node/src/accountant/mod.rs | 8 +- .../payment_adjuster/adjustment_runners.rs | 74 ++++--- .../balance_and_age_calculator.rs | 10 +- .../payment_adjuster/diagnostics.rs | 38 ++-- .../disqualification_arbiter.rs | 64 +++--- .../payment_adjuster/loading_test/mod.rs | 127 +++++++---- .../accountant/payment_adjuster/log_fns.rs | 20 +- .../miscellaneous/data_structures.rs | 35 ++- .../miscellaneous/helper_functions.rs | 102 ++++----- node/src/accountant/payment_adjuster/mod.rs | 209 ++++++++---------- .../payment_adjuster/preparatory_analyser.rs | 22 +- .../payment_adjuster/service_fee_adjuster.rs | 10 +- .../accountant/payment_adjuster/test_utils.rs | 12 +- node/src/accountant/scanners/mod.rs | 2 +- .../src/accountant/scanners/scanners_utils.rs | 6 +- node/src/accountant/test_utils.rs | 37 ++-- 16 files changed, 383 insertions(+), 393 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 1cc6b9ef0..a78ddcac1 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -131,7 +131,7 @@ pub struct ReceivedPayments { #[derive(Debug, PartialEq, Eq, Clone)] pub struct QualifiedPayableAccount { - pub qualified_as: PayableAccount, + pub bare_account: PayableAccount, pub payment_threshold_intercept_minor: u128, pub creditor_thresholds: CreditorThresholds, } @@ -143,7 +143,7 @@ impl QualifiedPayableAccount { creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { Self { - qualified_as, + bare_account: qualified_as, payment_threshold_intercept_minor, creditor_thresholds, } @@ -1545,11 +1545,11 @@ mod tests { ); let adjusted_account_1 = PayableAccount { balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1.qualified_as.clone() + ..unadjusted_account_1.bare_account.clone() }; let adjusted_account_2 = PayableAccount { balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2.qualified_as.clone() + ..unadjusted_account_2.bare_account.clone() }; let response_skeleton = ResponseSkeleton { client_id: 12, diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index fe77c70c1..f50857cfd 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -101,7 +101,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { weighted_accounts: Vec, ) -> Self::ReturnType { let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.qualified_account.qualified_as.balance_wei + weighted_account.qualified_account.bare_account.balance_wei }); let unallocated_cw_balance = payment_adjuster @@ -157,28 +157,29 @@ mod tests { AR: AdjustmentRunner, RT: Debug + PartialEq, { - let now = SystemTime::now(); - let wallet = make_wallet("abc"); - let mut qualified_payable = make_non_guaranteed_qualified_payable(111); - qualified_payable.qualified_as.balance_wei = 9_000_000_000; - qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; - qualified_payable - .creditor_thresholds - .permanent_debt_allowed_wei = 2_000_000_000; - let cw_balance = 8_645_123_505; - let adjustment = Adjustment::ByServiceFee; - let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); - - let result = subject.adjust_last_one(&mut payment_adjuster, qualified_payable.clone()); - - assert_eq!( - result, - expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { - qualified_payable: qualified_payable, - proposed_adjusted_balance_minor: cw_balance, - }]) - ) + todo!() + // let now = SystemTime::now(); + // let wallet = make_wallet("abc"); + // let mut qualified_payable = make_non_guaranteed_qualified_payable(111); + // qualified_payable.bare_account.balance_wei = 9_000_000_000; + // qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; + // qualified_payable + // .creditor_thresholds + // .permanent_debt_allowed_wei = 2_000_000_000; + // let cw_balance = 8_645_123_505; + // let adjustment = Adjustment::ByServiceFee; + // let mut payment_adjuster = PaymentAdjusterReal::new(); + // payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); + // + // let result = subject.adjust_last_one(&mut payment_adjuster, qualified_payable.clone()); + // + // assert_eq!( + // result, + // expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { + // weighted_account: qualified_payable, + // proposed_adjusted_balance_minor: cw_balance, + // }]) + // ) } #[test] @@ -208,7 +209,7 @@ mod tests { qualified_account: make_non_guaranteed_qualified_payable(111), weight: n as u128 * 1234, }; - payable.qualified_account.qualified_as.balance_wei = initial_balance_minor; + payable.qualified_account.bare_account.balance_wei = initial_balance_minor; payable } @@ -221,8 +222,8 @@ mod tests { // wonder why the implied surplus may happen let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor); - let initial_balance_minor_1 = payable_1.qualified_account.qualified_as.balance_wei; - let initial_balance_minor_2 = payable_2.qualified_account.qualified_as.balance_wei; + let initial_balance_minor_1 = payable_1.qualified_account.bare_account.balance_wei; + let initial_balance_minor_2 = payable_2.qualified_account.bare_account.balance_wei; let subject = ServiceFeeOnlyAdjustmentRunner {}; let result = subject.adjust_accounts( @@ -234,11 +235,11 @@ mod tests { result, vec![ AdjustedAccountBeforeFinalization { - qualified_payable: payable_1.qualified_account, + original_account: payable_1.qualified_account.bare_account, proposed_adjusted_balance_minor: initial_balance_minor_1 }, AdjustedAccountBeforeFinalization { - qualified_payable: payable_2.qualified_account, + original_account: payable_2.qualified_account.bare_account, proposed_adjusted_balance_minor: initial_balance_minor_2 } ] @@ -308,7 +309,7 @@ mod tests { let returned_accounts = result .into_iter() - .map(|account| account.qualified_payable.qualified_as.wallet) + .map(|account| account.original_account.wallet) .collect::>(); assert_eq!(returned_accounts, vec![wallet_1, wallet_2]) // If the transaction fee adjustment had been available to be performed, only one account @@ -324,12 +325,13 @@ mod tests { #[test] fn empty_or_single_element_vector_for_some() { - let account_info = AdjustedAccountBeforeFinalization { - qualified_payable: make_non_guaranteed_qualified_payable(123), - proposed_adjusted_balance_minor: 123_456_789, - }; - let result = empty_or_single_element_vector(Some(account_info.clone())); - - assert_eq!(result, vec![account_info]) + todo!() + // let account_info = AdjustedAccountBeforeFinalization { + // weighted_account: make_non_guaranteed_qualified_payable(123), + // proposed_adjusted_balance_minor: 123_456_789, + // }; + // let result = empty_or_single_element_vector(Some(account_info.clone())); + // + // assert_eq!(result, vec![account_info]) } } diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 08e6d5128..3bd58665d 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -16,15 +16,15 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { let now = context.now(); let debt_age_s = now - .duration_since(account.qualified_as.last_paid_timestamp) + .duration_since(account.bare_account.last_paid_timestamp) .expect("time traveller") .as_secs(); eprintln!( "{} - {}", - account.qualified_as.balance_wei, account.payment_threshold_intercept_minor + account.bare_account.balance_wei, account.payment_threshold_intercept_minor ); - account.qualified_as.balance_wei - account.payment_threshold_intercept_minor + account.bare_account.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128 } @@ -70,10 +70,10 @@ mod tests { .zip(computed_criteria.into_iter()); zipped.into_iter().for_each(|(account, actual_criterion)| { let debt_age_s = now - .duration_since(account.qualified_as.last_paid_timestamp) + .duration_since(account.bare_account.last_paid_timestamp) .unwrap() .as_secs(); - let expected_criterion = account.qualified_as.balance_wei + let expected_criterion = account.bare_account.balance_wei - account.payment_threshold_intercept_minor + debt_age_s as u128; assert_eq!(actual_criterion, expected_criterion) diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index 412951afa..fc54d1936 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -100,16 +100,19 @@ pub mod ordinary_diagnostic_functions { use crate::sub_lib::wallet::Wallet; use thousands::Separable; - pub fn possibly_outweighed_accounts_diagnostics( - account_info: &AdjustedAccountBeforeFinalization, - ) { + pub fn possibly_outweighed_accounts_diagnostics(account_info: &UnconfirmedAdjustment) { diagnostics!( - &account_info.qualified_payable.qualified_as.wallet, + &account_info + .weighted_account + .qualified_account + .bare_account + .wallet, "OUTWEIGHED ACCOUNT FOUND", "Original balance: {}, proposed balance: {}", account_info - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .balance_wei .separate_with_commas(), account_info @@ -125,9 +128,9 @@ pub mod ordinary_diagnostic_functions { ) { diagnostics!( account_info - .non_finalized_account - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .wallet, "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", "Proposed: {}, disqualification limit: {}", @@ -143,10 +146,7 @@ pub mod ordinary_diagnostic_functions { diagnostics!( "EXHAUSTING CW ON PAYMENT", "For account {} from proposed {} to the possible maximum of {}", - non_finalized_account_info - .qualified_payable - .qualified_as - .wallet, + non_finalized_account_info.original_account.wallet, non_finalized_account_info.proposed_adjusted_balance_minor, non_finalized_account_info.proposed_adjusted_balance_minor + possible_extra_addition ); @@ -158,14 +158,8 @@ pub mod ordinary_diagnostic_functions { diagnostics!( "FULLY EXHAUSTED CW, PASSING ACCOUNT OVER", "Account {} with original balance {} must be finalized with proposed {}", - non_finalized_account_info - .qualified_payable - .qualified_as - .wallet, - non_finalized_account_info - .qualified_payable - .qualified_as - .balance_wei, + non_finalized_account_info.original_account.wallet, + non_finalized_account_info.original_account.balance_wei, non_finalized_account_info.proposed_adjusted_balance_minor ); } @@ -175,7 +169,7 @@ pub mod ordinary_diagnostic_functions { proposed_adjusted_balance: u128, ) { diagnostics!( - &account.qualified_as.wallet, + &account.bare_account.wallet, "PROPOSED ADJUSTED BALANCE", "{}", proposed_adjusted_balance.separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 95695d45a..1de1af876 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -6,7 +6,7 @@ use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functi }; use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; @@ -42,8 +42,9 @@ impl DisqualificationArbiter { Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); let wallet = account_to_disqualify - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .wallet .clone(); @@ -74,7 +75,7 @@ impl DisqualificationArbiter { qualified_payable: &QualifiedPayableAccount, ) -> u128 { self.disqualification_gauge.determine_limit( - qualified_payable.qualified_as.balance_wei, + qualified_payable.bare_account.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable .creditor_thresholds @@ -90,11 +91,9 @@ impl DisqualificationArbiter { .iter() .flat_map(|adjustment_info| { let disqualification_edge = self.calculate_disqualification_edge( - &adjustment_info.non_finalized_account.qualified_payable, + &adjustment_info.weighted_account.qualified_account, ); - let proposed_adjusted_balance = adjustment_info - .non_finalized_account - .proposed_adjusted_balance_minor; + let proposed_adjusted_balance = adjustment_info.proposed_adjusted_balance_minor; if proposed_adjusted_balance < disqualification_edge { account_nominated_for_disqualification_diagnostics( @@ -112,22 +111,19 @@ impl DisqualificationArbiter { fn find_account_with_smallest_weight<'account>( accounts: &'account [&'account UnconfirmedAdjustment], - ) -> &'account AdjustedAccountBeforeFinalization { + ) -> &'account UnconfirmedAdjustment { let first_account = &accounts.first().expect("collection was empty"); - &accounts - .iter() - .fold( - **first_account, - |with_smallest_weight_so_far, current| match Ord::cmp( - ¤t.weight, - &with_smallest_weight_so_far.weight, - ) { - Ordering::Less => current, - Ordering::Greater => with_smallest_weight_so_far, - Ordering::Equal => with_smallest_weight_so_far, - }, - ) - .non_finalized_account + accounts.iter().fold( + **first_account, + |with_smallest_weight_so_far, current| match Ord::cmp( + ¤t.weighted_account.weight, + &with_smallest_weight_so_far.weighted_account.weight, + ) { + Ordering::Less => current, + Ordering::Greater => with_smallest_weight_so_far, + Ordering::Equal => with_smallest_weight_so_far, + }, + ) } } @@ -370,13 +366,9 @@ mod tests { .determine_limit_result(1_000_000_000) .determine_limit_result(9_999_999_999); let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(444); - account_1 - .non_finalized_account - .proposed_adjusted_balance_minor = 1_000_000_000; + account_1.proposed_adjusted_balance_minor = 1_000_000_000; let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(777); - account_2 - .non_finalized_account - .proposed_adjusted_balance_minor = 9_999_999_999; + account_2.proposed_adjusted_balance_minor = 9_999_999_999; let accounts = vec![account_1, account_2]; let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); @@ -497,14 +489,8 @@ mod tests { fn make_unconfirmed_adjustments_and_expected_test_result( weights: Vec, idx_of_expected_result: usize, - ) -> ( - Vec, - AdjustedAccountBeforeFinalization, - ) { - let init: ( - Vec, - Option, - ) = (vec![], None); + ) -> (Vec, UnconfirmedAdjustment) { + let init: (Vec, Option) = (vec![], None); let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( init, @@ -513,7 +499,7 @@ mod tests { let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; let garbage_permanent_debt_allowed_wei = 1_111_111_111; let qualified_account = QualifiedPayableAccount { - qualified_as: original_account, + bare_account: original_account, payment_threshold_intercept_minor: garbage_intercept, creditor_thresholds: CreditorThresholds { permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, @@ -527,7 +513,7 @@ mod tests { let expected_result_opt = if expected_result_opt_so_far.is_none() && actual_idx == idx_of_expected_result { - Some(new_adjustment_to_be_added.non_finalized_account.clone()) + Some(new_adjustment_to_be_added.clone()) } else { expected_result_opt_so_far }; diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 14abede25..929f6f3bc 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -3,11 +3,16 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; +use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; +use crate::accountant::test_utils::{ + make_guaranteed_qualified_payables, try_making_guaranteed_qualified_payables, +}; +use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -21,9 +26,6 @@ use std::io::Write; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; -use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; -use crate::accountant::QualifiedPayableAccount; -use crate::accountant::test_utils::{make_guaranteed_qualified_payables, try_making_guaranteed_qualified_payables}; #[test] fn loading_test_with_randomized_params() { @@ -33,7 +35,7 @@ fn loading_test_with_randomized_params() { // be corner cases that there wasn't awareness of when it was being designed. Therefore, the main // purpose of this test is to prove that out of a huge number of tries the PaymentAdjuster always // comes along fairly well, especially, that it cannot kill the Node by an accidental panic or - // that it can live up to its original purpose and vast majority of the attempted adjustments + // that it can live up to its original purpose and the vast majority of the attempted adjustments // end up with reasonable results. That said, a smaller amount of these attempts are expected // to be vain because of some chance always be there that with a given combination of payables // the algorithm will go step by step eliminating completely all accounts. There's hardly @@ -56,31 +58,42 @@ fn loading_test_with_randomized_params() { }; let first_stage_output = scenarios .into_iter() - .fold(init, |mut output_collector, scenario|{ + .fold(init, |mut output_collector, scenario| { // We watch only the service fee balance check, transaction fee can be added, but it // doesn't interact with the potential error 'AllAccountsEliminated' whose occurrence // rate is interesting compared to how many times the initial check lets the adjustment // procedure go on - let initial_check_result = - subject - .search_for_indispensable_adjustment(&scenario.qualified_payables, &*scenario.agent); - let allowed_scenario_opt = match initial_check_result{ - Ok(adjustment_opt) => {match adjustment_opt{ - None => panic!("Wrong test setup. This test is designed to generate scenarios \ - with balances always insufficient in some way!"), - Some(_) => () - }; - Some(scenario)} - Err(PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction {..}) => { - output_collector.test_overall_output_collector.scenarios_eliminated_before_adjustment_started += 1; + let initial_check_result = subject.search_for_indispensable_adjustment( + &scenario.qualified_payables, + &*scenario.agent, + ); + let allowed_scenario_opt = match initial_check_result { + Ok(adjustment_opt) => { + match adjustment_opt { + None => panic!( + "Wrong test setup. This test is designed to generate scenarios \ + with balances always insufficient in some way!" + ), + Some(_) => (), + }; + Some(scenario) + } + Err( + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + .. + }, + ) => { + output_collector + .test_overall_output_collector + .scenarios_eliminated_before_adjustment_started += 1; None } - _e => Some(scenario) + _e => Some(scenario), }; - match allowed_scenario_opt{ + match allowed_scenario_opt { Some(scenario) => output_collector.allowed_scenarios.push(scenario), - None => () + None => (), } output_collector @@ -125,16 +138,29 @@ fn generate_scenarios( .collect() } -fn try_making_single_valid_scenario(gn: &mut ThreadRng, now: SystemTime) -> Option { +fn try_making_single_valid_scenario( + gn: &mut ThreadRng, + now: SystemTime, +) -> Option { let (cw_service_fee_balance, payables) = make_payables(gn, now); let payables_len = payables.len(); - let qualified_payables = try_making_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS,now, false); - if payables_len != qualified_payables.len(){ - return None + let qualified_payables = try_making_guaranteed_qualified_payables( + payables, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + false, + ); + if payables_len != qualified_payables.len() { + return None; } let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, qualified_payables.len()); - Some(PreparedAdjustment::new(qualified_payables, Box::new(agent), None, adjustment)) + Some(PreparedAdjustment::new( + qualified_payables, + Box::new(agent), + None, + adjustment, + )) } fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { @@ -251,14 +277,17 @@ struct FailedAdjustment { adjuster_error: PaymentAdjusterError, } -fn preserve_account_infos(accounts: &[QualifiedPayableAccount], now: SystemTime) -> Vec { +fn preserve_account_infos( + accounts: &[QualifiedPayableAccount], + now: SystemTime, +) -> Vec { accounts .iter() .map(|account| AccountInfo { - wallet: account.qualified_as.wallet.clone(), - initially_requested_service_fee_minor: account.qualified_as.balance_wei, + wallet: account.bare_account.wallet.clone(), + initially_requested_service_fee_minor: account.bare_account.balance_wei, debt_age_s: now - .duration_since(account.qualified_as.last_paid_timestamp) + .duration_since(account.bare_account.last_paid_timestamp) .unwrap() .as_secs(), }) @@ -268,14 +297,14 @@ fn preserve_account_infos(accounts: &[QualifiedPayableAccount], now: SystemTime) fn render_results_to_file_and_attempt_basic_assertions( scenario_results: Vec, number_of_requested_scenarios: usize, - test_overall_output_collector: TestOverallOutputCollector, + overall_output_collector: TestOverallOutputCollector, ) { let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); introduction(&mut file); let test_overall_output_collector = scenario_results .into_iter() - .fold(test_overall_output_collector, |acc, scenario_result| { + .fold(overall_output_collector, |acc, scenario_result| { process_single_scenario(&mut file, acc, scenario_result) }); let total_scenarios_evaluated = test_overall_output_collector @@ -302,9 +331,15 @@ fn render_results_to_file_and_attempt_basic_assertions( - ((test_overall_output_collector.scenarios_eliminated_before_adjustment_started * 100) / total_scenarios_evaluated); let required_pass_rate = 80; - assert!(entry_check_pass_rate >= required_pass_rate, "Not at least {}% from {} the scenarios \ + assert!( + entry_check_pass_rate >= required_pass_rate, + "Not at least {}% from {} the scenarios \ generated for this test allows PaymentAdjuster to continue doing its job and ends too early. \ - Instead only {}%. Setup of the test might be needed",required_pass_rate, total_scenarios_evaluated, entry_check_pass_rate); + Instead only {}%. Setup of the test might be needed", + required_pass_rate, + total_scenarios_evaluated, + entry_check_pass_rate + ); let ok_adjustment_percentage = (test_overall_output_collector.oks * 100) / (total_scenarios_evaluated - test_overall_output_collector.scenarios_eliminated_before_adjustment_started); @@ -321,7 +356,7 @@ fn render_results_to_file_and_attempt_basic_assertions( fn introduction(file: &mut File) { write_thick_dividing_line(file); write_thick_dividing_line(file); - file.write(b"You may proceed to a brief summary at the tail of this test output\n") + file.write(b"A short summary can be found at the tail\n") .unwrap(); write_thick_dividing_line(file); write_thick_dividing_line(file) @@ -329,25 +364,25 @@ fn introduction(file: &mut File) { fn write_brief_test_summary_into_file( file: &mut File, - test_overall_output_collector: &TestOverallOutputCollector, + overall_output_collector: &TestOverallOutputCollector, number_of_requested_scenarios: usize, total_of_scenarios_evaluated: usize, ) { write_thick_dividing_line(file); file.write_fmt(format_args!( - "Scenarios \n\ - Requested: {}\n\ - Actually evaluated: {}\n\ - Caught by the entry check: {}\n\ - Successful: {}\n\ - With 'AllAccountsEliminated': {}\n\ - With late insufficient balance errors: {}", + "Scenarios\n\ + Requested:............................. {}\n\ + Actually evaluated:.................... {}\n\ + Caught by the entry check:............. {}\n\ + Successful:............................ {}\n\ + With 'AllAccountsEliminated':.......... {}\n\ + With late insufficient balance errors:. {}", number_of_requested_scenarios, total_of_scenarios_evaluated, - test_overall_output_collector.scenarios_eliminated_before_adjustment_started, - test_overall_output_collector.oks, - test_overall_output_collector.all_accounts_eliminated, - test_overall_output_collector.insufficient_service_fee_balance + overall_output_collector.scenarios_eliminated_before_adjustment_started, + overall_output_collector.oks, + overall_output_collector.all_accounts_eliminated, + overall_output_collector.insufficient_service_fee_balance )) .unwrap() } diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 85d9174f6..703c2cff8 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,7 +1,9 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, +}; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -118,22 +120,24 @@ pub fn accounts_before_and_after_debug( ) } -pub fn info_log_for_disqualified_account( - logger: &Logger, - account: &AdjustedAccountBeforeFinalization, -) { +pub fn info_log_for_disqualified_account(logger: &Logger, account: &UnconfirmedAdjustment) { info!( logger, "Shortage of MASQ in your consuming wallet impacts on payable {}, ruled out from this \ round of payments. The proposed adjustment {} wei was less than half of the recorded \ debt, {} wei", - account.qualified_payable.qualified_as.wallet, + account + .weighted_account + .qualified_account + .bare_account + .wallet, account .proposed_adjusted_balance_minor .separate_with_commas(), account - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .balance_wei .separate_with_commas() ) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index bd8e162d9..791795065 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,9 +1,10 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::QualifiedPayableAccount; use web3::types::U256; -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct WeightedPayable { pub qualified_account: QualifiedPayableAccount, pub weight: u128, @@ -23,7 +24,7 @@ pub enum AdjustmentIterationResult { AllAccountsProcessed(Vec), IterationWithSpecialHandling { case: SpecialHandling, - remaining_undecided_accounts: Vec, + remaining_undecided_accounts: Vec, }, } @@ -59,17 +60,14 @@ pub enum SpecialHandling { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { - pub qualified_payable: QualifiedPayableAccount, + pub original_account: PayableAccount, pub proposed_adjusted_balance_minor: u128, } impl AdjustedAccountBeforeFinalization { - pub fn new( - qualified_payable: QualifiedPayableAccount, - proposed_adjusted_balance_minor: u128, - ) -> Self { + pub fn new(original_account: PayableAccount, proposed_adjusted_balance_minor: u128) -> Self { Self { - qualified_payable, + original_account, proposed_adjusted_balance_minor, } } @@ -77,18 +75,15 @@ impl AdjustedAccountBeforeFinalization { #[derive(Debug, PartialEq, Eq, Clone)] pub struct UnconfirmedAdjustment { - pub non_finalized_account: AdjustedAccountBeforeFinalization, - pub weight: u128, + pub weighted_account: WeightedPayable, + pub proposed_adjusted_balance_minor: u128, } impl UnconfirmedAdjustment { pub fn new(weighted_account: WeightedPayable, proposed_adjusted_balance_minor: u128) -> Self { Self { - non_finalized_account: AdjustedAccountBeforeFinalization::new( - weighted_account.qualified_account, - proposed_adjusted_balance_minor, - ), - weight: weighted_account.weight, + weighted_account, + proposed_adjusted_balance_minor, } } } @@ -135,21 +130,23 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, }; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use crate::accountant::test_utils::{ + make_non_guaranteed_qualified_payable, make_payable_account, + }; use ethereum_types::U256; #[test] fn merging_results_from_recursion_works() { let non_finalized_account_1 = AdjustedAccountBeforeFinalization { - qualified_payable: make_non_guaranteed_qualified_payable(111), + original_account: make_payable_account(111), proposed_adjusted_balance_minor: 1234, }; let non_finalized_account_2 = AdjustedAccountBeforeFinalization { - qualified_payable: make_non_guaranteed_qualified_payable(222), + original_account: make_payable_account(222), proposed_adjusted_balance_minor: 5555, }; let non_finalized_account_3 = AdjustedAccountBeforeFinalization { - qualified_payable: make_non_guaranteed_qualified_payable(333), + original_account: make_payable_account(333), proposed_adjusted_balance_minor: 6789, }; let subject = RecursionResults { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 882b1f9d2..1d79c58c6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -77,29 +77,33 @@ fn find_largest_u128(slice: &[u128]) -> u128 { } pub fn adjust_account_balance_if_outweighed( - (mut outweighed, mut passing_through): (Vec, Vec), + (mut outweighed, mut passing_through): ( + Vec, + Vec, + ), mut current_adjustment_info: UnconfirmedAdjustment, -) -> (Vec, Vec) { - if current_adjustment_info - .non_finalized_account - .proposed_adjusted_balance_minor +) -> ( + Vec, + Vec, +) { + if current_adjustment_info.proposed_adjusted_balance_minor > current_adjustment_info - .non_finalized_account - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .balance_wei { - possibly_outweighed_accounts_diagnostics(¤t_adjustment_info.non_finalized_account); - - current_adjustment_info - .non_finalized_account - .proposed_adjusted_balance_minor = current_adjustment_info - .non_finalized_account - .qualified_payable - .qualified_as - .balance_wei; + possibly_outweighed_accounts_diagnostics(¤t_adjustment_info); + + let almost_finalized_account = AdjustedAccountBeforeFinalization::new( + current_adjustment_info + .weighted_account + .qualified_account + .bare_account, + current_adjustment_info.proposed_adjusted_balance_minor, + ); - outweighed.push(current_adjustment_info); + outweighed.push(todo!()); } else { passing_through.push(current_adjustment_info); } @@ -149,8 +153,7 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( ) -> ConsumingWalletExhaustingStatus { if status.remainder != 0 { let balance_gap_minor = non_finalized_account - .qualified_payable - .qualified_as + .original_account .balance_wei .checked_sub(non_finalized_account.proposed_adjusted_balance_minor) .unwrap_or_else(|| { @@ -158,10 +161,7 @@ fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( "Proposed balance should never be bigger than the original one. Proposed: \ {}, original: {}", non_finalized_account.proposed_adjusted_balance_minor, - non_finalized_account - .qualified_payable - .qualified_as - .balance_wei + non_finalized_account.original_account.balance_wei ) }); let possible_extra_addition = if balance_gap_minor < status.remainder { @@ -232,7 +232,7 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( ) -> Vec { weights_and_accounts .into_iter() - .map(|weighted_account| weighted_account.qualified_account.qualified_as) + .map(|weighted_account| weighted_account.qualified_account.bare_account) .collect() } @@ -246,28 +246,35 @@ pub fn nonzero_positive(x: u128) -> u128 { impl From for PayableAccount { fn from(qualified_payable: QualifiedPayableAccount) -> Self { - qualified_payable.qualified_as + qualified_payable.bare_account } } -impl From for QualifiedPayableAccount { +impl From for WeightedPayable { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment).qualified_payable + unconfirmed_adjustment.weighted_account } } impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - unconfirmed_adjustment.non_finalized_account + todo!(); + AdjustedAccountBeforeFinalization::new( + unconfirmed_adjustment + .weighted_account + .qualified_account + .bare_account, + unconfirmed_adjustment.proposed_adjusted_balance_minor, + ) } } impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { let proposed_adjusted_balance_minor = - weighted_account.qualified_account.qualified_as.balance_wei; + weighted_account.qualified_account.bare_account.balance_wei; AdjustedAccountBeforeFinalization { - qualified_payable: weighted_account.qualified_account, + original_account: weighted_account.qualified_account.bare_account, proposed_adjusted_balance_minor, } } @@ -280,16 +287,10 @@ impl From for PayableAccount { balance_wei: resolution_info .non_finalized_adjustment .proposed_adjusted_balance_minor, - ..resolution_info - .non_finalized_adjustment - .qualified_payable - .qualified_as + ..resolution_info.non_finalized_adjustment.original_account }, AdjustmentResolution::Revert => { - resolution_info - .non_finalized_adjustment - .qualified_payable - .qualified_as + resolution_info.non_finalized_adjustment.original_account } } } @@ -325,10 +326,7 @@ mod tests { #[test] fn zero_affordable_accounts_found_returns_false_for_non_finalized_accounts() { let result = zero_affordable_accounts_found(&Either::Left(vec![ - AdjustedAccountBeforeFinalization::new( - make_non_guaranteed_qualified_payable(456), - 1234, - ), + AdjustedAccountBeforeFinalization::new(make_payable_account(456), 1234), ])); assert_eq!(result, false) @@ -445,19 +443,13 @@ mod tests { let garbage_last_paid_timestamp = SystemTime::now(); let garbage_payment_threshold_intercept_minor = u128::MAX; let garbage_permanent_debt_allowed_wei = 123456789; - let qualified_payable = QualifiedPayableAccount { - qualified_as: PayableAccount { - wallet: wallet.clone(), - balance_wei: original_balance, - last_paid_timestamp: garbage_last_paid_timestamp, - pending_payable_opt: None, - }, - payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, - creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, - }, + let payable_account = PayableAccount { + wallet: wallet.clone(), + balance_wei: original_balance, + last_paid_timestamp: garbage_last_paid_timestamp, + pending_payable_opt: None, }; - AdjustedAccountBeforeFinalization::new(qualified_payable, proposed_adjusted_balance) + AdjustedAccountBeforeFinalization::new(payable_account, proposed_adjusted_balance) } fn assert_payable_accounts_after_adjustment_finalization( diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 78c185360..0a863a651 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -197,8 +197,9 @@ impl PaymentAdjusterReal { &mut self, qualified_accounts: Vec, ) -> Result, PaymentAdjusterError> { + let weighted_accounts_sorted = self.calculate_weights_for_accounts(qualified_accounts); let processed_accounts = self.calculate_criteria_and_propose_adjustments_recursively( - qualified_accounts, + weighted_accounts_sorted, TransactionAndServiceFeeAdjustmentRunner {}, )?; @@ -222,7 +223,7 @@ impl PaymentAdjusterReal { fn calculate_criteria_and_propose_adjustments_recursively( &mut self, - unresolved_qualified_accounts: Vec, + unresolved_accounts: Vec, adjustment_runner: AR, ) -> RT where @@ -230,22 +231,10 @@ impl PaymentAdjusterReal { { diagnostics!( "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", - &unresolved_qualified_accounts + &unresolved_accounts ); - //TODO remove me - // if unresolved_qualified_accounts.len() == 1 { - // let last_one = unresolved_qualified_accounts - // .into_iter() - // .next() - // .expect("previous if stmt must be wrong"); - // return adjustment_runner.adjust_last_one(self, last_one); - // } - - let weights_and_accounts_sorted = - self.calculate_weights_for_accounts(unresolved_qualified_accounts); - - adjustment_runner.adjust_accounts(self, weights_and_accounts_sorted) + adjustment_runner.adjust_accounts(self, unresolved_accounts) } fn begin_with_adjustment_by_transaction_fee( @@ -394,7 +383,7 @@ impl PaymentAdjusterReal { let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( - &payable.qualified_as.wallet, + &payable.bare_account.wallet, criterion_calculator.as_ref(), new_criterion, summed_up, @@ -436,8 +425,8 @@ impl PaymentAdjusterReal { .iter() .map(|payable| { ( - payable.qualified_as.wallet.clone(), - payable.qualified_as.balance_wei, + payable.bare_account.wallet.clone(), + payable.bare_account.balance_wei, ) }) .collect::>() @@ -460,38 +449,39 @@ impl PaymentAdjusterReal { &self, last_payable: QualifiedPayableAccount, ) -> Option { - let cw_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let proposed_adjusted_balance = if last_payable - .qualified_as - .balance_wei - .checked_sub(cw_balance) - == None - { - last_payable.qualified_as.balance_wei - } else { - diagnostics!( - "LAST REMAINING ACCOUNT", - "Balance adjusted to {} by exhausting the cw balance fully", - cw_balance - ); - - cw_balance - }; - // TODO the Disqualification check really makes sense only if we assigned less than the full balance!! - let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - WeightedPayable::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up - proposed_adjusted_balance, - )]; - - match self - .disqualification_arbiter - .try_finding_an_account_to_disqualify_in_this_iteration( - &proposed_adjustment_vec, - &self.logger, - ) { - Some(_) => None, - None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), - } + todo!() + // let cw_balance = self.inner.unallocated_cw_service_fee_balance_minor(); + // let proposed_adjusted_balance = if last_payable + // .bare_account + // .balance_wei + // .checked_sub(cw_balance) + // == None + // { + // last_payable.bare_account.balance_wei + // } else { + // diagnostics!( + // "LAST REMAINING ACCOUNT", + // "Balance adjusted to {} by exhausting the cw balance fully", + // cw_balance + // ); + // + // cw_balance + // }; + // // TODO the Disqualification check really makes sense only if we assigned less than the full balance!! + // let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( + // WeightedPayable::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up + // proposed_adjusted_balance, + // )]; + // + // match self + // .disqualification_arbiter + // .try_finding_an_account_to_disqualify_in_this_iteration( + // &proposed_adjustment_vec, + // &self.logger, + // ) { + // Some(_) => None, + // None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), + // } } } @@ -568,7 +558,7 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, SpecialHandling, - WeightedPayable, + UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; use crate::accountant::payment_adjuster::service_fee_adjuster::{ @@ -853,7 +843,7 @@ mod tests { let subject = make_initialized_subject(None, None, Some(calculator), None); let make_account = |n: u64| { let account = make_non_guaranteed_qualified_payable(n); - let wallet = account.qualified_as.wallet.clone(); + let wallet = account.bare_account.wallet.clone(); (wallet, account) }; let (wallet_1, payable_1) = make_account(111); @@ -874,7 +864,7 @@ mod tests { weighted_account.weight ); previous_weight = weighted_account.weight; - weighted_account.qualified_account.qualified_as.wallet + weighted_account.qualified_account.bare_account.wallet }) .collect::>(); assert_eq!(accounts_alone, vec![wallet_3, wallet_1, wallet_2]) @@ -885,39 +875,30 @@ mod tests { let now = SystemTime::now(); let cw_service_fee_balance_minor = multiple_by_billion(3_500_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let calculator_mock = CriterionCalculatorMock::default() - // Account 1, first iteration - .calculate_result(multiple_by_billion(2_000_100)) - // Account 2 in its only iteration, after which it is found outweighed and handled - // as a priority - .calculate_result(multiple_by_billion(3_999_900)) - // Account 1, second iteration - .calculate_result(multiple_by_billion(2_000_100)); - let mut subject = make_initialized_subject( - None, - Some(cw_service_fee_balance_minor), - Some(calculator_mock), - None, - ); + let mut subject = + make_initialized_subject(None, Some(cw_service_fee_balance_minor), None, None); let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(cw_service_fee_balance_minor / 2) .determine_limit_params(&determine_limit_params_arc); subject.disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let mut account_1 = make_qualified_payable_by_wallet("abc"); - let balance_1 = multiple_by_billion(3_000_000); - account_1.qualified_as.balance_wei = balance_1; + let balance_1 = multiple_by_billion(3_000_000); + account_1.bare_account.balance_wei = balance_1; let threshold_intercept_minor = account_1.payment_threshold_intercept_minor; let permanent_debt_allowed_minor = account_1.creditor_thresholds.permanent_debt_allowed_wei; let mut account_2 = make_qualified_payable_by_wallet("def"); - let wallet_2 = account_2.qualified_as.wallet.clone(); + let wallet_2 = account_2.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); - account_2.qualified_as.balance_wei = balance_2; - let qualified_payables = vec![account_1, account_2]; + account_2.bare_account.balance_wei = balance_2; + let weighted_payables = vec![ + WeightedPayable::new(account_1, multiple_by_billion(2_000_100)), + WeightedPayable::new(account_2, 3_999_900), + ]; let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( - qualified_payables.clone(), + weighted_payables.clone(), TransactionAndServiceFeeAdjustmentRunner {}, ) .unwrap() @@ -933,7 +914,7 @@ mod tests { prove_that_proposed_adjusted_balance_would_exceed_the_original_value( subject, cw_service_fee_balance_minor, - qualified_payables.clone(), + weighted_payables.clone(), wallet_2, balance_2, 2.3, @@ -942,8 +923,8 @@ mod tests { let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!( - &first_returned_account.qualified_payable, - &qualified_payables[1] + &first_returned_account.original_account, + &weighted_payables[1].qualified_account.bare_account ); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, @@ -951,22 +932,29 @@ mod tests { ); let second_returned_account = result.remove(0); assert_eq!( - &second_returned_account.qualified_payable, - &qualified_payables[0] + &second_returned_account.original_account, + &weighted_payables[0].qualified_account.bare_account ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, 2499999999999999 ); assert!(result.is_empty()); - let determine_limit_params= determine_limit_params_arc.lock().unwrap(); - assert_eq!(*determine_limit_params, vec![(balance_1, threshold_intercept_minor, permanent_debt_allowed_minor)]) + let determine_limit_params = determine_limit_params_arc.lock().unwrap(); + assert_eq!( + *determine_limit_params, + vec![( + balance_1, + threshold_intercept_minor, + permanent_debt_allowed_minor + )] + ) } fn prove_that_proposed_adjusted_balance_would_exceed_the_original_value( mut subject: PaymentAdjusterReal, cw_service_fee_balance_minor: u128, - accounts: Vec, + weighted_accounts: Vec, wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, outweighed_by_multiple_of: f64, @@ -976,18 +964,15 @@ mod tests { None, cw_service_fee_balance_minor, )); - let criteria_and_accounts = subject.calculate_weights_for_accounts(accounts); let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(criteria_and_accounts, cw_service_fee_balance_minor); + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); // The results are sorted from the biggest weights down - let proposed_adjusted_balance = unconfirmed_adjustments[0] - .non_finalized_account - .proposed_adjusted_balance_minor; + let proposed_adjusted_balance = unconfirmed_adjustments[0].proposed_adjusted_balance_minor; assert_eq!( unconfirmed_adjustments[0] - .non_finalized_account - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .wallet, wallet_of_expected_outweighed ); @@ -1079,7 +1064,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); let iteration_result = AdjustmentIterationResult::IterationWithSpecialHandling { case: OutweighedAccounts(vec![AdjustedAccountBeforeFinalization::new( - make_qualified_payable_by_wallet("abc"), + make_payable_account(123), 123456, )]), remaining_undecided_accounts: vec![], @@ -1089,8 +1074,8 @@ mod tests { } #[test] - fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them( - ) { + fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them() + { // Tests that a condition to short-circuit through is integrated for situations when // a disqualification frees means for other accounts and there is suddenly more to give // than how much the remaining accounts demand @@ -1190,7 +1175,7 @@ mod tests { assert_eq!( result, Some(AdjustedAccountBeforeFinalization { - qualified_payable: qualified_payable.clone(), + original_account: qualified_payable.bare_account.clone(), proposed_adjusted_balance_minor: cw_balance, }) ); @@ -1198,7 +1183,7 @@ mod tests { assert_eq!( *determine_limit_params, vec![( - qualified_payable.qualified_as.balance_wei, + qualified_payable.bare_account.balance_wei, qualified_payable.payment_threshold_intercept_minor, qualified_payable .creditor_thresholds @@ -1223,7 +1208,7 @@ mod tests { pending_payable_opt: None, }; let qualified_payable = QualifiedPayableAccount { - qualified_as: payable, + bare_account: payable, payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, creditor_thresholds: CreditorThresholds { permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, @@ -1398,15 +1383,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, - ..qualified_account_1.qualified_as + ..qualified_account_1.bare_account }; let account_2_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_2, - ..qualified_account_2.qualified_as + ..qualified_account_2.bare_account }; let account_3_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_3, - ..qualified_account_3.qualified_as + ..qualified_account_3.bare_account }; vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] }; @@ -1500,7 +1485,7 @@ mod tests { // (it weights more if the balance is so small) assert_eq!( result.affordable_accounts, - vec![account_3.qualified_as, account_2.qualified_as] + vec![account_3.bare_account, account_2.bare_account] ); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); @@ -1590,9 +1575,9 @@ mod tests { let expected_accounts = { let account_2_adjusted = PayableAccount { balance_wei: 222_000_000_000_000, - ..account_2.qualified_as + ..account_2.bare_account }; - vec![account_3.qualified_as, account_2_adjusted] + vec![account_3.bare_account, account_2_adjusted] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1669,9 +1654,9 @@ mod tests { let expected_accounts = { let account_1_adjusted = PayableAccount { balance_wei: 272_000_000_000, - ..account_1.qualified_as + ..account_1.bare_account }; - vec![account_1_adjusted, account_2.qualified_as] + vec![account_1_adjusted, account_2.bare_account] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -2115,7 +2100,7 @@ mod tests { .map(|(idx, payable)| { let balance = payable.balance_wei; QualifiedPayableAccount { - qualified_as: payable, + bare_account: payable, payment_threshold_intercept_minor: (balance / 10) * 7, creditor_thresholds: CreditorThresholds { permanent_debt_allowed_wei: (balance / 10) * 7, @@ -2153,7 +2138,7 @@ mod tests { let now = SystemTime::now(); let payment_adjuster = PaymentAdjusterReal::default(); let qualified_payable = QualifiedPayableAccount { - qualified_as: PayableAccount { + bare_account: PayableAccount { wallet: make_wallet("abc"), balance_wei: gwei_to_wei::(444_666_888), last_paid_timestamp: now.checked_sub(Duration::from_secs(123_000)).unwrap(), @@ -2194,10 +2179,10 @@ mod tests { // First stage: BalanceAndAgeCalculator { let mut account_1 = nominal_account_1; - account_1.qualified_as.balance_wei += 123_456_789; + account_1.bare_account.balance_wei += 123_456_789; let mut account_2 = nominal_account_2; - account_2.qualified_as.last_paid_timestamp = account_2 - .qualified_as + account_2.bare_account.last_paid_timestamp = account_2 + .bare_account .last_paid_timestamp .checked_sub(Duration::from_secs(4_000)) .unwrap(); @@ -2278,7 +2263,7 @@ mod tests { fn initialize_template_accounts(now: SystemTime) -> [QualifiedPayableAccount; 2] { let make_qualified_payable = |wallet| QualifiedPayableAccount { - qualified_as: PayableAccount { + bare_account: PayableAccount { wallet, balance_wei: gwei_to_wei::(20_000_000), last_paid_timestamp: now.checked_sub(Duration::from_secs(10_000)).unwrap(), @@ -2380,7 +2365,7 @@ mod tests { fn prepare_args_expected_weights_for_comparison( (qualified_payable, expected_computed_payable): (QualifiedPayableAccount, u128), ) -> (QualifiedPayableAccount, (Wallet, u128)) { - let wallet = qualified_payable.qualified_as.wallet.clone(); + let wallet = qualified_payable.bare_account.wallet.clone(); (qualified_payable, (wallet, expected_computed_payable)) } @@ -2420,7 +2405,7 @@ mod tests { ) -> HashMap { let feeding_iterator = weighted_accounts.into_iter().map(|account| { ( - account.qualified_account.qualified_as.wallet.clone(), + account.qualified_account.bare_account.wallet.clone(), account, ) }); diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 42316c437..bfe92e908 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -93,7 +93,7 @@ impl PreparatoryAnalyzer { let qualified_payables = Self::comb_qualified_payables(payables); let required_service_fee_sum: u128 = - sum_as(&qualified_payables, |qa| qa.qualified_as.balance_wei); + sum_as(&qualified_payables, |qa| qa.bare_account.balance_wei); if cw_service_fee_balance_minor >= required_service_fee_sum { Ok(false) @@ -142,7 +142,7 @@ impl PreparatoryAnalyzer { Ok(()) } else { let total_amount_demanded_minor = - sum_as(qualified_payables, |qp| qp.qualified_as.balance_wei); + sum_as(qualified_payables, |qp| qp.bare_account.balance_wei); Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: qualified_payables.len(), @@ -228,12 +228,12 @@ mod tests { *determine_limit_params, vec![ ( - account_1.qualified_as.balance_wei, + account_1.bare_account.balance_wei, account_1.payment_threshold_intercept_minor, account_1.creditor_thresholds.permanent_debt_allowed_wei ), ( - account_2.qualified_as.balance_wei, + account_2.bare_account.balance_wei, account_2.payment_threshold_intercept_minor, account_2.creditor_thresholds.permanent_debt_allowed_wei ) @@ -244,9 +244,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.qualified_as.balance_wei = 1_000_000_000; + account_1.bare_account.balance_wei = 1_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.qualified_as.balance_wei = 2_000_000_000; + account_2.bare_account.balance_wei = 2_000_000_000; let cw_service_fee_balance = 750_000_001; let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(750_000_000) @@ -263,9 +263,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.qualified_as.balance_wei = 2_000_000_000; + account_1.bare_account.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.qualified_as.balance_wei = 1_000_000_000; + account_2.bare_account.balance_wei = 1_000_000_000; let cw_service_fee_balance = 750_000_000; let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(1_500_000_000) @@ -282,11 +282,11 @@ mod tests { #[test] fn adjustment_possibility_err_from_insufficient_balance_for_even_the_least_demanding_account() { let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.qualified_as.balance_wei = 2_000_000_000; + account_1.bare_account.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(222); - account_2.qualified_as.balance_wei = 1_000_050_000; + account_2.bare_account.balance_wei = 1_000_050_000; let mut account_3 = make_non_guaranteed_qualified_payable(333); - account_3.qualified_as.balance_wei = 1_000_111_111; + account_3.bare_account.balance_wei = 1_000_111_111; let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; let accounts_in_expected_format = original_accounts diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 2c5ba4711..e6e076123 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -91,7 +91,7 @@ impl ServiceFeeAdjusterReal { if outweighed.is_empty() { Either::Left(properly_adjusted_accounts) } else { - let remaining_undecided_accounts: Vec = + let remaining_undecided_accounts: Vec = convert_collection(properly_adjusted_accounts); let pre_processed_outweighed: Vec = convert_collection(outweighed); @@ -115,9 +115,9 @@ impl ServiceFeeAdjusterReal { { let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { account_info - .non_finalized_account - .qualified_payable - .qualified_as + .weighted_account + .qualified_account + .bare_account .wallet != disqualified_account_wallet }); @@ -125,7 +125,7 @@ impl ServiceFeeAdjusterReal { let remaining_reverted = remaining .map(|account_info| { //TODO maybe implement from like before - account_info.non_finalized_account.qualified_payable + account_info.weighted_account // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( // account_info.non_finalized_account, // AdjustmentResolution::Revert, diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index d6103c567..b1d8bba14 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -123,13 +123,11 @@ pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_payable = make_non_guaranteed_qualified_payable(n); let proposed_adjusted_balance_minor = - (qualified_payable.qualified_as.balance_wei / 2) * (n as f64).sqrt() as u128; + (qualified_payable.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; + let weight = (n as u128).pow(3); UnconfirmedAdjustment { - non_finalized_account: AdjustedAccountBeforeFinalization { - qualified_payable, - proposed_adjusted_balance_minor, - }, - weight: (n as u128).pow(3), + weighted_account: WeightedPayable::new(qualified_payable, weight), + proposed_adjusted_balance_minor, } } @@ -245,6 +243,6 @@ pub fn make_qualified_payable_by_wallet(wallet_address_segment: &str) -> Qualifi let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); let wallet = make_wallet(wallet_address_segment); let mut account = make_non_guaranteed_qualified_payable(num); - account.qualified_as.wallet = wallet; + account.bare_account.wallet = wallet; account } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index f74d71777..76ec15047 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -2194,7 +2194,7 @@ mod tests { .payable_exceeded_threshold(&payable, SystemTime::now()) .unwrap(); assert_eq!(result.len(), 1); - assert_eq!(&result[0].qualified_as.wallet, &wallet); + assert_eq!(&result[0].bare_account.wallet, &wallet); assert!(intercept_before >= result[0].payment_threshold_intercept_minor && result[0].payment_threshold_intercept_minor >= intercept_after, "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept_minor, intercept_before, intercept_after ) diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 92a0f7f45..3dc8d7fd9 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -177,7 +177,7 @@ pub mod payable_scanner_utils { qualified_accounts .iter() .map(|qualified_account| { - let account = &qualified_account.qualified_as; + let account = &qualified_account.bare_account; let p_age = now .duration_since(account.last_paid_timestamp) .expect("Payable time is corrupt"); @@ -651,7 +651,7 @@ mod tests { let now = to_time_t(SystemTime::now()); let qualified_payables_and_threshold_points = vec![ QualifiedPayableAccount { - qualified_as: PayableAccount { + bare_account: PayableAccount { wallet: make_wallet("wallet0"), balance_wei: gwei_to_wei(10_002_000_u64), last_paid_timestamp: from_time_t(now - 2678400), @@ -663,7 +663,7 @@ mod tests { }, }, QualifiedPayableAccount { - qualified_as: PayableAccount { + bare_account: PayableAccount { wallet: make_wallet("wallet1"), balance_wei: gwei_to_wei(999_999_999_u64), last_paid_timestamp: from_time_t(now - 86455), diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index e67d514c5..a3eb415e2 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1701,7 +1701,7 @@ pub fn make_guaranteed_qualified_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) + try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) } pub fn try_making_guaranteed_qualified_payables( @@ -1729,26 +1729,23 @@ pub fn try_making_guaranteed_qualified_payables( let payable_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); payables .into_iter() - .flat_map(|payable| - match payable_inspector - .payable_exceeded_threshold(&payable, payment_thresholds, now) { - Some(payment_threshold_intercept) => { - Some(QualifiedPayableAccount::new( - payable, - payment_threshold_intercept, - CreditorThresholds::new(gwei_to_wei( - payment_thresholds.permanent_debt_allowed_gwei, - )), - )) - }, - None => if should_panic { - panic(&payable, &payment_thresholds, now) - } else { - None + .flat_map(|payable| { + match payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) { + Some(payment_threshold_intercept) => Some(QualifiedPayableAccount::new( + payable, + payment_threshold_intercept, + CreditorThresholds::new(gwei_to_wei( + payment_thresholds.permanent_debt_allowed_gwei, + )), + )), + None => { + if should_panic { + panic(&payable, &payment_thresholds, now) + } else { + None + } } } - ) + }) .collect() } - - From 86dc9d8f4c1d0dbc49c108f8dcffb2de3f89cfed Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 9 Apr 2024 09:59:31 +0200 Subject: [PATCH 154/250] GH-711-b: interim commit --- .../miscellaneous/helper_functions.rs | 15 +++++++------- node/src/accountant/payment_adjuster/mod.rs | 20 ++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 1d79c58c6..5cb98c9cc 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -95,15 +95,17 @@ pub fn adjust_account_balance_if_outweighed( { possibly_outweighed_accounts_diagnostics(¤t_adjustment_info); + let original_account = current_adjustment_info + .weighted_account + .qualified_account + .bare_account; + let proposed_full_balance = original_account.balance_wei; let almost_finalized_account = AdjustedAccountBeforeFinalization::new( - current_adjustment_info - .weighted_account - .qualified_account - .bare_account, - current_adjustment_info.proposed_adjusted_balance_minor, + original_account, + proposed_full_balance ); - outweighed.push(todo!()); + outweighed.push(almost_finalized_account); } else { passing_through.push(current_adjustment_info); } @@ -258,7 +260,6 @@ impl From for WeightedPayable { impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - todo!(); AdjustedAccountBeforeFinalization::new( unconfirmed_adjustment .weighted_account diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0a863a651..9f13e1a7f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -891,14 +891,14 @@ mod tests { let wallet_2 = account_2.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); account_2.bare_account.balance_wei = balance_2; - let weighted_payables = vec![ + let weighted_payables_in_descending_order = vec![ + WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), WeightedPayable::new(account_1, multiple_by_billion(2_000_100)), - WeightedPayable::new(account_2, 3_999_900), ]; let mut result = subject .calculate_criteria_and_propose_adjustments_recursively( - weighted_payables.clone(), + weighted_payables_in_descending_order.clone(), TransactionAndServiceFeeAdjustmentRunner {}, ) .unwrap() @@ -907,14 +907,10 @@ mod tests { // Let's have an example to explain why this test is important. // First, the mock must be renewed; the available cw balance updated to the original value. - let mock_calculator = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(2_000_100)) - .calculate_result(multiple_by_billion(3_999_900)); - subject.calculators = vec![Box::new(mock_calculator)]; prove_that_proposed_adjusted_balance_would_exceed_the_original_value( subject, cw_service_fee_balance_minor, - weighted_payables.clone(), + weighted_payables_in_descending_order.clone(), wallet_2, balance_2, 2.3, @@ -924,7 +920,7 @@ mod tests { // Outweighed accounts always take the first places assert_eq!( &first_returned_account.original_account, - &weighted_payables[1].qualified_account.bare_account + &weighted_payables_in_descending_order[0].qualified_account.bare_account ); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, @@ -933,7 +929,7 @@ mod tests { let second_returned_account = result.remove(0); assert_eq!( &second_returned_account.original_account, - &weighted_payables[0].qualified_account.bare_account + &weighted_payables_in_descending_order[1].qualified_account.bare_account ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, @@ -1108,8 +1104,8 @@ mod tests { make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(0) - .calculate_result(multiple_by_billion(30_000_000_000)) - .calculate_result(multiple_by_billion(30_000_000_000)); + .calculate_result(multiple_by_billion(50_000_000_000)) + .calculate_result(multiple_by_billion(50_000_000_000)); let mut subject = PaymentAdjusterReal::new(); subject.calculators.push(Box::new(calculator_mock)); subject.logger = Logger::new(test_name); From d3cbd11277bdbb205d9050096aadeb84c9095b3f Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 9 Apr 2024 12:20:17 +0200 Subject: [PATCH 155/250] GH-711-b: weighting system for balance inverted --- .../payment_adjuster/adjustment_runners.rs | 27 +++- .../balance_and_age_calculator.rs | 47 +++--- .../disqualification_arbiter.rs | 144 ++++++++--------- node/src/accountant/payment_adjuster/inner.rs | 33 ++++ .../miscellaneous/helper_functions.rs | 41 ++++- node/src/accountant/payment_adjuster/mod.rs | 145 ++++++++++++++---- .../accountant/payment_adjuster/test_utils.rs | 2 + 7 files changed, 309 insertions(+), 130 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index f50857cfd..c55fa6f4c 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -136,6 +136,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, }; @@ -200,8 +201,15 @@ mod tests { fn initialize_payment_adjuster( now: SystemTime, service_fee_balance: u128, + largest_exceeding_balance_recently_qualified: u128, ) -> PaymentAdjusterReal { - make_initialized_subject(Some(now), Some(service_fee_balance), None, None) + make_initialized_subject( + Some(now), + Some(service_fee_balance), + None, + Some(largest_exceeding_balance_recently_qualified), + None, + ) } fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { @@ -221,7 +229,8 @@ mod tests { // The disqualification doesn't take part in here, it is just an explanation for those who // wonder why the implied surplus may happen let now = SystemTime::now(); - let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor); + let mut payment_adjuster = + initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); let initial_balance_minor_1 = payable_1.qualified_account.bare_account.balance_wei; let initial_balance_minor_2 = payable_2.qualified_account.bare_account.balance_wei; let subject = ServiceFeeOnlyAdjustmentRunner {}; @@ -247,7 +256,7 @@ mod tests { } #[test] - fn service_fee_only_runner_cw_balance_equals_requested_money_after_dql_in_previous_iteration() { + fn service_fee_only_runner_cw_balance_equals_requested_money_after_dsq_in_previous_iteration() { let cw_service_fee_balance_minor = 10_000_000_000; let payable_1 = make_weighed_payable(111, 5_000_000_000); let payable_2 = make_weighed_payable(222, 5_000_000_000); @@ -260,7 +269,7 @@ mod tests { } #[test] - fn service_fee_only_runner_handles_means_bigger_requested_money_after_dql_in_previous_iteration( + fn service_fee_only_runner_handles_means_bigger_requested_money_after_dsq_in_previous_iteration( ) { let cw_service_fee_balance_minor = 10_000_000_000; let payable_1 = make_weighed_payable(111, 5_000_000_000); @@ -293,14 +302,20 @@ mod tests { let wallet_3 = make_wallet("ghj"); let mut account_3 = account_1.clone(); account_3.wallet = wallet_3; - let accounts = vec![account_1, account_2]; + let accounts = vec![account_1, account_2, account_3]; let qualified_payables = make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); + let largest_exceeding_balance_recently_qualified = + find_largest_exceeding_balance(&qualified_payables); let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; let service_fee_balance_wei = 10_000_000_000; - let mut payment_adjuster = initialize_payment_adjuster(now, service_fee_balance_wei); + let mut payment_adjuster = initialize_payment_adjuster( + now, + service_fee_balance_wei, + largest_exceeding_balance_recently_qualified, + ); let subject = ServiceFeeOnlyAdjustmentRunner {}; let criteria_and_accounts = payment_adjuster.calculate_weights_for_accounts(qualified_payables); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 3bd58665d..42389eddd 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -13,19 +13,14 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner, ) -> u128 { - let now = context.now(); + let largest = context.largest_exceeding_balance_recently_qualified(); - let debt_age_s = now - .duration_since(account.bare_account.last_paid_timestamp) - .expect("time traveller") - .as_secs(); + let this_account = + account.bare_account.balance_wei - account.payment_threshold_intercept_minor; + let diff = largest - this_account; - eprintln!( - "{} - {}", - account.bare_account.balance_wei, account.payment_threshold_intercept_minor - ); - account.bare_account.balance_wei - account.payment_threshold_intercept_minor - + debt_age_s as u128 + // We invert the magnitude for smaller debts + largest + diff } fn parameter_name(&self) -> &'static str { @@ -38,6 +33,8 @@ mod tests { use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; + use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use std::time::SystemTime; @@ -53,11 +50,20 @@ mod tests { #[test] fn balance_and_age_criterion_calculator_works() { let now = SystemTime::now(); - let qualified_accounts = [0, 10, 22] + let qualified_accounts = [50, 100, 2_222] .into_iter() - .map(|n| make_non_guaranteed_qualified_payable(n)) + .enumerate() + .map(|(idx, n)| { + let mut basic_q_payable = make_non_guaranteed_qualified_payable(idx as u64); + basic_q_payable.bare_account.balance_wei = multiple_by_billion(n); + basic_q_payable.payment_threshold_intercept_minor = + (multiple_by_billion(2) / 5) * 3; + basic_q_payable + }) .collect::>(); - let payment_adjuster_inner = PaymentAdjusterInnerReal::new(now, None, 123456789); + let largest_exceeding_balance = find_largest_exceeding_balance(&qualified_accounts); + let payment_adjuster_inner = + PaymentAdjusterInnerReal::new(now, None, 123456789, largest_exceeding_balance); let subject = BalanceAndAgeCriterionCalculator::default(); let computed_criteria = qualified_accounts @@ -69,13 +75,12 @@ mod tests { .into_iter() .zip(computed_criteria.into_iter()); zipped.into_iter().for_each(|(account, actual_criterion)| { - let debt_age_s = now - .duration_since(account.bare_account.last_paid_timestamp) - .unwrap() - .as_secs(); - let expected_criterion = account.bare_account.balance_wei - - account.payment_threshold_intercept_minor - + debt_age_s as u128; + let expected_criterion = { + let exceeding_balance_on_this_account = + account.bare_account.balance_wei - account.payment_threshold_intercept_minor; + let diff = largest_exceeding_balance - exceeding_balance_on_this_account; + largest_exceeding_balance + diff + }; assert_eq!(actual_criterion, expected_criterion) }) } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 1de1af876..1aa6a8b71 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -191,13 +191,16 @@ impl DisqualificationGaugeReal { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::db_access_objects::utils::from_time_t; use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + find_largest_exceeding_balance, weights_total, + }; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, @@ -415,75 +418,76 @@ mod tests { #[test] fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { - todo!() - // let test_name = - // "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; - // let now = SystemTime::now(); - // let cw_service_fee_balance_minor = 200_000_000_000; - // let mut payment_thresholds = PaymentThresholds::default(); - // payment_thresholds.permanent_debt_allowed_gwei = 10; - // payment_thresholds.maturity_threshold_sec = 1_000; - // payment_thresholds.threshold_interval_sec = 10_000; - // let base_time_for_qualified = payment_thresholds.maturity_threshold_sec - // + payment_thresholds.threshold_interval_sec - // + 1; - // let logger = Logger::new(test_name); - // let subject = make_initialized_subject(now, Some(cw_service_fee_balance_minor), None, None); - // let wallet_1 = make_wallet("abc"); - // let debt_age_1 = base_time_for_qualified + 1; - // let account_1 = PayableAccount { - // wallet: wallet_1.clone(), - // balance_wei: 120_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_1)).unwrap(), - // pending_payable_opt: None, - // }; - // let wallet_2 = make_wallet("def"); - // let debt_age_2 = base_time_for_qualified + 3; - // let account_2 = PayableAccount { - // wallet: wallet_2.clone(), - // balance_wei: 120_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_2)).unwrap(), - // pending_payable_opt: None, - // }; - // let wallet_3 = make_wallet("ghi"); - // let debt_age_3 = base_time_for_qualified; - // let account_3 = PayableAccount { - // wallet: wallet_3.clone(), - // balance_wei: 120_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_3)).unwrap(), - // pending_payable_opt: None, - // }; - // let wallet_4 = make_wallet("jkl"); - // let debt_age_4 = base_time_for_qualified + 2; - // let account_4 = PayableAccount { - // wallet: wallet_4.clone(), - // balance_wei: 120_000_000_000, - // last_paid_timestamp: now.checked_sub(Duration::from_secs(debt_age_4)).unwrap(), - // pending_payable_opt: None, - // }; - // let accounts = vec![account_1, account_2, account_3, account_4]; - // let qualified_payables = - // make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); - // let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - // let unconfirmed_adjustments = AdjustmentComputer::default() - // .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); - // let subject = DisqualificationArbiter::default(); - // - // let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( - // &unconfirmed_adjustments, - // &logger, - // ); - // - // unconfirmed_adjustments.iter().for_each(|payable| { - // // Condition of disqualification at the horizontal threshold - // assert!( - // payable - // .non_finalized_account - // .proposed_adjusted_balance_minor - // < 120_000_000_000 - // ) - // }); - // assert_eq!(result, Some(wallet_3)); + let test_name = + "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; + let now = SystemTime::now(); + let cw_service_fee_balance_minor = 200_000_000_000; + let mut payment_thresholds = PaymentThresholds::default(); + payment_thresholds.permanent_debt_allowed_gwei = 10; + payment_thresholds.maturity_threshold_sec = 1_000; + payment_thresholds.threshold_interval_sec = 10_000; + let logger = Logger::new(test_name); + let wallet_1 = make_wallet("abc"); + let common_timestamp = from_time_t( + (payment_thresholds.maturity_threshold_sec + + payment_thresholds.threshold_interval_sec + + 1) as i64, + ); + let account_1 = PayableAccount { + wallet: wallet_1.clone(), + balance_wei: 120_000_000_000 + 1, + last_paid_timestamp: common_timestamp, + pending_payable_opt: None, + }; + let wallet_2 = make_wallet("def"); + let account_2 = PayableAccount { + wallet: wallet_2.clone(), + balance_wei: 120_000_000_000, + last_paid_timestamp: common_timestamp, + pending_payable_opt: None, + }; + let wallet_3 = make_wallet("ghi"); + // This account has the largest exceeding balance + // and therefore has the smallest weight + let account_3 = PayableAccount { + wallet: wallet_3.clone(), + balance_wei: 120_000_000_000 + 2, + last_paid_timestamp: common_timestamp, + pending_payable_opt: None, + }; + let wallet_4 = make_wallet("jkl"); + let account_4 = PayableAccount { + wallet: wallet_4.clone(), + balance_wei: 120_000_000_000 - 1, + last_paid_timestamp: common_timestamp, + pending_payable_opt: None, + }; + let accounts = vec![account_1, account_2, account_3, account_4]; + let qualified_payables = + make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); + let largest_exceeding_balance = find_largest_exceeding_balance(&qualified_payables); + let subject = make_initialized_subject( + Some(now), + Some(cw_service_fee_balance_minor), + None, + Some(largest_exceeding_balance), + None, + ); + let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + let unconfirmed_adjustments = AdjustmentComputer::default() + .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); + let subject = DisqualificationArbiter::default(); + + let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + &logger, + ); + + unconfirmed_adjustments.iter().for_each(|payable| { + // Condition of disqualification at the horizontal threshold + assert!(payable.proposed_adjusted_balance_minor < 120_000_000_000) + }); + assert_eq!(result, Some(wallet_3)); } fn make_unconfirmed_adjustments_and_expected_test_result( diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index b667abd63..d74a6703c 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -4,6 +4,7 @@ use std::time::SystemTime; pub trait PaymentAdjusterInner { fn now(&self) -> SystemTime; + fn largest_exceeding_balance_recently_qualified(&self) -> u128; fn transaction_fee_count_limit_opt(&self) -> Option; fn original_cw_service_fee_balance_minor(&self) -> u128; fn unallocated_cw_service_fee_balance_minor(&self) -> u128; @@ -13,6 +14,7 @@ pub trait PaymentAdjusterInner { pub struct PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, + largest_exceeding_balance_recently_qualified: u128, original_cw_service_fee_balance_minor: u128, unallocated_cw_service_fee_balance_minor: u128, } @@ -22,10 +24,12 @@ impl PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, cw_service_fee_balance_minor: u128, + largest_exceeding_balance_recently_qualified: u128, ) -> Self { Self { now, transaction_fee_count_limit_opt, + largest_exceeding_balance_recently_qualified, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } @@ -36,6 +40,11 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { fn now(&self) -> SystemTime { self.now } + + fn largest_exceeding_balance_recently_qualified(&self) -> u128 { + self.largest_exceeding_balance_recently_qualified + } + fn transaction_fee_count_limit_opt(&self) -> Option { self.transaction_fee_count_limit_opt } @@ -69,6 +78,13 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull { fn now(&self) -> SystemTime { PaymentAdjusterInnerNull::panicking_operation("now()") } + + fn largest_exceeding_balance_recently_qualified(&self) -> u128 { + PaymentAdjusterInnerNull::panicking_operation( + "largest_exceeding_balance_recently_qualified()", + ) + } + fn transaction_fee_count_limit_opt(&self) -> Option { PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") } @@ -97,10 +113,12 @@ mod tests { let now = SystemTime::now(); let transaction_fee_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; + let largest_exceeding_balance_recently_qualified = 44_555_666; let result = PaymentAdjusterInnerReal::new( now, transaction_fee_count_limit_opt, cw_service_fee_balance, + largest_exceeding_balance_recently_qualified, ); assert_eq!(result.now, now); @@ -115,6 +133,10 @@ mod tests { assert_eq!( result.unallocated_cw_service_fee_balance_minor, cw_service_fee_balance + ); + assert_eq!( + result.largest_exceeding_balance_recently_qualified, + largest_exceeding_balance_recently_qualified ) } @@ -128,6 +150,17 @@ mod tests { let _ = subject.now(); } + #[test] + #[should_panic( + expected = "Broken code: Called the null implementation of the largest_exceeding_balance_recently_qualified() \ + method in PaymentAdjusterInner" + )] + fn inner_null_calling_largest_exceeding_balance_recently_qualified() { + let subject = PaymentAdjusterInnerNull {}; + + let _ = subject.largest_exceeding_balance_recently_qualified(); + } + #[test] #[should_panic( expected = "Broken code: Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 5cb98c9cc..8db1a3b7c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -70,6 +70,20 @@ pub fn find_largest_weight(weighted_accounts: &[WeightedPayable]) -> u128 { find_largest_u128(&weights) } +pub fn find_largest_exceeding_balance(qualified_accounts: &[QualifiedPayableAccount]) -> u128 { + let diffs = qualified_accounts + .iter() + .map(|account| { + account + .bare_account + .balance_wei + .checked_sub(account.payment_threshold_intercept_minor) + .expect("should be: balance > intercept!") + }) + .collect::>(); + find_largest_u128(&diffs) +} + fn find_largest_u128(slice: &[u128]) -> u128 { slice .iter() @@ -95,15 +109,13 @@ pub fn adjust_account_balance_if_outweighed( { possibly_outweighed_accounts_diagnostics(¤t_adjustment_info); - let original_account = current_adjustment_info + let original_account = current_adjustment_info .weighted_account .qualified_account .bare_account; let proposed_full_balance = original_account.balance_wei; - let almost_finalized_account = AdjustedAccountBeforeFinalization::new( - original_account, - proposed_full_balance - ); + let almost_finalized_account = + AdjustedAccountBeforeFinalization::new(original_account, proposed_full_balance); outweighed.push(almost_finalized_account); } else { @@ -306,8 +318,10 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, - find_largest_u128, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, + find_largest_exceeding_balance, find_largest_u128, zero_affordable_accounts_found, + ConsumingWalletExhaustingStatus, }; + use crate::accountant::payment_adjuster::test_utils::make_non_guaranteed_unconfirmed_adjustment; use crate::accountant::test_utils::{ make_non_guaranteed_qualified_payable, make_payable_account, }; @@ -362,6 +376,21 @@ mod tests { assert_eq!(result, 456565) } + #[test] + fn find_largest_exceeding_balance_works() { + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.bare_account.balance_wei = 5_000_000_000; + account_1.payment_threshold_intercept_minor = 2_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(222); + account_2.bare_account.balance_wei = 4_000_000_000; + account_2.payment_threshold_intercept_minor = 800_000_000; + let qualified_accounts = &[account_1, account_2]; + + let result = find_largest_exceeding_balance(qualified_accounts); + + assert_eq!(result, 4_000_000_000 - 800_000_000) + } + #[test] fn multiplication_coefficient_is_based_on_cw_balance_if_largest_then_the_largest_weight() { let cw_service_fee_balance_minor = 12345678; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 9f13e1a7f..377068149 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -41,8 +41,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, - exhaust_cw_till_the_last_drop, sort_in_descendant_order_by_weights, sum_as, - zero_affordable_accounts_found, + exhaust_cw_till_the_last_drop, find_largest_exceeding_balance, + sort_in_descendant_order_by_weights, sum_as, zero_affordable_accounts_found, }; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::service_fee_adjuster::{ @@ -134,8 +134,15 @@ impl PaymentAdjuster for PaymentAdjusterReal { let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment; + let largest_exceeding_balance_recently_qualified = + find_largest_exceeding_balance(&qualified_payables); - self.initialize_inner(initial_service_fee_balance_minor, required_adjustment, now); + self.initialize_inner( + initial_service_fee_balance_minor, + required_adjustment, + largest_exceeding_balance_recently_qualified, + now, + ); let sketched_debug_info_opt = self.sketch_debug_info_opt(&qualified_payables); @@ -175,6 +182,7 @@ impl PaymentAdjusterReal { &mut self, cw_service_fee_balance: u128, required_adjustment: Adjustment, + largest_exceeding_balance_recently_qualified: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { @@ -188,6 +196,7 @@ impl PaymentAdjusterReal { now, transaction_fee_limitation_opt, cw_service_fee_balance, + largest_exceeding_balance_recently_qualified, ); self.inner = Box::new(inner); @@ -560,7 +569,9 @@ mod tests { AdjustedAccountBeforeFinalization, AdjustmentIterationResult, SpecialHandling, UnconfirmedAdjustment, WeightedPayable, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::weights_total; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + find_largest_exceeding_balance, weights_total, + }; use crate::accountant::payment_adjuster::service_fee_adjuster::{ AdjustmentComputer, ServiceFeeAdjusterReal, }; @@ -608,6 +619,52 @@ mod tests { let _ = result.inner.unallocated_cw_service_fee_balance_minor(); } + fn test_initialize_inner_works( + required_adjustment: Adjustment, + expected_tx_fee_limit_opt_result: Option, + ) { + let mut subject = PaymentAdjusterReal::default(); + let cw_service_fee_balance = 111_222_333_444; + let largest_exceeding_balance_recently_qualified = 3_555_666; + let now = SystemTime::now(); + + subject.initialize_inner( + cw_service_fee_balance, + required_adjustment, + largest_exceeding_balance_recently_qualified, + now, + ); + + assert_eq!(subject.inner.now(), now); + assert_eq!( + subject.inner.transaction_fee_count_limit_opt(), + expected_tx_fee_limit_opt_result + ); + assert_eq!( + subject.inner.original_cw_service_fee_balance_minor(), + cw_service_fee_balance + ); + assert_eq!( + subject.inner.unallocated_cw_service_fee_balance_minor(), + cw_service_fee_balance + ); + assert_eq!( + subject.inner.largest_exceeding_balance_recently_qualified(), + largest_exceeding_balance_recently_qualified + ) + } + + #[test] + fn initialize_inner_processes_works() { + test_initialize_inner_works(Adjustment::ByServiceFee, None); + test_initialize_inner_works( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 5, + }, + Some(5), + ); + } + #[test] fn search_for_indispensable_adjustment_happy_path() { init_test_logging(); @@ -840,7 +897,7 @@ mod tests { .calculate_result(1_000_000_002) .calculate_result(1_000_000_001) .calculate_result(1_000_000_003); - let subject = make_initialized_subject(None, None, Some(calculator), None); + let subject = make_initialized_subject(None, None, Some(calculator), Some(12345678), None); let make_account = |n: u64| { let account = make_non_guaranteed_qualified_payable(n); let wallet = account.bare_account.wallet.clone(); @@ -875,13 +932,6 @@ mod tests { let now = SystemTime::now(); let cw_service_fee_balance_minor = multiple_by_billion(3_500_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let mut subject = - make_initialized_subject(None, Some(cw_service_fee_balance_minor), None, None); - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(cw_service_fee_balance_minor / 2) - .determine_limit_params(&determine_limit_params_arc); - subject.disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); let mut account_1 = make_qualified_payable_by_wallet("abc"); let balance_1 = multiple_by_billion(3_000_000); account_1.bare_account.balance_wei = balance_1; @@ -891,8 +941,22 @@ mod tests { let wallet_2 = account_2.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); account_2.bare_account.balance_wei = balance_2; + let largest_exceeding_balance = (balance_1 - account_1.payment_threshold_intercept_minor) + .max(balance_2 - account_2.payment_threshold_intercept_minor); + let mut subject = make_initialized_subject( + None, + Some(cw_service_fee_balance_minor), + None, + Some(largest_exceeding_balance), + None, + ); + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(cw_service_fee_balance_minor / 2) + .determine_limit_params(&determine_limit_params_arc); + subject.disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); let weighted_payables_in_descending_order = vec![ - WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), + WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), WeightedPayable::new(account_1, multiple_by_billion(2_000_100)), ]; @@ -920,7 +984,9 @@ mod tests { // Outweighed accounts always take the first places assert_eq!( &first_returned_account.original_account, - &weighted_payables_in_descending_order[0].qualified_account.bare_account + &weighted_payables_in_descending_order[0] + .qualified_account + .bare_account ); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, @@ -929,7 +995,9 @@ mod tests { let second_returned_account = result.remove(0); assert_eq!( &second_returned_account.original_account, - &weighted_payables_in_descending_order[1].qualified_account.bare_account + &weighted_payables_in_descending_order[1] + .qualified_account + .bare_account ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, @@ -955,10 +1023,12 @@ mod tests { original_balance_of_outweighed_account: u128, outweighed_by_multiple_of: f64, ) { + let garbage_largest_exceeding_balance_recently_qualified = 123456789; subject.inner = Box::new(PaymentAdjusterInnerReal::new( SystemTime::now(), None, cw_service_fee_balance_minor, + garbage_largest_exceeding_balance_recently_qualified, )); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); @@ -1016,7 +1086,12 @@ mod tests { let payables = vec![account_1, account_2, account_3]; let qualified_payables = make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(2_000_000_000)) + .calculate_result(0) + .calculate_result(0); let mut subject = PaymentAdjusterReal::new(); + subject.calculators.push(Box::new(calculator_mock)); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); let service_fee_balance_in_minor_units = balance_2; @@ -1028,7 +1103,10 @@ mod tests { &subject.logger, ); // If concluded at the entry into the PaymentAdjuster that it has no point going off - // because away the least demanding account cannot be satisfied we would get an error here + // because away the least demanding account cannot be satisfied we would get an error here. + // However, it can only assess the balance (that early - in the real world) and accounts + // with the smallest balance is outplayed by the other one gaining some kind of extra + // significance assert_eq!(analysis_result, Ok(true)); let agent = { let mock = BlockchainAgentMock::default() @@ -1047,7 +1125,10 @@ mod tests { let err = match result { Err(e) => e, - Ok(_) => panic!("we expected to get an error but it was ok"), + Ok(ok) => panic!( + "we expected to get an error but it was ok: {:?}", + ok.affordable_accounts + ), }; assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated) } @@ -2144,7 +2225,14 @@ mod tests { creditor_thresholds: CreditorThresholds::new(gwei_to_wei::(10_000)), }; let cw_service_fee_balance_minor = gwei_to_wei::(3_000); - let context = PaymentAdjusterInnerReal::new(now, None, cw_service_fee_balance_minor); + let exceeding_balance = qualified_payable.bare_account.balance_wei + - qualified_payable.payment_threshold_intercept_minor; + let context = PaymentAdjusterInnerReal::new( + now, + None, + cw_service_fee_balance_minor, + exceeding_balance, + ); let _ = payment_adjuster .calculators .into_iter() @@ -2177,18 +2265,14 @@ mod tests { let mut account_1 = nominal_account_1; account_1.bare_account.balance_wei += 123_456_789; let mut account_2 = nominal_account_2; - account_2.bare_account.last_paid_timestamp = account_2 - .bare_account - .last_paid_timestamp - .checked_sub(Duration::from_secs(4_000)) - .unwrap(); - [(account_1, 8000000123466789), (account_2, 8000000000014000)] + account_2.bare_account.balance_wei += 999_999_999; + [(account_1, 8000001876543209), (account_2, 8000000999999999)] }, ] }; // This is the value that is computed if the account stays unmodified. Same for both nominal // accounts. - let current_nominal_weight = 8000000000010000; + let current_nominal_weight = 8000000000000000; test_calculators_reactivity(input_matrix, current_nominal_weight) } @@ -2306,8 +2390,15 @@ mod tests { now: SystemTime, cw_service_fee_balance_minor: u128, ) -> Vec { - let mut subject = - make_initialized_subject(Some(now), Some(cw_service_fee_balance_minor), None, None); + let largest_exceeding_balance_recently_qualified = + find_largest_exceeding_balance(&qualified_payables); + let mut subject = make_initialized_subject( + Some(now), + Some(cw_service_fee_balance_minor), + None, + Some(largest_exceeding_balance_recently_qualified), + None, + ); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); let service_fee_adjuster_mock = ServiceFeeAdjusterMock::default() // We use this container to intercept those values we are after diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index b1d8bba14..8272ba116 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -37,6 +37,7 @@ pub fn make_initialized_subject( now_opt: Option, cw_service_fee_balance_minor_opt: Option, criterion_calculator_mock_opt: Option, + largest_exceeding_balance_recently_qualified: Option, logger_opt: Option, ) -> PaymentAdjusterReal { let cw_service_fee_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); @@ -47,6 +48,7 @@ pub fn make_initialized_subject( now_opt.unwrap_or(SystemTime::now()), None, cw_service_fee_balance_minor, + largest_exceeding_balance_recently_qualified.unwrap_or(0), )); if let Some(calculator) = criterion_calculator_mock_opt { subject.calculators = vec![Box::new(calculator)] From 4f8e58609813e58e5f9cd0b98343edb54eba39b5 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 9 Apr 2024 12:27:29 +0200 Subject: [PATCH 156/250] GH-711-b: deleted 'adjust_last_one' utils --- .../payment_adjuster/adjustment_runners.rs | 108 +------------ node/src/accountant/payment_adjuster/mod.rs | 143 ------------------ 2 files changed, 1 insertion(+), 250 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index c55fa6f4c..bb84496e2 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -23,16 +23,6 @@ use std::vec; pub trait AdjustmentRunner { type ReturnType; - //TODO old code - // This specialized method: - // a) helps with writing tests targeting edge cases, - // b) allows to avoid performing unnecessary computation for an evident result - fn adjust_last_one( - &self, - payment_adjuster: &PaymentAdjusterReal, - last_account: QualifiedPayableAccount, - ) -> Self::ReturnType; - fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, @@ -48,16 +38,6 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { PaymentAdjusterError, >; - fn adjust_last_one( - &self, - payment_adjuster: &PaymentAdjusterReal, - last_account: QualifiedPayableAccount, - ) -> Self::ReturnType { - todo!("to be pulled out"); - let account_opt = payment_adjuster.adjust_last_account_opt(last_account); - Ok(Either::Left(empty_or_single_element_vector(account_opt))) - } - fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, @@ -85,16 +65,6 @@ pub struct ServiceFeeOnlyAdjustmentRunner {} impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { type ReturnType = Vec; - fn adjust_last_one( - &self, - payment_adjuster: &PaymentAdjusterReal, - last_account: QualifiedPayableAccount, - ) -> Self::ReturnType { - todo!("to be pulled out"); - let account_opt = payment_adjuster.adjust_last_account_opt(last_account); - empty_or_single_element_vector(account_opt) - } - fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, @@ -117,21 +87,11 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { } } -fn empty_or_single_element_vector( - adjusted_account_opt: Option, -) -> Vec { - match adjusted_account_opt { - Some(elem) => vec![elem], - None => vec![], - } -} - #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - empty_or_single_element_vector, AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, - TransactionAndServiceFeeAdjustmentRunner, + AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, @@ -151,53 +111,6 @@ mod tests { use std::fmt::Debug; use std::time::{Duration, SystemTime}; - fn test_adjust_last_one( - subject: AR, - expected_return_type_finalizer: fn(Vec) -> RT, - ) where - AR: AdjustmentRunner, - RT: Debug + PartialEq, - { - todo!() - // let now = SystemTime::now(); - // let wallet = make_wallet("abc"); - // let mut qualified_payable = make_non_guaranteed_qualified_payable(111); - // qualified_payable.bare_account.balance_wei = 9_000_000_000; - // qualified_payable.payment_threshold_intercept_minor = 7_000_000_000; - // qualified_payable - // .creditor_thresholds - // .permanent_debt_allowed_wei = 2_000_000_000; - // let cw_balance = 8_645_123_505; - // let adjustment = Adjustment::ByServiceFee; - // let mut payment_adjuster = PaymentAdjusterReal::new(); - // payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); - // - // let result = subject.adjust_last_one(&mut payment_adjuster, qualified_payable.clone()); - // - // assert_eq!( - // result, - // expected_return_type_finalizer(vec![AdjustedAccountBeforeFinalization { - // weighted_account: qualified_payable, - // proposed_adjusted_balance_minor: cw_balance, - // }]) - // ) - } - - #[test] - fn transaction_and_service_fee_adjust_last_one_works() { - test_adjust_last_one( - TransactionAndServiceFeeAdjustmentRunner {}, - |expected_vec| Ok(Either::Left(expected_vec)), - ) - } - - #[test] - fn service_fee_only_adjust_last_one_works() { - test_adjust_last_one(ServiceFeeOnlyAdjustmentRunner {}, |expected_vec| { - expected_vec - }) - } - fn initialize_payment_adjuster( now: SystemTime, service_fee_balance: u128, @@ -330,23 +243,4 @@ mod tests { // If the transaction fee adjustment had been available to be performed, only one account // would've been returned. This test passes } - - #[test] - fn empty_or_single_element_vector_for_none() { - let result = empty_or_single_element_vector(None); - - assert_eq!(result, vec![]) - } - - #[test] - fn empty_or_single_element_vector_for_some() { - todo!() - // let account_info = AdjustedAccountBeforeFinalization { - // weighted_account: make_non_guaranteed_qualified_payable(123), - // proposed_adjusted_balance_minor: 123_456_789, - // }; - // let result = empty_or_single_element_vector(Some(account_info.clone())); - // - // assert_eq!(result, vec![account_info]) - } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 377068149..8759240f5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -453,45 +453,6 @@ impl PaymentAdjusterReal { accounts_before_and_after_debug(sketched_debug_info, affordable_accounts) }) } - - fn adjust_last_account_opt( - &self, - last_payable: QualifiedPayableAccount, - ) -> Option { - todo!() - // let cw_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - // let proposed_adjusted_balance = if last_payable - // .bare_account - // .balance_wei - // .checked_sub(cw_balance) - // == None - // { - // last_payable.bare_account.balance_wei - // } else { - // diagnostics!( - // "LAST REMAINING ACCOUNT", - // "Balance adjusted to {} by exhausting the cw balance fully", - // cw_balance - // ); - // - // cw_balance - // }; - // // TODO the Disqualification check really makes sense only if we assigned less than the full balance!! - // let mut proposed_adjustment_vec = vec![UnconfirmedAdjustment::new( - // WeightedPayable::new(last_payable, u128::MAX), // The weight doesn't matter really and is made up - // proposed_adjusted_balance, - // )]; - // - // match self - // .disqualification_arbiter - // .try_finding_an_account_to_disqualify_in_this_iteration( - // &proposed_adjustment_vec, - // &self.logger, - // ) { - // Some(_) => None, - // None => Some(proposed_adjustment_vec.remove(0).non_finalized_account), - // } - } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1214,110 +1175,6 @@ mod tests { assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp) } - fn prepare_subject( - cw_balance: u128, - now: SystemTime, - disqualification_gauge: DisqualificationGaugeMock, - ) -> PaymentAdjusterReal { - let adjustment = Adjustment::ByServiceFee; - let mut payment_adjuster = PaymentAdjusterReal::new(); - todo!("is this necessary?"); - //payment_adjuster.initialize_inner(cw_balance.into(), adjustment, now); - payment_adjuster.disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - payment_adjuster - } - - #[test] - fn adjust_last_account_opt_for_balance_smaller_than_cw_but_no_need_to_disqualify() { - let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let now = SystemTime::now(); - let account_balance = 4_500_600; - let cw_balance = 4_500_601; - let payable = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: account_balance, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; - let qualified_payable = - QualifiedPayableAccount::new(payable, 111222333, CreditorThresholds::new(1000000)); - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_params(&determine_limit_params_arc) - .determine_limit_result(4_500_600); - let payment_adjuster = prepare_subject(cw_balance, now, disqualification_gauge); - - let result = payment_adjuster.adjust_last_account_opt(qualified_payable.clone()); - - assert_eq!( - result, - Some(AdjustedAccountBeforeFinalization { - original_account: qualified_payable.bare_account.clone(), - proposed_adjusted_balance_minor: cw_balance, - }) - ); - let determine_limit_params = determine_limit_params_arc.lock().unwrap(); - assert_eq!( - *determine_limit_params, - vec![( - qualified_payable.bare_account.balance_wei, - qualified_payable.payment_threshold_intercept_minor, - qualified_payable - .creditor_thresholds - .permanent_debt_allowed_wei - )] - ) - } - - fn test_adjust_last_account_opt_when_account_disqualified( - cw_balance: u128, - disqualification_gauge: DisqualificationGaugeMock, - ) { - let now = SystemTime::now(); - let garbage_balance_wei = 123456789; - let garbage_last_paid_timestamp = SystemTime::now(); - let garbage_payment_threshold_intercept_minor = 33333333; - let garbage_permanent_debt_allowed_wei = 11111111; - let payable = PayableAccount { - wallet: make_wallet("abc"), - balance_wei: garbage_balance_wei, - last_paid_timestamp: garbage_last_paid_timestamp, - pending_payable_opt: None, - }; - let qualified_payable = QualifiedPayableAccount { - bare_account: payable, - payment_threshold_intercept_minor: garbage_payment_threshold_intercept_minor, - creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, - }, - }; - let payment_adjuster = prepare_subject(cw_balance, now, disqualification_gauge); - - let result = payment_adjuster.adjust_last_account_opt(qualified_payable); - - assert_eq!(result, None) - } - - #[test] - fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_evens_the_edge() - { - let cw_balance = 1_000_111; - let disqualification_gauge = - DisqualificationGaugeMock::default().determine_limit_result(1_000_111); - - test_adjust_last_account_opt_when_account_disqualified(cw_balance, disqualification_gauge) - } - - #[test] - fn account_facing_much_smaller_cw_balance_hits_disqualification_when_adjustment_slightly_under() - { - let cw_balance = 1_000_111; - let disqualification_gauge = - DisqualificationGaugeMock::default().determine_limit_result(1_000_110); - - test_adjust_last_account_opt_when_account_disqualified(cw_balance, disqualification_gauge) - } - #[test] fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { init_test_logging(); From 9ce6272f3bfd2377f4f61678a5eb5e4c2fb08fd1 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 12 Apr 2024 00:00:24 +0200 Subject: [PATCH 157/250] GH-711-b: fixing various stuff; also the a bit lowered balance for outweighed accounts --- .../payment_adjuster/adjustment_runners.rs | 66 ++--- .../payment_adjuster/diagnostics.rs | 5 +- .../disqualification_arbiter.rs | 177 ++++++------ .../payment_adjuster/loading_test/mod.rs | 4 +- .../accountant/payment_adjuster/log_fns.rs | 77 ++++-- .../miscellaneous/data_structures.rs | 27 +- .../miscellaneous/helper_functions.rs | 236 +++++----------- node/src/accountant/payment_adjuster/mod.rs | 118 ++++---- .../payment_adjuster/preparatory_analyser.rs | 4 +- .../payment_adjuster/service_fee_adjuster.rs | 251 +++++++++++------- .../accountant/payment_adjuster/test_utils.rs | 37 +-- .../src/accountant/scanners/scanners_utils.rs | 2 +- 12 files changed, 457 insertions(+), 547 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index bb84496e2..f2f3ca4b7 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -6,10 +6,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; -use crate::accountant::QualifiedPayableAccount; use itertools::Either; use masq_lib::utils::convert_collection; -use std::vec; // TODO review this comment // There are only two runners. They perform adjustment either by both the transaction and service @@ -89,27 +87,20 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { #[cfg(test)] mod tests { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, + AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; - use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, - }; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; - use crate::accountant::test_utils::{ - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - }; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; - use itertools::Either; - use std::fmt::Debug; - use std::time::{Duration, SystemTime}; + use std::time::SystemTime; fn initialize_payment_adjuster( now: SystemTime, @@ -197,41 +188,38 @@ mod tests { #[test] fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { - let now = SystemTime::now(); - let wallet_1 = make_wallet("abc"); let mut payment_thresholds = PaymentThresholds::default(); payment_thresholds.maturity_threshold_sec = 100; payment_thresholds.threshold_interval_sec = 1000; payment_thresholds.permanent_debt_allowed_gwei = 1; - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 5_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(2_500)).unwrap(), - pending_payable_opt: None, - }; + let balance = 5_000_000_000; + let mut account = make_non_guaranteed_qualified_payable(111); + account.bare_account.balance_wei = 5_000_000_000; + account.payment_threshold_intercept_minor = 4_000_000_000; + account.creditor_thresholds = CreditorThresholds::new(1_000_000_000); + let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); - let mut account_2 = account_1.clone(); - account_2.wallet = wallet_2.clone(); - let wallet_3 = make_wallet("ghj"); - let mut account_3 = account_1.clone(); - account_3.wallet = wallet_3; - let accounts = vec![account_1, account_2, account_3]; - let qualified_payables = - make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); - let largest_exceeding_balance_recently_qualified = - find_largest_exceeding_balance(&qualified_payables); + let mut account_1 = account.clone(); + account_1.bare_account.wallet = wallet_1.clone(); + let mut account_2 = account; + account_2.bare_account.wallet = wallet_2.clone(); let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; - let service_fee_balance_wei = 10_000_000_000; - let mut payment_adjuster = initialize_payment_adjuster( - now, - service_fee_balance_wei, - largest_exceeding_balance_recently_qualified, + let service_fee_balance_minor = (5 * balance) / 3; + let mut payment_adjuster = PaymentAdjusterReal::new(); + payment_adjuster.initialize_inner( + service_fee_balance_minor, + adjustment, + 123456789, + SystemTime::now(), ); let subject = ServiceFeeOnlyAdjustmentRunner {}; - let criteria_and_accounts = - payment_adjuster.calculate_weights_for_accounts(qualified_payables); + let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { + qualified_account: account, + weight: 4_000_000_000, + }; + let criteria_and_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; let result = subject.adjust_accounts(&mut payment_adjuster, criteria_and_accounts); diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/diagnostics.rs index fc54d1936..6a8348ce7 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/diagnostics.rs @@ -93,6 +93,7 @@ pub fn collection_diagnostics( pub mod ordinary_diagnostic_functions { use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::diagnostics; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, }; @@ -100,7 +101,7 @@ pub mod ordinary_diagnostic_functions { use crate::sub_lib::wallet::Wallet; use thousands::Separable; - pub fn possibly_outweighed_accounts_diagnostics(account_info: &UnconfirmedAdjustment) { + pub fn outweighed_accounts_diagnostics(account_info: &UnconfirmedAdjustment) { diagnostics!( &account_info .weighted_account @@ -177,7 +178,7 @@ pub mod ordinary_diagnostic_functions { } pub fn try_finding_an_account_to_disqualify_diagnostics( - disqualification_suspected_accounts: &[&UnconfirmedAdjustment], + disqualification_suspected_accounts: &[DisqualificationSuspectedAccount], wallet: &Wallet, ) { diagnostics!( diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 1aa6a8b71..08eb77e66 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -5,9 +5,7 @@ use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functi try_finding_an_account_to_disqualify_diagnostics, }; use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, -}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; @@ -41,12 +39,7 @@ impl DisqualificationArbiter { let account_to_disqualify = Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); - let wallet = account_to_disqualify - .weighted_account - .qualified_account - .bare_account - .wallet - .clone(); + let wallet = account_to_disqualify.wallet.clone(); try_finding_an_account_to_disqualify_diagnostics( &disqualification_suspected_accounts, @@ -55,11 +48,8 @@ impl DisqualificationArbiter { debug!( logger, - "Found accounts {:?} whose proposed adjusted balances didn't get above the limit \ - for disqualification. Chose the least desirable disqualified account as the one \ - with the biggest balance, which is {}. To be thrown away in this iteration.", + "Found accounts {:?} applying for disqualification", disqualification_suspected_accounts, - wallet ); info_log_for_disqualified_account(logger, account_to_disqualify); @@ -86,7 +76,7 @@ impl DisqualificationArbiter { fn list_accounts_nominated_for_disqualification<'unconfirmed_adj>( &self, unconfirmed_adjustments: &'unconfirmed_adj [UnconfirmedAdjustment], - ) -> Vec<&'unconfirmed_adj UnconfirmedAdjustment> { + ) -> Vec> { unconfirmed_adjustments .iter() .flat_map(|adjustment_info| { @@ -101,7 +91,11 @@ impl DisqualificationArbiter { proposed_adjusted_balance, disqualification_edge, ); - Some(adjustment_info) + + let suspected_account: DisqualificationSuspectedAccount = + (adjustment_info.into(), disqualification_edge).into(); + + Some(suspected_account) } else { None } @@ -109,15 +103,15 @@ impl DisqualificationArbiter { .collect() } - fn find_account_with_smallest_weight<'account>( - accounts: &'account [&'account UnconfirmedAdjustment], - ) -> &'account UnconfirmedAdjustment { - let first_account = &accounts.first().expect("collection was empty"); + fn find_account_with_smallest_weight<'accounts>( + accounts: &'accounts [DisqualificationSuspectedAccount], + ) -> &'accounts DisqualificationSuspectedAccount<'accounts> { + let first_account = accounts.first().expect("collection was empty"); accounts.iter().fold( - **first_account, + first_account, |with_smallest_weight_so_far, current| match Ord::cmp( - ¤t.weighted_account.weight, - &with_smallest_weight_so_far.weighted_account.weight, + ¤t.weight, + &with_smallest_weight_so_far.weight, ) { Ordering::Less => current, Ordering::Greater => with_smallest_weight_so_far, @@ -127,6 +121,37 @@ impl DisqualificationArbiter { } } +#[derive(Debug, PartialEq, Eq)] +pub struct DisqualificationSuspectedAccount<'account> { + pub wallet: &'account Wallet, + pub weight: u128, + // For an Info log message + pub proposed_adjusted_balance_minor: u128, + pub disqualification_edge: u128, +} + +impl<'unconfirmed_accounts> From<(&'unconfirmed_accounts UnconfirmedAdjustment, u128)> + for DisqualificationSuspectedAccount<'unconfirmed_accounts> +{ + fn from( + (unconfirmed_account, disqualification_edge): ( + &'unconfirmed_accounts UnconfirmedAdjustment, + u128, + ), + ) -> Self { + DisqualificationSuspectedAccount { + wallet: &unconfirmed_account + .weighted_account + .qualified_account + .bare_account + .wallet, + weight: unconfirmed_account.weighted_account.weight, + proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, + disqualification_edge, + } + } +} + pub trait DisqualificationGauge { fn determine_limit( &self, @@ -194,27 +219,24 @@ mod tests { use crate::accountant::db_access_objects::utils::from_time_t; use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, + DisqualificationSuspectedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, - }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - find_largest_exceeding_balance, weights_total, + UnconfirmedAdjustment, WeightedPayable, }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, DisqualificationGaugeMock, }; - use crate::accountant::test_utils::{ - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - make_payable_account, - }; + use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; - use std::time::{Duration, SystemTime}; + use masq_lib::utils::convert_collection; + use std::time::SystemTime; #[test] fn constants_are_correct() { @@ -382,38 +404,27 @@ mod tests { #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { - let idx_of_expected_result = 1; - let (adjustments, expected_result) = make_unconfirmed_adjustments_and_expected_test_result( - vec![1004, 1000, 1002, 1001], - idx_of_expected_result, - ); - let referenced_unconfirmed_adjustments = by_reference(&adjustments); + let pairs = + make_pairs_of_unconfirmed_adjustments_and_dsq_edges(vec![1004, 1000, 1002, 1001]); + let dsq_suspected_accounts = make_dsq_suspected_accounts(&pairs); - let result = DisqualificationArbiter::find_account_with_smallest_weight( - &referenced_unconfirmed_adjustments, - ); + let result = + DisqualificationArbiter::find_account_with_smallest_weight(&dsq_suspected_accounts); - assert_eq!(result, &expected_result) + let expected_result = &dsq_suspected_accounts[1]; + assert_eq!(result, expected_result) } #[test] fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { - let idx_of_expected_result = 0; - let (adjustments, expected_result) = make_unconfirmed_adjustments_and_expected_test_result( - vec![1111, 1113, 1111], - idx_of_expected_result, - ); - let referenced_non_finalized_accounts = by_reference(&adjustments); - - let result = DisqualificationArbiter::find_account_with_smallest_weight( - &referenced_non_finalized_accounts, - ); + let pairs = make_pairs_of_unconfirmed_adjustments_and_dsq_edges(vec![1111, 1113, 1111]); + let dsq_suspected_accounts = make_dsq_suspected_accounts(&pairs); - assert_eq!(result, &expected_result) - } + let result = + DisqualificationArbiter::find_account_with_smallest_weight(&dsq_suspected_accounts); - fn by_reference(adjusted_accounts: &[UnconfirmedAdjustment]) -> Vec<&UnconfirmedAdjustment> { - adjusted_accounts.iter().collect() + let expected_result = &dsq_suspected_accounts[0]; + assert_eq!(result, expected_result) } #[test] @@ -490,17 +501,15 @@ mod tests { assert_eq!(result, Some(wallet_3)); } - fn make_unconfirmed_adjustments_and_expected_test_result( + fn make_pairs_of_unconfirmed_adjustments_and_dsq_edges( weights: Vec, - idx_of_expected_result: usize, - ) -> (Vec, UnconfirmedAdjustment) { - let init: (Vec, Option) = (vec![], None); - - let (adjustments, expected_result_opt) = weights.into_iter().enumerate().fold( - init, - |(mut adjustments_so_far, expected_result_opt_so_far), (actual_idx, weight)| { - let original_account = make_payable_account(actual_idx as u64); - let garbage_intercept = 2_000_000_000; // Unimportant for the tests this is used in; + ) -> Vec<(UnconfirmedAdjustment, u128)> { + weights + .into_iter() + .enumerate() + .map(|(index, weight)| { + let original_account = make_payable_account(index as u64); + let garbage_intercept = 2_000_000_000; let garbage_permanent_debt_allowed_wei = 1_111_111_111; let qualified_account = QualifiedPayableAccount { bare_account: original_account, @@ -509,22 +518,26 @@ mod tests { permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, }, }; - let garbage_proposed_balance = 1_000_000_000; // Same here - let new_adjustment_to_be_added = UnconfirmedAdjustment::new( - WeightedPayable::new(qualified_account, weight), - garbage_proposed_balance, - ); - let expected_result_opt = if expected_result_opt_so_far.is_none() - && actual_idx == idx_of_expected_result - { - Some(new_adjustment_to_be_added.clone()) - } else { - expected_result_opt_so_far - }; - adjustments_so_far.push(new_adjustment_to_be_added); - (adjustments_so_far, expected_result_opt) - }, - ); - (adjustments, expected_result_opt.unwrap()) + let garbage_proposed_balance = 1_000_000_000; + let garbage_dsq_edge = garbage_intercept - 1_500_000_000; + ( + UnconfirmedAdjustment::new( + WeightedPayable::new(qualified_account, weight), + garbage_proposed_balance, + ), + garbage_dsq_edge, + ) + }) + .collect() + } + + fn make_dsq_suspected_accounts( + accounts_and_dsq_edges: &[(UnconfirmedAdjustment, u128)], + ) -> Vec { + let with_referred_accounts: Vec<(&UnconfirmedAdjustment, u128)> = accounts_and_dsq_edges + .iter() + .map(|(account, dsq_edge)| (account, *dsq_edge)) + .collect(); + convert_collection(with_referred_accounts) } } diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 929f6f3bc..701b73040 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -9,9 +9,7 @@ use crate::accountant::payment_adjuster::{ }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; -use crate::accountant::test_utils::{ - make_guaranteed_qualified_payables, try_making_guaranteed_qualified_payables, -}; +use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/log_fns.rs index 703c2cff8..2996c1172 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/log_fns.rs @@ -1,9 +1,7 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, -}; +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::masq_lib::utils::ExpectValue; use crate::sub_lib::wallet::Wallet; use itertools::Itertools; @@ -16,11 +14,11 @@ use thousands::Separable; use web3::types::U256; const REFILL_RECOMMENDATION: &str = "\ -Please be aware that ignoring your debts might result in delinquency bans. In order to consume \ -services without limitations, you will need to put more funds into your consuming wallet."; +Please be aware that abandoning your debts is going to result in delinquency bans. In order to \ +consume services without limitations, you will need to place more funds into your consuming wallet."; const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ -Passed successfully adjustment by transaction fee but noticing critical scarcity of MASQ balance. \ -Operation will abort."; +Passed successfully adjustment by transaction fee, but by a second look, noticing of critical \ +shortage of MASQ balance. Operation will abort."; const BLANK_SPACE: &str = ""; @@ -120,26 +118,20 @@ pub fn accounts_before_and_after_debug( ) } -pub fn info_log_for_disqualified_account(logger: &Logger, account: &UnconfirmedAdjustment) { +pub fn info_log_for_disqualified_account( + logger: &Logger, + account: &DisqualificationSuspectedAccount, +) { info!( logger, - "Shortage of MASQ in your consuming wallet impacts on payable {}, ruled out from this \ - round of payments. The proposed adjustment {} wei was less than half of the recorded \ - debt, {} wei", - account - .weighted_account - .qualified_account - .bare_account - .wallet, + "Shortage of MASQ in your consuming wallet will impact payable {}, ruled out from this \ + round of payments. The proposed adjustment {} wei was below the disqualification limit \ + {} wei", + account.wallet, account .proposed_adjusted_balance_minor .separate_with_commas(), - account - .weighted_account - .qualified_account - .bare_account - .balance_wei - .separate_with_commas() + account.disqualification_edge.separate_with_commas() ) } @@ -182,22 +174,51 @@ pub fn log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger: &Lo #[cfg(test)] mod tests { + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::accountant::payment_adjuster::log_fns::{ - LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, REFILL_RECOMMENDATION, + info_log_for_disqualified_account, LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, + REFILL_RECOMMENDATION, }; + use crate::test_utils::make_wallet; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; #[test] fn constants_are_correct() { assert_eq!( REFILL_RECOMMENDATION, - "Please be aware that ignoring your debts might result in delinquency bans. In order to \ - consume services without limitations, you will need to put more funds into your \ - consuming wallet." + "Please be aware that abandoning your debts is going to result in delinquency bans. In \ + order to consume services without limitations, you will need to place more funds into \ + your consuming wallet." ); assert_eq!( LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, - "Passed successfully adjustment by transaction fee but noticing critical scarcity of \ - MASQ balance. Operation will abort." + "Passed successfully adjustment by transaction fee, but by a second look, noticing of \ + critical shortage of MASQ balance. Operation will abort." ) } + + #[test] + fn disqualification_log_properly_formatted() { + init_test_logging(); + let test_name = "disqualification_log_properly_formatted"; + let logger = Logger::new(test_name); + let wallet = make_wallet("aaa"); + let disqualified_account = DisqualificationSuspectedAccount { + wallet: &wallet, + weight: 0, + proposed_adjusted_balance_minor: 1_555_666_777, + disqualification_edge: 2_000_000_000, + }; + + info_log_for_disqualified_account(&logger, &disqualified_account); + + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {}: Shortage of MASQ \ + in your consuming wallet will impact payable 0x0000000000000000000000000000000000616161, \ + ruled out from this round of payments. The proposed adjustment 1,555,666,777 wei was \ + below the disqualification limit 2,000,000,000 wei", + test_name + )); + } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 791795065..c3baf7222 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -88,29 +88,6 @@ impl UnconfirmedAdjustment { } } -pub struct NonFinalizedAdjustmentWithResolution { - pub non_finalized_adjustment: AdjustedAccountBeforeFinalization, - pub adjustment_resolution: AdjustmentResolution, -} - -impl NonFinalizedAdjustmentWithResolution { - pub fn new( - non_finalized_adjustment: AdjustedAccountBeforeFinalization, - adjustment_resolution: AdjustmentResolution, - ) -> Self { - Self { - non_finalized_adjustment, - adjustment_resolution, - } - } -} - -#[derive(Clone, Copy)] -pub enum AdjustmentResolution { - Finalize, - Revert, -} - pub struct TransactionCountsWithin16bits { pub affordable: u16, pub required: u16, @@ -130,9 +107,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, }; - use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, make_payable_account, - }; + use crate::accountant::test_utils::make_payable_account; use ethereum_types::U256; #[test] diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8db1a3b7c..930aed761 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -4,16 +4,12 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, - possibly_outweighed_accounts_diagnostics, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentResolution, NonFinalizedAdjustmentWithResolution, - UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::QualifiedPayableAccount; use itertools::{Either, Itertools}; -use std::cmp::Ordering; -use std::iter::successors; pub fn zero_affordable_accounts_found( accounts: &Either, Vec>, @@ -56,18 +52,8 @@ pub fn dump_unaffordable_accounts_by_txn_fee( pub fn compute_mul_coefficient_preventing_fractional_numbers( cw_service_fee_balance_minor: u128, - largest_weight_in_the_set: u128, ) -> u128 { - let max_value = cw_service_fee_balance_minor.max(largest_weight_in_the_set); - u128::MAX / max_value -} - -pub fn find_largest_weight(weighted_accounts: &[WeightedPayable]) -> u128 { - let weights = weighted_accounts - .iter() - .map(|account| account.weight) - .collect::>(); - find_largest_u128(&weights) + u128::MAX / cw_service_fee_balance_minor } pub fn find_largest_exceeding_balance(qualified_accounts: &[QualifiedPayableAccount]) -> u128 { @@ -90,41 +76,6 @@ fn find_largest_u128(slice: &[u128]) -> u128 { .fold(0, |largest_so_far, num| largest_so_far.max(*num)) } -pub fn adjust_account_balance_if_outweighed( - (mut outweighed, mut passing_through): ( - Vec, - Vec, - ), - mut current_adjustment_info: UnconfirmedAdjustment, -) -> ( - Vec, - Vec, -) { - if current_adjustment_info.proposed_adjusted_balance_minor - > current_adjustment_info - .weighted_account - .qualified_account - .bare_account - .balance_wei - { - possibly_outweighed_accounts_diagnostics(¤t_adjustment_info); - - let original_account = current_adjustment_info - .weighted_account - .qualified_account - .bare_account; - let proposed_full_balance = original_account.balance_wei; - let almost_finalized_account = - AdjustedAccountBeforeFinalization::new(original_account, proposed_full_balance); - - outweighed.push(almost_finalized_account); - } else { - passing_through.push(current_adjustment_info); - } - - (outweighed, passing_through) -} - pub fn exhaust_cw_till_the_last_drop( approved_accounts: Vec, original_cw_service_fee_balance_minor: u128, @@ -156,9 +107,6 @@ pub fn exhaust_cw_till_the_last_drop( run_cw_exhausting_on_possibly_sub_optimal_account_balances, ) .accounts_finalized_so_far - .into_iter() - .sorted_by(|account_a, account_b| Ord::cmp(&account_b.balance_wei, &account_a.balance_wei)) - .collect() } fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( @@ -224,10 +172,7 @@ impl ConsumingWalletExhaustingStatus { } fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { - let finalized_account = PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( - non_finalized_account_info, - AdjustmentResolution::Finalize, - )); + let finalized_account = PayableAccount::from(non_finalized_account_info); self.accounts_finalized_so_far.push(finalized_account); self } @@ -250,20 +195,20 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( .collect() } -pub fn nonzero_positive(x: u128) -> u128 { - if x == 0 { - 1 - } else { - x - } -} - impl From for PayableAccount { fn from(qualified_payable: QualifiedPayableAccount) -> Self { qualified_payable.bare_account } } +impl From for PayableAccount { + fn from(non_finalized_adjustment: AdjustedAccountBeforeFinalization) -> Self { + let mut account = non_finalized_adjustment.original_account; + account.balance_wei = non_finalized_adjustment.proposed_adjusted_balance_minor; + account + } +} + impl From for WeightedPayable { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { unconfirmed_adjustment.weighted_account @@ -272,13 +217,7 @@ impl From for WeightedPayable { impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - AdjustedAccountBeforeFinalization::new( - unconfirmed_adjustment - .weighted_account - .qualified_account - .bare_account, - unconfirmed_adjustment.proposed_adjusted_balance_minor, - ) + WeightedPayable::from(unconfirmed_adjustment).into() } } @@ -286,26 +225,9 @@ impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { let proposed_adjusted_balance_minor = weighted_account.qualified_account.bare_account.balance_wei; - AdjustedAccountBeforeFinalization { - original_account: weighted_account.qualified_account.bare_account, - proposed_adjusted_balance_minor, - } - } -} + let original_account = weighted_account.qualified_account.bare_account; -impl From for PayableAccount { - fn from(resolution_info: NonFinalizedAdjustmentWithResolution) -> Self { - match resolution_info.adjustment_resolution { - AdjustmentResolution::Finalize => PayableAccount { - balance_wei: resolution_info - .non_finalized_adjustment - .proposed_adjusted_balance_minor, - ..resolution_info.non_finalized_adjustment.original_account - }, - AdjustmentResolution::Revert => { - resolution_info.non_finalized_adjustment.original_account - } - } + AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) } } @@ -313,15 +235,13 @@ impl From for PayableAccount { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, find_largest_exceeding_balance, find_largest_u128, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; - use crate::accountant::payment_adjuster::test_utils::make_non_guaranteed_unconfirmed_adjustment; use crate::accountant::test_utils::{ make_non_guaranteed_qualified_payable, make_payable_account, }; @@ -392,87 +312,22 @@ mod tests { } #[test] - fn multiplication_coefficient_is_based_on_cw_balance_if_largest_then_the_largest_weight() { + fn compute_mul_coefficient_preventing_fractional_numbers_works() { let cw_service_fee_balance_minor = 12345678; - let largest_weight = 12345677; - let result = compute_mul_coefficient_preventing_fractional_numbers( - cw_service_fee_balance_minor, - largest_weight, - ); + let result = + compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance_minor); let expected_result = u128::MAX / cw_service_fee_balance_minor; assert_eq!(result, expected_result) } - #[test] - fn multiplication_coefficient_is_based_on_the_largest_weight_if_larger_then_cw_balance() { - let cw_service_fee_balance_minor = 12345677; - let largest_weight = 12345678; - - let result = compute_mul_coefficient_preventing_fractional_numbers( - cw_service_fee_balance_minor, - largest_weight, - ); - - let expected_result = u128::MAX / largest_weight; - assert_eq!(result, expected_result) - } - - #[test] - fn multiplication_coefficient_computed_when_both_parameters_the_same() { - let cw_service_fee_balance_minor = 111111; - let largest_weight = 111111; - - let result = compute_mul_coefficient_preventing_fractional_numbers( - cw_service_fee_balance_minor, - largest_weight, - ); - - let expected_result = u128::MAX / 111111; - assert_eq!(result, expected_result) - } - - #[test] - fn accounts_with_original_balances_equal_to_the_proposed_ones_are_not_outweighed() { - let payable = PayableAccount { - wallet: make_wallet("blah"), - balance_wei: 9_000_000_000, - last_paid_timestamp: SystemTime::now(), - pending_payable_opt: None, - }; - let garbage_payment_threshold_intercept = 1234567; - let garbage_creditor_thresholds = CreditorThresholds { - permanent_debt_allowed_wei: 1000000, - }; - let qualified_payable = QualifiedPayableAccount::new( - payable, - garbage_payment_threshold_intercept, - garbage_creditor_thresholds, - ); - let garbage_weight = 123456; - let garbage_proposed_adjusted_balance_minor = 9_000_000_000; - let unconfirmed_adjustment = UnconfirmedAdjustment::new( - WeightedPayable::new(qualified_payable, garbage_weight), - garbage_proposed_adjusted_balance_minor, - ); - let init = (vec![], vec![]); - - let (outweighed, ok) = - adjust_account_balance_if_outweighed(init, unconfirmed_adjustment.clone()); - - assert_eq!(outweighed, vec![]); - assert_eq!(ok, vec![unconfirmed_adjustment]) - } - fn make_non_finalized_adjusted_account( wallet: &Wallet, original_balance: u128, proposed_adjusted_balance: u128, ) -> AdjustedAccountBeforeFinalization { let garbage_last_paid_timestamp = SystemTime::now(); - let garbage_payment_threshold_intercept_minor = u128::MAX; - let garbage_permanent_debt_allowed_wei = 123456789; let payable_account = PayableAccount { wallet: wallet.clone(), balance_wei: original_balance, @@ -504,6 +359,37 @@ mod tests { ) } + #[test] + fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let non_finalized_account = + AdjustedAccountBeforeFinalization::new(original_payable_account.clone(), 123_456_789); + + let result = PayableAccount::from(non_finalized_account); + + original_payable_account.balance_wei = 123_456_789; + assert_eq!(result, original_payable_account) + } + + #[test] + fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let qualified_account = QualifiedPayableAccount::new( + original_payable_account.clone(), + 444_555_666, + CreditorThresholds::new(111_111_111), + ); + let weighted_account = WeightedPayable::new(qualified_account, 777_777_777); + + let result = AdjustedAccountBeforeFinalization::from(weighted_account); + + let expected_result = + AdjustedAccountBeforeFinalization::new(original_payable_account, 200_000_000); + assert_eq!(result, expected_result) + } + #[test] fn exhaustive_status_is_constructed_properly() { let cw_balance_remainder = 45678; @@ -571,18 +457,18 @@ mod tests { fn three_non_exhaustive_accounts_with_one_completely_refilled_one_partly_one_not_at_all() { // The smallest proposed adjusted balance gets refilled first, and then gradually on... let wallet_1 = make_wallet("abc"); - let original_requested_balance_1 = 54_000_000_000; - let proposed_adjusted_balance_1 = 53_898_000_000; + let original_requested_balance_1 = 41_000_000; + let proposed_adjusted_balance_1 = 39_700_000; let wallet_2 = make_wallet("def"); let original_requested_balance_2 = 33_500_000_000; - let proposed_adjusted_balance_2 = 33_487_999_999; + let proposed_adjusted_balance_2 = 32_487_999_999; let wallet_3 = make_wallet("ghi"); - let original_requested_balance_3 = 41_000_000; - let proposed_adjusted_balance_3 = 40_980_000; - let original_cw_balance = original_requested_balance_2 - + original_requested_balance_3 - + proposed_adjusted_balance_1 - - 2_000_000; + let original_requested_balance_3 = 50_000_000_000; + let proposed_adjusted_balance_3 = 43_000_000_000; + let original_cw_balance = original_requested_balance_1 + + proposed_adjusted_balance_2 + + proposed_adjusted_balance_3 + + 222_000_000; let non_finalized_adjusted_accounts = vec![ make_non_finalized_adjusted_account( &wallet_1, @@ -605,9 +491,9 @@ mod tests { exhaust_cw_till_the_last_drop(non_finalized_adjusted_accounts, original_cw_balance); let expected_resulted_balances = vec![ - (wallet_1, proposed_adjusted_balance_1), - (wallet_2, 33_498_000_000), - (wallet_3, original_requested_balance_3), + (wallet_1, original_requested_balance_1), + (wallet_2, proposed_adjusted_balance_2 + 222_000_000), + (wallet_3, proposed_adjusted_balance_3), ]; let check_sum: u128 = expected_resulted_balances .iter() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8759240f5..b5daff7d6 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -715,14 +715,14 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Transaction fee amount 16,499,999,000,000,000 wei \ - from your wallet will not cover anticipated fees to send 3 transactions. \ - Maximum is 2. The payments count needs to be adjusted." + "WARN: {test_name}: Transaction fee amount 16,499,999,000,000,000 wei from your wallet \ + will not cover anticipated fees to send 3 transactions. Maximum is 2. The payments \ + count needs to be adjusted." )); log_handler.exists_log_containing(&format!( - "INFO: {test_name}: Please be aware that \ - ignoring your debts might result in delinquency bans. In order to consume services without \ - limitations, you will need to put more funds into your consuming wallet." + "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ + delinquency bans. In order to consume services without limitations, you will need to \ + place more funds into your consuming wallet." )); } @@ -748,13 +748,13 @@ mod tests { assert_eq!(result, Ok(Some(Adjustment::ByServiceFee))); let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,000,001 \ - wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of the MASQ \ - token. Adjustment in their count or the amounts is required.")); + log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,\ + 000,001 wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of \ + the MASQ token. Adjustment in their count or the amounts is required.")); log_handler.exists_log_containing(&format!( - "INFO: {test_name}: Please be aware that \ - ignoring your debts might result in delinquency bans. In order to consume services without \ - limitations, you will need to put more funds into your consuming wallet." + "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ + delinquency bans. In order to consume services without limitations, you will need to \ + place more funds into your consuming wallet." )); } @@ -1169,7 +1169,7 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_affordable_accounts = { vec![account_2, account_3] }; + let expected_affordable_accounts = { vec![account_3, account_2] }; assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp) @@ -1224,9 +1224,9 @@ mod tests { assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated); let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { format!( - "INFO: {test_name}: Shortage of MASQ in your consuming wallet impacts on payable \ - {wallet}, ruled out from this round of payments. The proposed adjustment {} wei \ - was less than half of the recorded debt, {} wei", + "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ + {wallet}, ruled out from this round of payments. The proposed adjustment {} wei was \ + below the disqualification limit {} wei", proposed_adjusted_balance_in_this_iteration.separate_with_commas(), (*MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR).separate_with_commas() ) @@ -1243,59 +1243,67 @@ mod tests { )); log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616832", - 1000, + 999, )); } - #[test] - fn qualified_accounts_count_before_equals_the_payments_count_after() { - // Meaning adjustment by service fee but no account elimination - init_test_logging(); - let test_name = "qualified_accounts_count_before_equals_the_payments_count_after"; - let now = SystemTime::now(); - let balance_1 = 4_444_444_444_444_444_444; - let qualified_account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("abc"), - balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(101_000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let balance_2 = 6_000_000_000_000_000_000; - let qualified_account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("def"), - balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(150_000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let balance_3 = 6_666_666_666_000_000_000; - let qualified_account_3 = QualifiedPayableAccount::new( + fn meaningless_timestamp() -> SystemTime { + SystemTime::now() + } + + // This function should take just such args that affects the adjustment mechanism, except + // those that work as pure criteria parameters (= make up the weights). These should be + // limited to minimum because in this kind of tests we don't want to be burdened with their + // consideration. + fn make_plucked_qualified_account( + wallet_addr_fragment: &str, + balance_major: u128, + threshold_intercept_major: u128, + permanent_debt_allowed_major: u128, + ) -> QualifiedPayableAccount { + QualifiedPayableAccount::new( PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(100_000)).unwrap(), + wallet: make_wallet(wallet_addr_fragment), + balance_wei: multiple_by_billion(balance_major), + last_paid_timestamp: meaningless_timestamp(), pending_payable_opt: None, }, - todo!(), - todo!(), - ); + multiple_by_billion(threshold_intercept_major), + CreditorThresholds::new(multiple_by_billion(permanent_debt_allowed_major)), + ) + } + + #[test] + fn count_of_qualified_accounts_before_equals_the_one_of_payments_after() { + // In other words, adjustment by service fee with no account eliminated + init_test_logging(); + let test_name = "count_of_qualified_accounts_before_equals_the_one_of_payments_after"; + let now = SystemTime::now(); + let balance_1 = 5_444_444_444; + let qualified_account_1 = + make_plucked_qualified_account("abc", balance_1, 2_000_000_000, 1_000_000_000); + let balance_2 = 6_000_000_000; + let qualified_account_2 = + make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000); + let balance_3 = 6_666_666_666; + let qualified_account_3 = + make_plucked_qualified_account("ghi", balance_3, 3_000_000_000, 1_111_111_111); let qualified_payables = vec![ qualified_account_1.clone(), qualified_account_2.clone(), qualified_account_3.clone(), ]; let mut subject = PaymentAdjusterReal::new(); + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(4_500_000_000)) + .calculate_result(multiple_by_billion(4_200_000_000)) + .calculate_result(multiple_by_billion(3_800_000_000)); + subject.calculators = vec![Box::new(calculator_mock)]; subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum = balance_1 + balance_2 + balance_3; - let service_fee_balance_in_minor_units = accounts_sum - 3_000_000_000_000_000_000; + let accounts_sum_minor = balance_1 + balance_2 + balance_3; + let service_fee_balance_in_minor_units = + multiple_by_billion(accounts_sum_minor) - multiple_by_billion(3_000_000_000); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index bfe92e908..1cdedf471 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -12,10 +12,8 @@ use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::QualifiedPayableAccount; use ethereum_types::U256; -use itertools::{Either, Itertools}; +use itertools::Either; use masq_lib::logger::Logger; -use std::cmp::Ordering; -use std::ops::Mul; pub struct PreparatoryAnalyzer {} diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index e6e076123..8c8df0199 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,6 +1,8 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::proposed_adjusted_balance_diagnostics; +use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ + outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics, +}; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ InsignificantAccountEliminated, OutweighedAccounts, @@ -10,10 +12,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - adjust_account_balance_if_outweighed, compute_mul_coefficient_preventing_fractional_numbers, - find_largest_weight, weights_total, + compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; -use crate::accountant::QualifiedPayableAccount; use itertools::Either; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; @@ -44,11 +44,13 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { .adjustment_computer .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - let still_unchecked_for_disqualified = - match Self::handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { - Either::Left(first_check_passing_accounts) => first_check_passing_accounts, - Either::Right(with_some_outweighed) => return with_some_outweighed, - }; + let still_unchecked_for_disqualified = match Self::handle_possibly_outweighed_accounts( + disqualification_arbiter, + non_finalized_adjusted_accounts, + ) { + Either::Left(first_check_passing_accounts) => first_check_passing_accounts, + Either::Right(with_some_outweighed) => return with_some_outweighed, + }; let verified_accounts = match Self::consider_account_disqualification( disqualification_arbiter, @@ -80,13 +82,13 @@ impl ServiceFeeAdjusterReal { // significantly based on a different parameter than the debt size. Untreated, we would which // grant the account (much) more money than what the accountancy has recorded for it. fn handle_possibly_outweighed_accounts( + disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let init = (vec![], vec![]); - - let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments - .into_iter() - .fold(init, adjust_account_balance_if_outweighed); + let (outweighed, properly_adjusted_accounts) = Self::adjust_account_balance_if_outweighed( + disqualification_arbiter, + unconfirmed_adjustments, + ); if outweighed.is_empty() { Either::Left(properly_adjusted_accounts) @@ -113,26 +115,20 @@ impl ServiceFeeAdjusterReal { logger, ) { - let remaining = unconfirmed_adjustments.into_iter().filter(|account_info| { - account_info - .weighted_account - .qualified_account - .bare_account - .wallet - != disqualified_account_wallet - }); - - let remaining_reverted = remaining - .map(|account_info| { - //TODO maybe implement from like before - account_info.weighted_account - // PayableAccount::from(NonFinalizedAdjustmentWithResolution::new( - // account_info.non_finalized_account, - // AdjustmentResolution::Revert, - // )) + let remaining = unconfirmed_adjustments + .into_iter() + .filter(|account_info| { + account_info + .weighted_account + .qualified_account + .bare_account + .wallet + != disqualified_account_wallet }) .collect(); + let remaining_reverted = convert_collection(remaining); + Either::Right(AdjustmentIterationResult::IterationWithSpecialHandling { case: InsignificantAccountEliminated, remaining_undecided_accounts: remaining_reverted, @@ -141,6 +137,41 @@ impl ServiceFeeAdjusterReal { Either::Left(convert_collection(unconfirmed_adjustments)) } } + + fn adjust_account_balance_if_outweighed( + disqualification_arbiter: &DisqualificationArbiter, + unconfirmed_adjustments: Vec, + ) -> ( + Vec, + Vec, + ) { + let (outweighed, properly_adjusted_accounts): (Vec<_>, Vec<_>) = unconfirmed_adjustments + .into_iter() + .partition(|adjustment_info| { + adjustment_info.proposed_adjusted_balance_minor + > adjustment_info + .weighted_account + .qualified_account + .bare_account + .balance_wei + }); + + let outweighed_adjusted = outweighed + .into_iter() + .map(|account| { + outweighed_accounts_diagnostics(&account); + + let maximized_proposed_adjusted_balance_minor = disqualification_arbiter + .calculate_disqualification_edge(&account.weighted_account.qualified_account); + AdjustedAccountBeforeFinalization::new( + account.weighted_account.qualified_account.bare_account, + maximized_proposed_adjusted_balance_minor, + ) + }) + .collect(); + + (outweighed_adjusted, properly_adjusted_accounts) + } } #[derive(Default)] @@ -153,19 +184,18 @@ impl AdjustmentComputer { unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { let weights_total = weights_total(&weighted_accounts); - let largest_weight = find_largest_weight(&weighted_accounts); let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; - let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( - cw_service_fee_balance, - largest_weight, - ); + let multiplication_coefficient = + compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance); + eprintln!("multiplication coeff {}", multiplication_coefficient); let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( cw_service_fee_balance, weights_total, multiplication_coefficient, ); + eprintln!("proportional fragment {}", proportional_cw_balance_fragment); let compute_proposed_adjusted_balance = |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; @@ -186,17 +216,17 @@ impl AdjustmentComputer { } fn compute_proportional_cw_fragment( - cw_service_fee_balance: u128, + cw_service_fee_balance_minor: u128, weights_total: u128, multiplication_coefficient: u128, ) -> u128 { - cw_service_fee_balance + cw_service_fee_balance_minor // Considered safe due to the process of getting this coefficient .checked_mul(multiplication_coefficient) .unwrap_or_else(|| { panic!( "mul overflow from {} * {}", - weights_total, multiplication_coefficient + cw_service_fee_balance_minor, multiplication_coefficient ) }) .checked_div(weights_total) @@ -204,64 +234,87 @@ impl AdjustmentComputer { } } -// fn perform_adjustment_by_service_fee( -// &self, -// weighted_accounts: Vec, -// ) -> AdjustmentIterationResult { -// let non_finalized_adjusted_accounts = -// self.compute_unconfirmed_adjustments(weighted_accounts); -// -// let still_unchecked_for_disqualified = -// match self.handle_possibly_outweighed_accounts(non_finalized_adjusted_accounts) { -// Either::Left(first_check_passing_accounts) => first_check_passing_accounts, -// Either::Right(with_some_outweighed) => return with_some_outweighed, -// }; -// -// let verified_accounts = match self -// .consider_account_disqualification(still_unchecked_for_disqualified, &self.logger) -// { -// Either::Left(verified_accounts) => verified_accounts, -// Either::Right(with_some_disqualified) => return with_some_disqualified, -// }; -// -// AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) -// } +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; + use crate::accountant::payment_adjuster::test_utils::{ + make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, DisqualificationGaugeMock, + }; + + #[test] + fn adjust_account_balance_if_outweighed_limits_them_by_the_standard_disqualification_edge() { + let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); + account_1 + .weighted_account + .qualified_account + .bare_account + .balance_wei = multiple_by_billion(2_000_000_000); + account_1.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000) + 1; + let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); + account_2 + .weighted_account + .qualified_account + .bare_account + .balance_wei = multiple_by_billion(5_000_000_000); + account_2.proposed_adjusted_balance_minor = multiple_by_billion(5_000_000_000) + 1; + let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); + account_3 + .weighted_account + .qualified_account + .bare_account + .balance_wei = multiple_by_billion(3_000_000_000); + account_3.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); + let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); + account_4 + .weighted_account + .qualified_account + .bare_account + .balance_wei = multiple_by_billion(1_500_000_000); + account_4.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); + let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); + account_5 + .weighted_account + .qualified_account + .bare_account + .balance_wei = multiple_by_billion(2_000_000_000); + account_5.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000) - 1; + let unconfirmed_accounts = vec![ + account_1.clone(), + account_2.clone(), + account_3.clone(), + account_4.clone(), + account_5.clone(), + ]; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(multiple_by_billion(1_700_000_000)) + .determine_limit_result(multiple_by_billion(4_000_000_000)) + .determine_limit_result(multiple_by_billion(1_250_555_555)); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); -// TODO Should this become a helper? ...with which I can catch mid-results and assert on them? -// -// fn compute_unconfirmed_adjustments( -// &self, -// weighted_accounts: Vec, -// ) -> Vec { -// let weights_total = weights_total(&weighted_accounts); -// let largest_weight = find_largest_weight(&weighted_accounts); -// let cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); -// -// let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( -// cw_service_fee_balance, -// largest_weight, -// ); -// -// let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( -// cw_service_fee_balance, -// weights_total, -// multiplication_coefficient, -// ); -// let compute_proposed_adjusted_balance = -// |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; -// -// weighted_accounts -// .into_iter() -// .map(|weighted_account| { -// let proposed_adjusted_balance = -// compute_proposed_adjusted_balance(weighted_account.weight); -// -// proposed_adjusted_balance_diagnostics( -// &weighted_account.qualified_account, -// proposed_adjusted_balance, -// ); -// -// UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) -// }) -// .collect() -// } + let (outweighed_accounts, properly_adjusted_accounts) = + ServiceFeeAdjusterReal::adjust_account_balance_if_outweighed( + &disqualification_arbiter, + unconfirmed_accounts, + ); + + assert_eq!(properly_adjusted_accounts, vec![account_3, account_5]); + let expected_adjusted_outweighed_accounts = vec![ + AdjustedAccountBeforeFinalization { + original_account: account_1.weighted_account.qualified_account.bare_account, + proposed_adjusted_balance_minor: multiple_by_billion(1_700_000_000), + }, + AdjustedAccountBeforeFinalization { + original_account: account_2.weighted_account.qualified_account.bare_account, + proposed_adjusted_balance_minor: multiple_by_billion(4_000_000_000), + }, + AdjustedAccountBeforeFinalization { + original_account: account_4.weighted_account.qualified_account.bare_account, + proposed_adjusted_balance_minor: multiple_by_billion(1_250_555_555), + }, + ]; + assert_eq!(outweighed_accounts, expected_adjusted_outweighed_accounts) + } +} diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 8272ba116..bb8f89b01 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -9,15 +9,13 @@ use crate::accountant::payment_adjuster::disqualification_arbiter::{ }; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, - WeightedPayable, + AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; -use crate::accountant::{gwei_to_wei, QualifiedPayableAccount}; +use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::accountant::PaymentThresholds; -use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::Either; use lazy_static::lazy_static; @@ -93,35 +91,6 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO unban_below_gwei: 1_000_000, }; -pub fn assert_constants_and_remind_checking_sync_of_calculators_if_any_constant_changes( - constants_and_expected_values: &[(i128, i128)], - expected_num_sum: i128, -) { - constants_and_expected_values.iter().enumerate().for_each( - |(idx, (constant, expected_value))| { - assert_eq!( - constant, expected_value, - "constant wrong value at position {}", - idx - ) - }, - ); - - // This matters only if the constants participate in the calculator's formula. If that's not - // true, simply update the num sum and ignore the concern about synchronization - let actual_sum: i128 = constants_and_expected_values - .iter() - .map(|(val, _)| *val) - .sum(); - assert_eq!(actual_sum, expected_num_sum, - "The sum of constants used to calibre the calculator has changed, therefore you ought to see about \n\ - maintenance of the whole system with its all parameters (e.g. debt age, debt balance,...) and make \n\ - sure the weights coming from them are sensibly proportionate. There is a tool that can help you with \n\ - that, look for a global flag in the file 'diagnostics' in the PaymentAdjuster module. It will enable \n\ - rendering characteristics of the curves the calculations of these parameters are based on." - ) -} - pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_payable = make_non_guaranteed_qualified_payable(n); let proposed_adjusted_balance_minor = @@ -188,7 +157,7 @@ impl DisqualificationGaugeMock { self } - pub fn determine_limit_result(mut self, result: u128) -> Self { + pub fn determine_limit_result(self, result: u128) -> Self { self.determine_limit_results.borrow_mut().push(result); self } diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 3dc8d7fd9..9de25057e 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -489,7 +489,7 @@ mod tests { PayableThresholdsGaugeReal, }; use crate::accountant::scanners::scanners_utils::receivable_scanner_utils::balance_and_age; - use crate::accountant::{checked_conversion, CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, SentPayables}; + use crate::accountant::{CreditorThresholds, gwei_to_wei, QualifiedPayableAccount, SentPayables}; use crate::blockchain::test_utils::make_tx_hash; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; From 1d0b0adfbce5001d3c92be3efed4fb3ce2318b82 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 12 Apr 2024 12:23:28 +0200 Subject: [PATCH 158/250] GH-711-b: loading test little upgrade --- .../payment_adjuster/loading_test/mod.rs | 180 +++++++++++++++--- 1 file changed, 150 insertions(+), 30 deletions(-) diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index 701b73040..da447242f 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -41,7 +41,7 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 50; + let number_of_requested_scenarios = 500; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let test_overall_output_collector = TestOverallOutputCollector::default(); @@ -83,7 +83,7 @@ fn loading_test_with_randomized_params() { ) => { output_collector .test_overall_output_collector - .scenarios_eliminated_before_adjustment_started += 1; + .scenarios_denied_before_adjustment_started += 1; None } _e => Some(scenario), @@ -186,8 +186,11 @@ fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec { let mut adjusted_accounts = outbound_payment_instructions.affordable_accounts; - let adjusted_accounts = account_infos - .into_iter() - .map(|account_info| { - prepare_interpretable_account_resolution(account_info, &mut adjusted_accounts) - }) - .collect(); - let sorted_interpretable_adjustments = + let portion_of_cw_cumulatively_used_percents = { + let used_absolute: u128 = sum_as(&adjusted_accounts, |account| account.balance_wei); + ((100 * used_absolute) / common.cw_service_fee_balance_minor) as u8 + }; + let adjusted_accounts = + interpretable_account_resolutions(account_infos, &mut adjusted_accounts); + let (partially_sorted_interpretable_adjustments, were_no_accounts_eliminated) = sort_interpretable_adjustments(adjusted_accounts); Ok(SuccessfulAdjustment { common, - partially_sorted_interpretable_adjustments: sorted_interpretable_adjustments, + portion_of_cw_cumulatively_used_percents, + partially_sorted_interpretable_adjustments, + were_no_accounts_eliminated, }) } Err(adjuster_error) => Err(FailedAdjustment { @@ -254,6 +259,18 @@ fn prepare_single_scenario_result( ScenarioResult::new(reinterpreted_result) } +fn interpretable_account_resolutions( + account_infos: Vec, + adjusted_accounts: &mut Vec, +) -> Vec { + account_infos + .into_iter() + .map(|account_info| { + prepare_interpretable_account_resolution(account_info, adjusted_accounts) + }) + .collect() +} + struct ScenarioResult { result: Result, } @@ -266,7 +283,9 @@ impl ScenarioResult { struct SuccessfulAdjustment { common: CommonScenarioInfo, + portion_of_cw_cumulatively_used_percents: u8, partially_sorted_interpretable_adjustments: Vec, + were_no_accounts_eliminated: bool, } struct FailedAdjustment { @@ -300,13 +319,14 @@ fn render_results_to_file_and_attempt_basic_assertions( let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); introduction(&mut file); - let test_overall_output_collector = scenario_results - .into_iter() - .fold(overall_output_collector, |acc, scenario_result| { - process_single_scenario(&mut file, acc, scenario_result) - }); + let test_overall_output_collector = + scenario_results + .into_iter() + .fold(overall_output_collector, |acc, scenario_result| { + do_final_processing_of_single_scenario(&mut file, acc, scenario_result) + }); let total_scenarios_evaluated = test_overall_output_collector - .scenarios_eliminated_before_adjustment_started + .scenarios_denied_before_adjustment_started + test_overall_output_collector.oks + test_overall_output_collector.all_accounts_eliminated + test_overall_output_collector.insufficient_service_fee_balance; @@ -326,7 +346,7 @@ fn render_results_to_file_and_attempt_basic_assertions( // It rather indicates how well the setting is so that you can adjust it eventually, // to see more relevant results let entry_check_pass_rate = 100 - - ((test_overall_output_collector.scenarios_eliminated_before_adjustment_started * 100) + - ((test_overall_output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); let required_pass_rate = 80; assert!( @@ -340,7 +360,7 @@ fn render_results_to_file_and_attempt_basic_assertions( ); let ok_adjustment_percentage = (test_overall_output_collector.oks * 100) / (total_scenarios_evaluated - - test_overall_output_collector.scenarios_eliminated_before_adjustment_started); + - test_overall_output_collector.scenarios_denied_before_adjustment_started); let required_success_rate = 70; assert!( ok_adjustment_percentage >= required_success_rate, @@ -366,32 +386,51 @@ fn write_brief_test_summary_into_file( number_of_requested_scenarios: usize, total_of_scenarios_evaluated: usize, ) { + write_thick_dividing_line(file); write_thick_dividing_line(file); file.write_fmt(format_args!( - "Scenarios\n\ + "\n\ + Scenarios\n\ Requested:............................. {}\n\ - Actually evaluated:.................... {}\n\ - Caught by the entry check:............. {}\n\ + Actually evaluated:.................... {}\n\n\ Successful:............................ {}\n\ + Successes with no accounts eliminated:. {}\n\ + Fulfillment distribution (service fee adjustment only):\n\ + {}\n\n\ + Unsuccessful\n\ + Caught by the entry check:............. {}\n\ With 'AllAccountsEliminated':.......... {}\n\ With late insufficient balance errors:. {}", number_of_requested_scenarios, total_of_scenarios_evaluated, - overall_output_collector.scenarios_eliminated_before_adjustment_started, overall_output_collector.oks, + overall_output_collector.with_no_accounts_eliminated, + overall_output_collector + .fulfillment_distribution + .render_in_two_lines(), + overall_output_collector.scenarios_denied_before_adjustment_started, overall_output_collector.all_accounts_eliminated, overall_output_collector.insufficient_service_fee_balance )) .unwrap() } -fn process_single_scenario( +fn do_final_processing_of_single_scenario( file: &mut File, mut test_overall_output: TestOverallOutputCollector, scenario: ScenarioResult, ) -> TestOverallOutputCollector { match scenario.result { Ok(positive) => { + if positive.were_no_accounts_eliminated { + test_overall_output.with_no_accounts_eliminated += 1 + } + if Adjustment::ByServiceFee == positive.common.required_adjustment { + test_overall_output + .fulfillment_distribution + .collected_fulfillment_percentages + .push(positive.portion_of_cw_cumulatively_used_percents) + } render_positive_scenario(file, positive); test_overall_output.oks += 1; test_overall_output @@ -399,7 +438,7 @@ fn process_single_scenario( Err(negative) => { match negative.adjuster_error { PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => { - panic!("impossible in this kind of test without the initial check") + panic!("impossible in this kind of test without the tx fee initial check") } PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { .. @@ -417,12 +456,15 @@ fn process_single_scenario( fn render_scenario_header( file: &mut File, cw_service_fee_balance_minor: u128, + portion_of_cw_used_percents: u8, required_adjustment: Adjustment, ) { file.write_fmt(format_args!( "CW service fee balance: {} wei\n\ + Portion of CW balance used: {}%\n\ Maximal txt count due to CW txt fee balance: {}\n", cw_service_fee_balance_minor.separate_with_commas(), + portion_of_cw_used_percents, resolve_affordable_transaction_count(required_adjustment) )) .unwrap(); @@ -432,6 +474,7 @@ fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { render_scenario_header( file, result.common.cw_service_fee_balance_minor, + result.portion_of_cw_cumulatively_used_percents, result.common.required_adjustment, ); write_thin_dividing_line(file); @@ -481,6 +524,7 @@ fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) render_scenario_header( file, negative_result.common.cw_service_fee_balance_minor, + 0, negative_result.common.required_adjustment, ); write_thin_dividing_line(file); @@ -555,13 +599,14 @@ fn prepare_interpretable_account_resolution( fn sort_interpretable_adjustments( interpretable_adjustments: Vec, -) -> Vec { +) -> (Vec, bool) { let (finished, eliminated): ( Vec, Vec, ) = interpretable_adjustments .into_iter() .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); + let were_no_accounts_eliminated = eliminated.is_empty(); let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { Ord::cmp( &result_b.bill_coverage_in_percentage_opt.unwrap(), @@ -571,7 +616,8 @@ fn sort_interpretable_adjustments( let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { Ord::cmp(&result_b.initial_balance, &result_a.initial_balance) }); - finished_sorted.chain(eliminated_sorted).collect() + let all_results = finished_sorted.chain(eliminated_sorted).collect(); + (all_results, were_no_accounts_eliminated) } fn generate_usize_guts(gn: &mut ThreadRng, low: usize, up_to: usize) -> usize { @@ -594,15 +640,89 @@ fn generate_boolean(gn: &mut ThreadRng) -> bool { struct TestOverallOutputCollector { // First stage: entry check // ____________________________________ - scenarios_eliminated_before_adjustment_started: usize, - // Second stage: proper adjustment + scenarios_denied_before_adjustment_started: usize, + // Second stage: proper adjustments // ____________________________________ oks: usize, + with_no_accounts_eliminated: usize, + fulfillment_distribution: PercentageFulfillmentDistribution, // Errors all_accounts_eliminated: usize, insufficient_service_fee_balance: usize, } +#[derive(Default)] +struct PercentageFulfillmentDistribution { + collected_fulfillment_percentages: Vec, +} + +impl PercentageFulfillmentDistribution { + fn render_in_two_lines(&self) -> String { + #[derive(Default)] + struct Ranges { + from_0_to_10: usize, + from_10_to_20: usize, + from_20_to_30: usize, + from_30_to_40: usize, + from_40_to_50: usize, + from_50_to_60: usize, + from_60_to_70: usize, + from_70_to_80: usize, + from_80_to_90: usize, + from_90_to_100: usize, + } + + let full_count = self.collected_fulfillment_percentages.len(); + let ranges_populated = self.collected_fulfillment_percentages.iter().fold( + Ranges::default(), + |mut ranges, current| { + match current { + 0..=9 => ranges.from_0_to_10 += 1, + 10..=19 => ranges.from_10_to_20 += 1, + 20..=29 => ranges.from_20_to_30 += 1, + 30..=39 => ranges.from_30_to_40 += 1, + 40..=49 => ranges.from_40_to_50 += 1, + 50..=59 => ranges.from_50_to_60 += 1, + 60..=69 => ranges.from_60_to_70 += 1, + 70..=79 => ranges.from_70_to_80 += 1, + 80..=89 => ranges.from_80_to_90 += 1, + 90..=100 => ranges.from_90_to_100 += 1, + _ => panic!("Shouldn't happen"), + } + ranges + }, + ); + let digits = 6.max(full_count.to_string().len()); + format!( + "Percentage ranges\n\ + {:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|\ + {:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}\n\ + {:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|\ + {:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}|{:^digits$}", + "0-9", + "10-19", + "20-29", + "30-39", + "40-49", + "50-59", + "60-69", + "70-79", + "80-89", + "90-100", + ranges_populated.from_0_to_10, + ranges_populated.from_10_to_20, + ranges_populated.from_20_to_30, + ranges_populated.from_30_to_40, + ranges_populated.from_40_to_50, + ranges_populated.from_50_to_60, + ranges_populated.from_60_to_70, + ranges_populated.from_70_to_80, + ranges_populated.from_80_to_90, + ranges_populated.from_90_to_100 + ) + } +} + struct CommonScenarioInfo { cw_service_fee_balance_minor: u128, required_adjustment: Adjustment, From f11e30255812038bba22e0101418eca6926d5002 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 12 Apr 2024 20:19:11 +0200 Subject: [PATCH 159/250] GH-711-b: a few more tests fixed --- .../payment_adjuster/loading_test/mod.rs | 2 +- .../miscellaneous/helper_functions.rs | 47 ++++++++-- node/src/accountant/payment_adjuster/mod.rs | 85 ++++++++++++------- 3 files changed, 94 insertions(+), 40 deletions(-) diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/loading_test/mod.rs index da447242f..21438994d 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/loading_test/mod.rs @@ -26,6 +26,7 @@ use thousands::Separable; use web3::types::U256; #[test] +#[ignore] fn loading_test_with_randomized_params() { // This test needs to be understood as a generator of extensive amount of scenarios that // the PaymentAdjuster might come to be asked to resolve while there are quite many combinations @@ -386,7 +387,6 @@ fn write_brief_test_summary_into_file( number_of_requested_scenarios: usize, total_of_scenarios_evaluated: usize, ) { - write_thick_dividing_line(file); write_thick_dividing_line(file); file.write_fmt(format_args!( "\n\ diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 930aed761..8c822df10 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -195,6 +195,7 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( .collect() } +// If passing along without PA just to BlockchainBridge impl From for PayableAccount { fn from(qualified_payable: QualifiedPayableAccount) -> Self { qualified_payable.bare_account @@ -209,18 +210,31 @@ impl From for PayableAccount { } } +// Preparing "remaining, unresolved accounts" for another iteration that always begins with +// WeightedPayable types impl From for WeightedPayable { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { unconfirmed_adjustment.weighted_account } } +// Used after the unconfirmed adjustment pass through all confirmations impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - WeightedPayable::from(unconfirmed_adjustment).into() + let proposed_adjusted_balance_minor = + unconfirmed_adjustment.proposed_adjusted_balance_minor; + let original_account = unconfirmed_adjustment + .weighted_account + .qualified_account + .bare_account; + + AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) } } +// This is used when we detect that the upcoming iterations begins with a surplus in the remaining +// unallocated CW service fee, and therefore we grant the remaining accounts with the full balance +// they requested impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { let proposed_adjusted_balance_minor = @@ -235,7 +249,7 @@ impl From for AdjustedAccountBeforeFinalization { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, @@ -372,16 +386,19 @@ mod tests { assert_eq!(result, original_payable_account) } - #[test] - fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { - let mut original_payable_account = make_payable_account(123); - original_payable_account.balance_wei = 200_000_000; + fn prepare_weighted_account(payable_account: PayableAccount) -> WeightedPayable { let qualified_account = QualifiedPayableAccount::new( - original_payable_account.clone(), + payable_account, 444_555_666, CreditorThresholds::new(111_111_111), ); - let weighted_account = WeightedPayable::new(qualified_account, 777_777_777); + WeightedPayable::new(qualified_account, 777_777_777) + } + #[test] + fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let weighted_account = prepare_weighted_account(original_payable_account.clone()); let result = AdjustedAccountBeforeFinalization::from(weighted_account); @@ -390,6 +407,20 @@ mod tests { assert_eq!(result, expected_result) } + #[test] + fn conversion_between_unconfirmed_adjustment_and_non_finalized_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let weighted_account = prepare_weighted_account(original_payable_account.clone()); + let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); + + let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); + + let expected_result = + AdjustedAccountBeforeFinalization::new(original_payable_account, 111_222_333); + assert_eq!(result, expected_result) + } + #[test] fn exhaustive_status_is_constructed_properly() { let cw_balance_remainder = 45678; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b5daff7d6..86f8a5ca0 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -137,6 +137,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { let largest_exceeding_balance_recently_qualified = find_largest_exceeding_balance(&qualified_payables); + eprintln!( + "INITIAL SV CW FEE BAL {}", + initial_service_fee_balance_minor + ); + self.initialize_inner( initial_service_fee_balance_minor, required_adjustment, @@ -420,8 +425,10 @@ impl PaymentAdjusterReal { diagnostics!( "LOWERED CW BALANCE", "Unallocated balance lowered by {} to {}", - subtrahend_total, - self.inner.unallocated_cw_service_fee_balance_minor() + subtrahend_total.separate_with_commas(), + self.inner + .unallocated_cw_service_fee_balance_minor() + .separate_with_commas() ) } @@ -507,8 +514,8 @@ impl Display for PaymentAdjusterError { ), PaymentAdjusterError::AllAccountsEliminated => write!( f, - "The adjustment algorithm had to eliminate each payable from the recently urged payment \ - due to lack of resources." + "The adjustment algorithm had to eliminate each payable from the recently urged \ + payment due to lack of resources." ), } } @@ -890,18 +897,23 @@ mod tests { #[test] fn tinier_but_larger_in_weight_account_is_prioritized_outweighed_up_to_its_original_balance() { - let now = SystemTime::now(); - let cw_service_fee_balance_minor = multiple_by_billion(3_500_000); + let cw_service_fee_balance_minor = multiple_by_billion(3_600_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); let mut account_1 = make_qualified_payable_by_wallet("abc"); let balance_1 = multiple_by_billion(3_000_000); + let disqualification_limit_1 = multiple_by_billion(2_300_000); account_1.bare_account.balance_wei = balance_1; - let threshold_intercept_minor = account_1.payment_threshold_intercept_minor; - let permanent_debt_allowed_minor = account_1.creditor_thresholds.permanent_debt_allowed_wei; + let threshold_intercept_minor_1 = account_1.payment_threshold_intercept_minor; + let permanent_debt_allowed_minor_1 = + account_1.creditor_thresholds.permanent_debt_allowed_wei; let mut account_2 = make_qualified_payable_by_wallet("def"); let wallet_2 = account_2.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); + let disqualification_limit_2 = multiple_by_billion(800_000); account_2.bare_account.balance_wei = balance_2; + let threshold_intercept_minor_2 = account_2.payment_threshold_intercept_minor; + let permanent_debt_allowed_minor_2 = + account_2.creditor_thresholds.permanent_debt_allowed_wei; let largest_exceeding_balance = (balance_1 - account_1.payment_threshold_intercept_minor) .max(balance_2 - account_2.payment_threshold_intercept_minor); let mut subject = make_initialized_subject( @@ -912,7 +924,11 @@ mod tests { None, ); let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(cw_service_fee_balance_minor / 2) + // Requested for an outweighed account in order to give it the minimal possible balance + // that still will solve a supposed ban + .determine_limit_result(disqualification_limit_2) + // Simple testing an unconfirmed account on disqualification + .determine_limit_result(disqualification_limit_1) .determine_limit_params(&determine_limit_params_arc); subject.disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); @@ -940,7 +956,7 @@ mod tests { balance_2, 2.3, ); - // // So the assertion above showed the concern true. + // So the assertion above showed the concern true. let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!( @@ -951,7 +967,7 @@ mod tests { ); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, - balance_2 + disqualification_limit_2 ); let second_returned_account = result.remove(0); assert_eq!( @@ -962,17 +978,24 @@ mod tests { ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 2499999999999999 + 2799999999999999 ); assert!(result.is_empty()); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); assert_eq!( *determine_limit_params, - vec![( - balance_1, - threshold_intercept_minor, - permanent_debt_allowed_minor - )] + vec![ + ( + balance_2, + threshold_intercept_minor_2, + permanent_debt_allowed_minor_2 + ), + ( + balance_1, + threshold_intercept_minor_1, + permanent_debt_allowed_minor_1 + ) + ] ) } @@ -1257,14 +1280,14 @@ mod tests { // consideration. fn make_plucked_qualified_account( wallet_addr_fragment: &str, - balance_major: u128, + balance_minor: u128, threshold_intercept_major: u128, permanent_debt_allowed_major: u128, ) -> QualifiedPayableAccount { QualifiedPayableAccount::new( PayableAccount { wallet: make_wallet(wallet_addr_fragment), - balance_wei: multiple_by_billion(balance_major), + balance_wei: balance_minor, last_paid_timestamp: meaningless_timestamp(), pending_payable_opt: None, }, @@ -1279,13 +1302,13 @@ mod tests { init_test_logging(); let test_name = "count_of_qualified_accounts_before_equals_the_one_of_payments_after"; let now = SystemTime::now(); - let balance_1 = 5_444_444_444; + let balance_1 = multiple_by_billion(5_444_444_444); let qualified_account_1 = make_plucked_qualified_account("abc", balance_1, 2_000_000_000, 1_000_000_000); - let balance_2 = 6_000_000_000; + let balance_2 = multiple_by_billion(6_000_000_000); let qualified_account_2 = make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000); - let balance_3 = 6_666_666_666; + let balance_3 = multiple_by_billion(6_666_666_666); let qualified_account_3 = make_plucked_qualified_account("ghi", balance_3, 3_000_000_000, 1_111_111_111); let qualified_payables = vec![ @@ -1303,7 +1326,7 @@ mod tests { let agent_id_stamp = ArbitraryIdStamp::new(); let accounts_sum_minor = balance_1 + balance_2 + balance_3; let service_fee_balance_in_minor_units = - multiple_by_billion(accounts_sum_minor) - multiple_by_billion(3_000_000_000); + accounts_sum_minor - multiple_by_billion(2_000_000_000); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1319,9 +1342,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_adjusted_balance_1 = 3_878_112_909_226_659_278; - let expected_adjusted_balance_2 = 5_941_743_288_347_289_696; - let expected_adjusted_balance_3 = 4_291_254_912_870_495_470; + let expected_adjusted_balance_1 = 4_444_444_444_000_000_001; + let expected_adjusted_balance_2 = 5_500_000_000_000_000_000; + let expected_adjusted_balance_3 = 6_166_666_665_999_999_999; let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, @@ -1335,7 +1358,7 @@ mod tests { balance_wei: expected_adjusted_balance_3, ..qualified_account_3.bare_account }; - vec![account_2_adjusted, account_3_adjusted, account_1_adjusted] + vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] }; assert_eq!( result.affordable_accounts, @@ -1350,16 +1373,16 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000646566 {} -| {} |0x0000000000000000000000000000000000676869 {} | {} +|0x0000000000000000000000000000000000646566 {} +| {} |0x0000000000000000000000000000000000616263 {} | {}", - balance_2.separate_with_commas(), - expected_adjusted_balance_2.separate_with_commas(), balance_3.separate_with_commas(), expected_adjusted_balance_3.separate_with_commas(), + balance_2.separate_with_commas(), + expected_adjusted_balance_2.separate_with_commas(), balance_1.separate_with_commas(), expected_adjusted_balance_1.separate_with_commas() ); From 308e6b3b540f3b361dae584af1d85adf1eddf914 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 13 Apr 2024 19:34:06 +0200 Subject: [PATCH 160/250] GH-711-b: two more accounts --- node/src/accountant/payment_adjuster/mod.rs | 457 +++++++++----------- 1 file changed, 202 insertions(+), 255 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 86f8a5ca0..bd5fe8e3c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -526,7 +526,9 @@ mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, + }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; @@ -1274,10 +1276,10 @@ mod tests { SystemTime::now() } - // This function should take just such args that affects the adjustment mechanism, except - // those that work as pure criteria parameters (= make up the weights). These should be - // limited to minimum because in this kind of tests we don't want to be burdened with their - // consideration. + // This function should take just such essential args as balances and those that play rather + // a secondary role, yet an important one in the verification processes for proposed adjusted + // balances. Refrain from employing more of the weights-affecting parameters as they would + // only burden us with their consideration in these tests. fn make_plucked_qualified_account( wallet_addr_fragment: &str, balance_minor: u128, @@ -1325,17 +1327,13 @@ mod tests { subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); let accounts_sum_minor = balance_1 + balance_2 + balance_3; - let service_fee_balance_in_minor_units = - accounts_sum_minor - multiple_by_billion(2_000_000_000); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); - Box::new(mock) - }; + let cw_service_fee_balance_minor = accounts_sum_minor - multiple_by_billion(2_000_000_000); + let agent = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(cw_service_fee_balance_minor); let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, }; @@ -1395,49 +1393,30 @@ mod tests { let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); - let account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_3 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let balance_1 = multiple_by_billion(111_000_000); + let account_1 = make_plucked_qualified_account("abc", balance_1, 100_000_000, 20_000_000); + let balance_2 = multiple_by_billion(300_000_000); + let account_2 = make_plucked_qualified_account("def", balance_2, 120_000_000, 50_000_000); + let balance_3 = multiple_by_billion(222_222_222); + let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 40_000_000); + let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(400_000_000)) + // This account will be cut off because it has the lowest weight and only two accounts + // can be kept according to the limitations detected in the transaction fee balance + .calculate_result(multiple_by_billion(120_000_000)) + .calculate_result(multiple_by_billion(250_000_000)); let mut subject = PaymentAdjusterReal::new(); + subject.calculators = vec![Box::new(calculator_mock)]; subject.logger = Logger::new(test_name); + let cw_service_fee_balance_minor = balance_1 + balance_2 + balance_3; let agent_id_stamp = ArbitraryIdStamp::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(10_u128.pow(22)); - Box::new(mock) - }; + let agent = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(10_u128.pow(22)); let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1446,11 +1425,10 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // The account 3 takes the first place for its age - // (it weights more if the balance is so small) + // The account 1 takes the first place for its weight being the biggest assert_eq!( result.affordable_accounts, - vec![account_3.bare_account, account_2.bare_account] + vec![account_1.bare_account, account_3.bare_account] ); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); @@ -1461,14 +1439,14 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000646566 333,000,000,000,000 -| 333,000,000,000,000 -|0x0000000000000000000000000000000000676869 222,000,000,000,000 -| 222,000,000,000,000 +|0x0000000000000000000000000000000000676869 222,222,222,000,000,000 +| 222,222,222,000,000,000 +|0x0000000000000000000000000000000000616263 111,000,000,000,000,000 +| 111,000,000,000,000,000 | |Ruled Out Accounts Original | -|0x0000000000000000000000000000000000616263 111,000,000,000,000" +|0x0000000000000000000000000000000000646566 300,000,000,000,000,000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1481,53 +1459,36 @@ mod tests { // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_3 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("ghk"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let qualified_payables = vec![account_1, account_2.clone(), account_3.clone()]; + let balance_1 = multiple_by_billion(111_000_000); + let account_1 = make_plucked_qualified_account("abc", balance_1, 50_000_000, 10_000_000); + let balance_2 = multiple_by_billion(333_000_000); + let account_2 = make_plucked_qualified_account("def", balance_2, 200_000_000, 50_000_000); + let balance_3 = multiple_by_billion(222_000_000); + let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 35_000_000); + let disqualification_arbiter = DisqualificationArbiter::default(); + let disqualification_limit_1 = + disqualification_arbiter.calculate_disqualification_edge(&account_1); + let disqualification_limit_3 = + disqualification_arbiter.calculate_disqualification_edge(&account_3); + let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(400_000_000)) + .calculate_result(multiple_by_billion(200_000_000)) + .calculate_result(multiple_by_billion(300_000_000)); let mut subject = PaymentAdjusterReal::new(); - let service_fee_balance_in_minor_units = 111_000_000_000_000_u128 + 333_000_000_000_000; + let cw_service_fee_balance_minor = + disqualification_limit_1 + disqualification_limit_3 + multiple_by_billion(10_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); - Box::new(mock) - }; + let agent = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(cw_service_fee_balance_minor); let response_skeleton_opt = Some(ResponseSkeleton { client_id: 123, context_id: 321, - }); //just hardening, not so important + }); // Just hardening, not so important let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1536,13 +1497,17 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - // Account_1, the least important one, was eliminated for not big enough transaction fee balance + // Account 2, the least important one, was eliminated for a lack of transaction fee in the cw let expected_accounts = { - let account_2_adjusted = PayableAccount { - balance_wei: 222_000_000_000_000, - ..account_2.bare_account + let account_1_adjusted = PayableAccount { + balance_wei: 71_000_000_000_000_001, + ..account_1.bare_account }; - vec![account_3.bare_account, account_2_adjusted] + let account_3_adjusted = PayableAccount { + balance_wei: 166_999_999_999_999_999, + ..account_3.bare_account + }; + vec![account_1_adjusted, account_3_adjusted] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1641,156 +1606,138 @@ mod tests { )); } - struct CompetitiveAccountsTestInputs<'a> { - common: WalletsSetup<'a>, - account_1_balance_positive_correction_minor: u128, - account_2_balance_positive_correction_minor: u128, - account_1_age_positive_correction_secs: u64, - account_2_age_positive_correction_secs: u64, - } - - #[derive(Clone, Copy)] - struct WalletsSetup<'a> { - wallet_1: &'a Wallet, - wallet_2: &'a Wallet, - } - - fn test_two_competitive_accounts_with_one_disqualified<'a>( - test_scenario_name: &str, - inputs: CompetitiveAccountsTestInputs, - expected_wallet_of_the_winning_account: &'a Wallet, - ) { - let now = SystemTime::now(); - let cw_service_fee_balance_in_minor = 100_000_000_000_000 - 1; - let standard_balance_per_account = 100_000_000_000_000; - let standard_age_per_account = 12000; - let account_1 = PayableAccount { - wallet: inputs.common.wallet_1.clone(), - balance_wei: standard_balance_per_account - + inputs.account_1_balance_positive_correction_minor, - last_paid_timestamp: now - .checked_sub(Duration::from_secs( - standard_age_per_account + inputs.account_1_age_positive_correction_secs, - )) - .unwrap(), - pending_payable_opt: None, - }; - let account_2 = PayableAccount { - wallet: inputs.common.wallet_2.clone(), - balance_wei: standard_balance_per_account - + inputs.account_2_balance_positive_correction_minor, - last_paid_timestamp: now - .checked_sub(Duration::from_secs( - standard_age_per_account + inputs.account_2_age_positive_correction_secs, - )) - .unwrap(), - pending_payable_opt: None, - }; - let payables = vec![account_1, account_2]; - let qualified_payables = - make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - let mut subject = PaymentAdjusterReal::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .service_fee_balance_minor_result(cw_service_fee_balance_in_minor); - Box::new(mock) - }; - let adjustment_setup = PreparedAdjustment { - qualified_payables, - agent, - adjustment: Adjustment::ByServiceFee, - response_skeleton_opt: None, - }; - - let mut result = subject - .adjust_payments(adjustment_setup, now) - .unwrap() - .affordable_accounts; - - let winning_account = result.remove(0); - assert_eq!( - &winning_account.wallet, expected_wallet_of_the_winning_account, - "{}: expected wallet {} but got {}", - test_scenario_name, winning_account.wallet, expected_wallet_of_the_winning_account - ); - assert_eq!( - winning_account.balance_wei, cw_service_fee_balance_in_minor, - "{}: expected full cw balance {}, but the account had {}", - test_scenario_name, winning_account.balance_wei, cw_service_fee_balance_in_minor - ); - assert!( - result.is_empty(), - "{}: is not empty, {:?} remains", - test_scenario_name, - result - ) - } - - #[test] - fn not_enough_service_fee_for_both_accounts_at_least_by_their_half_so_only_one_wins() { - fn merge_test_name_with_test_scenario(description: &str) -> String { - format!( - "not_enough_service_fee_for_both_accounts_at_least_by_their_half_so_only_one_wins{}", - description - ) - } - - let w1 = make_wallet("abcd"); - let w2 = make_wallet("cdef"); - let common_input = WalletsSetup { - wallet_1: &w1, - wallet_2: &w2, - }; - // scenario A - let first_scenario_name = merge_test_name_with_test_scenario("when equally significant"); - let expected_wallet_of_the_winning_account = &w2; - - test_two_competitive_accounts_with_one_disqualified( - &first_scenario_name, - CompetitiveAccountsTestInputs { - common: common_input, - account_1_balance_positive_correction_minor: 0, - account_2_balance_positive_correction_minor: 0, - account_1_age_positive_correction_secs: 0, - account_2_age_positive_correction_secs: 0, - }, - expected_wallet_of_the_winning_account, - ); - //-------------------------------------------------------------------- - // scenario B - let second_scenario_name = - merge_test_name_with_test_scenario("first more significant by balance"); - let expected_wallet_of_the_winning_account = &w1; - - test_two_competitive_accounts_with_one_disqualified( - &second_scenario_name, - CompetitiveAccountsTestInputs { - common: common_input, - account_1_balance_positive_correction_minor: 1, - account_2_balance_positive_correction_minor: 0, - account_1_age_positive_correction_secs: 0, - account_2_age_positive_correction_secs: 0, - }, - expected_wallet_of_the_winning_account, - ); - //-------------------------------------------------------------------- - // scenario C - let third_scenario_name = - merge_test_name_with_test_scenario("second more significant by age"); - let expected_wallet_of_the_winning_account = &w2; - - test_two_competitive_accounts_with_one_disqualified( - &third_scenario_name, - CompetitiveAccountsTestInputs { - common: common_input, - account_1_balance_positive_correction_minor: 0, - account_2_balance_positive_correction_minor: 0, - account_1_age_positive_correction_secs: 0, - account_2_age_positive_correction_secs: 1, - }, - expected_wallet_of_the_winning_account, - ); - } + //TODO Should I keep the next test? It works but it might be unnecessary now... + // + // struct CompetitiveAccountsTestInputs { + // common: WalletsSetup, + // account_1_balance_positive_correction_minor: u128, + // account_2_balance_positive_correction_minor: u128, + // } + // + // #[derive(Clone)] + // struct WalletsSetup { + // wallet_1: Wallet, + // wallet_2: Wallet, + // } + // + // fn test_two_competitive_accounts_with_one_disqualified<'a>( + // test_scenario_name: &str, + // inputs: CompetitiveAccountsTestInputs, + // expected_wallet_of_the_winning_account: &'a Wallet, + // ) { + // let now = SystemTime::now(); + // let standard_balance_per_account = multiple_by_billion(2_000_000); + // let garbage_timestamp = now.checked_sub(Duration::from_secs(120000)).unwrap(); + // // Ideally, cancel out the impact of those other parameters than purely balances. + // let account_1 = PayableAccount { + // wallet: inputs.common.wallet_1.clone(), + // balance_wei: standard_balance_per_account + // + inputs.account_1_balance_positive_correction_minor, + // last_paid_timestamp: garbage_timestamp, + // pending_payable_opt: None, + // }; + // let mut account_2 = account_1.clone(); + // account_2.wallet = inputs.common.wallet_2.clone(); + // account_2.balance_wei = standard_balance_per_account + inputs.account_2_balance_positive_correction_minor; + // let payables = vec![account_1, account_2]; + // let qualified_payables = + // make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + // let disqualification_limit = DisqualificationArbiter::default().calculate_disqualification_edge(&qualified_payables[0]); + // let cw_service_fee_balance_minor = 2 * disqualification_limit - 1; + // let mut subject = PaymentAdjusterReal::new(); + // let agent = BlockchainAgentMock::default() + // .service_fee_balance_minor_result(cw_service_fee_balance_minor); + // let adjustment_setup = PreparedAdjustment { + // qualified_payables, + // agent: Box::new(agent), + // adjustment: Adjustment::ByServiceFee, + // response_skeleton_opt: None, + // }; + // + // let mut result = subject + // .adjust_payments(adjustment_setup, now) + // .unwrap() + // .affordable_accounts; + // + // let winning_account = result.remove(0); + // assert_eq!( + // &winning_account.wallet, expected_wallet_of_the_winning_account, + // "{}: expected wallet {} but got {}", + // test_scenario_name, winning_account.wallet, expected_wallet_of_the_winning_account + // ); + // assert_eq!( + // winning_account.balance_wei, standard_balance_per_account, + // "{}: expected full cw balance {}, but the account had {}", + // test_scenario_name, standard_balance_per_account, winning_account.balance_wei + // ); + // assert!( + // result.is_empty(), + // "{}: is not empty, {:?} remains", + // test_scenario_name, + // result + // ) + // } + // + // #[test] + // fn granularity_test_one_unit_missing_in_service_fee_balance_for_two_accounts_means_only_one_wins() { + // fn prepare_scenario_name(description: &str) -> String { + // format!( + // "testing_granularity_on_one_unit_missing_in_service_fee_for_two_accounts_means_\ + // only_one_wins: {}", + // description + // ) + // } + // + // let w1 = make_wallet("abc"); + // let w2 = make_wallet("def"); + // let common_input = WalletsSetup { + // wallet_1: w1.clone(), + // wallet_2: w2.clone(), + // }; + // // scenario A + // let first_scenario_name = prepare_scenario_name("when equally significant"); + // let expected_wallet_of_the_winning_account = &w2; + // + // test_two_competitive_accounts_with_one_disqualified( + // &first_scenario_name, + // CompetitiveAccountsTestInputs { + // common: common_input.clone(), + // account_1_balance_positive_correction_minor: 0, + // account_2_balance_positive_correction_minor: 0, + // }, + // expected_wallet_of_the_winning_account, + // ); + // //-------------------------------------------------------------------- + // // scenario B + // let second_scenario_name = + // prepare_scenario_name("first more significant by balance"); + // let expected_wallet_of_the_winning_account = &w2; + // + // test_two_competitive_accounts_with_one_disqualified( + // &second_scenario_name, + // CompetitiveAccountsTestInputs { + // common: common_input.clone(), + // account_1_balance_positive_correction_minor: 1, + // account_2_balance_positive_correction_minor: 0, + // }, + // expected_wallet_of_the_winning_account, + // ); + // //-------------------------------------------------------------------- + // // scenario C + // let second_scenario_name = + // prepare_scenario_name("second more significant by balance"); + // let expected_wallet_of_the_winning_account = &w1; + // + // test_two_competitive_accounts_with_one_disqualified( + // &second_scenario_name, + // CompetitiveAccountsTestInputs { + // common: common_input, + // account_1_balance_positive_correction_minor: 0, + // account_2_balance_positive_correction_minor: 1, + // }, + // expected_wallet_of_the_winning_account, + // ); + // } #[test] fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { From ebed0c2bf8a9011706245e31a383ca0de6039ca8 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 14 Apr 2024 00:08:36 +0200 Subject: [PATCH 161/250] GH-711-b: establishing a true standard for accounts considered easy to resolve right away...fairer...but some other test are gonna be suffering from that for a momemt now --- .../payment_adjuster/adjustment_runners.rs | 7 +- .../account_stages_conversions.rs | 115 ++++++++++ .../miscellaneous/helper_functions.rs | 98 --------- .../payment_adjuster/miscellaneous/mod.rs | 1 + node/src/accountant/payment_adjuster/mod.rs | 60 ++--- .../payment_adjuster/service_fee_adjuster.rs | 208 ++++++++++++++++-- 6 files changed, 325 insertions(+), 164 deletions(-) create mode 100644 node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index f2f3ca4b7..50e151b8b 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -5,9 +5,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; +use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; -use masq_lib::utils::convert_collection; // TODO review this comment // There are only two runners. They perform adjustment either by both the transaction and service @@ -78,7 +78,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { if check_sum <= unallocated_cw_balance { // Fast return after a direct conversion into the expected type - return convert_collection(weighted_accounts); + return ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( + weighted_accounts, + &payment_adjuster.disqualification_arbiter, + ); } payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs new file mode 100644 index 000000000..074292a62 --- /dev/null +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, +}; +use crate::accountant::QualifiedPayableAccount; + +// If passing along without PA just to BlockchainBridge +impl From for PayableAccount { + fn from(qualified_payable: QualifiedPayableAccount) -> Self { + qualified_payable.bare_account + } +} + +impl From for PayableAccount { + fn from(non_finalized_adjustment: AdjustedAccountBeforeFinalization) -> Self { + let mut account = non_finalized_adjustment.original_account; + account.balance_wei = non_finalized_adjustment.proposed_adjusted_balance_minor; + account + } +} + +// Preparing "remaining, unresolved accounts" for another iteration that always begins with +// WeightedPayable types +impl From for WeightedPayable { + fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { + unconfirmed_adjustment.weighted_account + } +} + +// Used after the unconfirmed adjustment pass through all confirmations +impl From for AdjustedAccountBeforeFinalization { + fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { + let proposed_adjusted_balance_minor = + unconfirmed_adjustment.proposed_adjusted_balance_minor; + let original_account = unconfirmed_adjustment + .weighted_account + .qualified_account + .bare_account; + + AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) + } +} + +// This is used when we detect that the upcoming iterations begins with a surplus in the remaining +// unallocated CW service fee, and therefore we grant the remaining accounts with the full balance +// they requested +impl From for AdjustedAccountBeforeFinalization { + fn from(weighted_account: WeightedPayable) -> Self { + let proposed_adjusted_balance_minor = + weighted_account.qualified_account.bare_account.balance_wei; + let original_account = weighted_account.qualified_account.bare_account; + + AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + }; + use crate::accountant::test_utils::make_payable_account; + use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; + + #[test] + fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let non_finalized_account = + AdjustedAccountBeforeFinalization::new(original_payable_account.clone(), 123_456_789); + + let result = PayableAccount::from(non_finalized_account); + + original_payable_account.balance_wei = 123_456_789; + assert_eq!(result, original_payable_account) + } + + fn prepare_weighted_account(payable_account: PayableAccount) -> WeightedPayable { + let qualified_account = QualifiedPayableAccount::new( + payable_account, + 444_555_666, + CreditorThresholds::new(111_111_111), + ); + WeightedPayable::new(qualified_account, 777_777_777) + } + #[test] + fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let weighted_account = prepare_weighted_account(original_payable_account.clone()); + + let result = AdjustedAccountBeforeFinalization::from(weighted_account); + + let expected_result = + AdjustedAccountBeforeFinalization::new(original_payable_account, 200_000_000); + assert_eq!(result, expected_result) + } + + #[test] + fn conversion_between_unconfirmed_adjustment_and_non_finalized_account_is_implemented() { + let mut original_payable_account = make_payable_account(123); + original_payable_account.balance_wei = 200_000_000; + let weighted_account = prepare_weighted_account(original_payable_account.clone()); + let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); + + let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); + + let expected_result = + AdjustedAccountBeforeFinalization::new(original_payable_account, 111_222_333); + assert_eq!(result, expected_result) + } +} diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8c822df10..b3a839907 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -195,56 +195,6 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( .collect() } -// If passing along without PA just to BlockchainBridge -impl From for PayableAccount { - fn from(qualified_payable: QualifiedPayableAccount) -> Self { - qualified_payable.bare_account - } -} - -impl From for PayableAccount { - fn from(non_finalized_adjustment: AdjustedAccountBeforeFinalization) -> Self { - let mut account = non_finalized_adjustment.original_account; - account.balance_wei = non_finalized_adjustment.proposed_adjusted_balance_minor; - account - } -} - -// Preparing "remaining, unresolved accounts" for another iteration that always begins with -// WeightedPayable types -impl From for WeightedPayable { - fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - unconfirmed_adjustment.weighted_account - } -} - -// Used after the unconfirmed adjustment pass through all confirmations -impl From for AdjustedAccountBeforeFinalization { - fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - let proposed_adjusted_balance_minor = - unconfirmed_adjustment.proposed_adjusted_balance_minor; - let original_account = unconfirmed_adjustment - .weighted_account - .qualified_account - .bare_account; - - AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) - } -} - -// This is used when we detect that the upcoming iterations begins with a surplus in the remaining -// unallocated CW service fee, and therefore we grant the remaining accounts with the full balance -// they requested -impl From for AdjustedAccountBeforeFinalization { - fn from(weighted_account: WeightedPayable) -> Self { - let proposed_adjusted_balance_minor = - weighted_account.qualified_account.bare_account.balance_wei; - let original_account = weighted_account.qualified_account.bare_account; - - AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) - } -} - #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; @@ -373,54 +323,6 @@ mod tests { ) } - #[test] - fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { - let mut original_payable_account = make_payable_account(123); - original_payable_account.balance_wei = 200_000_000; - let non_finalized_account = - AdjustedAccountBeforeFinalization::new(original_payable_account.clone(), 123_456_789); - - let result = PayableAccount::from(non_finalized_account); - - original_payable_account.balance_wei = 123_456_789; - assert_eq!(result, original_payable_account) - } - - fn prepare_weighted_account(payable_account: PayableAccount) -> WeightedPayable { - let qualified_account = QualifiedPayableAccount::new( - payable_account, - 444_555_666, - CreditorThresholds::new(111_111_111), - ); - WeightedPayable::new(qualified_account, 777_777_777) - } - #[test] - fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { - let mut original_payable_account = make_payable_account(123); - original_payable_account.balance_wei = 200_000_000; - let weighted_account = prepare_weighted_account(original_payable_account.clone()); - - let result = AdjustedAccountBeforeFinalization::from(weighted_account); - - let expected_result = - AdjustedAccountBeforeFinalization::new(original_payable_account, 200_000_000); - assert_eq!(result, expected_result) - } - - #[test] - fn conversion_between_unconfirmed_adjustment_and_non_finalized_account_is_implemented() { - let mut original_payable_account = make_payable_account(123); - original_payable_account.balance_wei = 200_000_000; - let weighted_account = prepare_weighted_account(original_payable_account.clone()); - let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); - - let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); - - let expected_result = - AdjustedAccountBeforeFinalization::new(original_payable_account, 111_222_333); - assert_eq!(result, expected_result) - } - #[test] fn exhaustive_status_is_constructed_properly() { let cw_balance_remainder = 45678; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs index bc40584d2..4d9c39e16 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/mod.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/mod.rs @@ -1,4 +1,5 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +mod account_stages_conversions; pub mod data_structures; pub mod helper_functions; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bd5fe8e3c..2b32cf0b9 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. // If possible, let these modules be private mod adjustment_runners; @@ -304,9 +304,9 @@ impl PaymentAdjusterReal { &mut self, weighed_accounts: Vec, ) -> Vec { + let disqualification_arbiter = &self.disqualification_arbiter; let unallocated_cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let disqualification_arbiter = &self.disqualification_arbiter; let logger = &self.logger; let current_iteration_result = self.service_fee_adjuster.perform_adjustment_by_service_fee( @@ -1519,54 +1519,26 @@ mod tests { init_test_logging(); let test_name = "only_service_fee_balance_limits_the_payments_count"; let now = SystemTime::now(); - let wallet_1 = make_wallet("def"); // Account to be adjusted to keep as much as how much is left in the cw balance - let account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 333_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(12000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); + let balance_1 = multiple_by_billion(333_000_000); + let account_1 = make_plucked_qualified_account("abc", balance_1, 200_000_000, 50_000_000); + let wallet_1 = account_1.bare_account.wallet.clone(); // Account to be outweighed and fully preserved - let wallet_2 = make_wallet("abc"); - let account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 111_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(8000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); + let balance_2 = multiple_by_billion(111_000_000); + let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000, 10_000_000); + let wallet_2 = account_2.bare_account.wallet.clone(); // Account to be disqualified - let wallet_3 = make_wallet("ghk"); - let balance_3 = 600_000_000_000; - let account_3 = QualifiedPayableAccount::new( - PayableAccount { - wallet: wallet_3.clone(), - balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(6000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); + let balance_3 = multiple_by_billion(600_000_000); + let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); + let wallet_3 = account_3.bare_account.wallet.clone(); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let service_fee_balance_in_minor_units = 333_000_000_000 + 50_000_000_000; + let service_fee_balance_in_minor_units = balance_3 + balance_2; let agent_id_stamp = ArbitraryIdStamp::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); - Box::new(mock) - }; + let agent = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); let response_skeleton_opt = Some(ResponseSkeleton { client_id: 111, context_id: 234, @@ -1574,7 +1546,7 @@ mod tests { // Another place where I pick a populated response skeleton for hardening let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::ByServiceFee, response_skeleton_opt, }; diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 8c8df0199..85a8af345 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,7 +1,7 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ - outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics, + proposed_adjusted_balance_diagnostics, }; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ @@ -17,6 +17,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; +use std::vec; pub trait ServiceFeeAdjuster { fn perform_adjustment_by_service_fee( @@ -40,9 +41,11 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self - .adjustment_computer - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + let non_finalized_adjusted_accounts = self.generate_adjustments( + weighted_accounts, + disqualification_arbiter, + cw_service_fee_balance_minor, + ); let still_unchecked_for_disqualified = match Self::handle_possibly_outweighed_accounts( disqualification_arbiter, @@ -72,15 +75,76 @@ impl Default for ServiceFeeAdjusterReal { } impl ServiceFeeAdjusterReal { + pub fn assign_accounts_their_minimal_acceptable_balance( + accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, + ) -> Vec + where + WeightedPayable: From, + OutputAccounts: From, + { + // In some cases taking advantage of that Rust std library implements also From for T + let weighted_accounts: Vec = convert_collection(accounts); + + let unconfirmed_accounts = weighted_accounts + .into_iter() + .map(|weighted_account| { + let disqualification_limit = disqualification_arbiter + .calculate_disqualification_edge(&weighted_account.qualified_account); + UnconfirmedAdjustment::new(weighted_account, disqualification_limit) + }) + .collect(); + + convert_collection(unconfirmed_accounts) + } + fn new() -> Self { Self { adjustment_computer: Default::default(), } } + fn generate_adjustments( + &self, + weighted_accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, + cw_service_fee_balance_minor: u128, + ) -> Vec { + if weighted_accounts.len() == 1 { + let last_account = { + let mut weighted_accounts = weighted_accounts; + weighted_accounts.remove(0) + }; + vec![Self::handle_last_account( + last_account, + disqualification_arbiter, + cw_service_fee_balance_minor, + )] + } else { + self.adjustment_computer + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor) + } + } + // The term "outweighed account" comes from a phenomenon with account weight increasing - // significantly based on a different parameter than the debt size. Untreated, we would which - // grant the account (much) more money than what the accountancy has recorded for it. + // significantly based on a different parameter than the debt size. Untreated, we would + // grant the account (much) more money than what the accountancy has actually recorded. + // + // Outweighed accounts gain instantly the portion that tallies with the disqualification + // limit for this account. That's why we use the disqualification arbiter also in other + // places than only where we test an account on its minimal allowed size, considering + // elimination of such accounts. + // + // The idea is that we want to spare as much as possible means that could be distributed + // among the rest of accounts. Given we declare that accounts having at least the size + // of the disqualification limit are fine, we don't exhaust the means right away on + // these. + // + // On the other hand, if it turns out the spared money cannot be effectively used + // to adjust more accounts for the minimal necessary value, letting them fall away + // eventually, there is still the ending operation where the already prepared accounts + // are reconsidered to be give more bits from the fund of unallocated money, all down + // to zero. fn handle_possibly_outweighed_accounts( disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, @@ -156,22 +220,27 @@ impl ServiceFeeAdjusterReal { .balance_wei }); - let outweighed_adjusted = outweighed - .into_iter() - .map(|account| { - outweighed_accounts_diagnostics(&account); - - let maximized_proposed_adjusted_balance_minor = disqualification_arbiter - .calculate_disqualification_edge(&account.weighted_account.qualified_account); - AdjustedAccountBeforeFinalization::new( - account.weighted_account.qualified_account.bare_account, - maximized_proposed_adjusted_balance_minor, - ) - }) - .collect(); + let outweighed_adjusted = Self::assign_accounts_their_minimal_acceptable_balance( + outweighed, + disqualification_arbiter, + ); (outweighed_adjusted, properly_adjusted_accounts) } + + fn handle_last_account( + account: WeightedPayable, + disqualification_arbiter: &DisqualificationArbiter, + cw_service_fee_balance_minor: u128, + ) -> UnconfirmedAdjustment { + let disqualification_limit = + disqualification_arbiter.calculate_disqualification_edge(&account.qualified_account); + if disqualification_limit >= cw_service_fee_balance_minor { + UnconfirmedAdjustment::new(account, cw_service_fee_balance_minor) + } else { + UnconfirmedAdjustment::new(account, disqualification_limit) + } + } } #[derive(Default)] @@ -237,11 +306,14 @@ impl AdjustmentComputer { #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, DisqualificationGaugeMock, }; + use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; #[test] fn adjust_account_balance_if_outweighed_limits_them_by_the_standard_disqualification_edge() { @@ -317,4 +389,100 @@ mod tests { ]; assert_eq!(outweighed_accounts, expected_adjusted_outweighed_accounts) } + + #[test] + fn assign_accounts_their_minimal_acceptable_balance_works_for_unconfirmed_accounts() { + let unconfirmed_account_1 = make_non_guaranteed_unconfirmed_adjustment(111); + let weighted_account_1 = unconfirmed_account_1.weighted_account.clone(); + let unconfirmed_account_2 = make_non_guaranteed_unconfirmed_adjustment(222); + let weighted_account_2 = unconfirmed_account_2.weighted_account.clone(); + let accounts = vec![unconfirmed_account_1, unconfirmed_account_2]; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(123456789) + .determine_limit_result(987654321); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + + let result: Vec = + ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( + accounts, + &disqualification_arbiter, + ); + + let expected_result = vec![ + UnconfirmedAdjustment::new(weighted_account_1, 123456789), + UnconfirmedAdjustment::new(weighted_account_2, 987654321), + ]; + assert_eq!(result, expected_result) + } + + #[test] + fn assign_accounts_their_minimal_acceptable_balance_works_for_non_finalized_accounts() { + let unconfirmed_account_1 = make_non_guaranteed_unconfirmed_adjustment(111); + let payable_account_1 = unconfirmed_account_1 + .weighted_account + .qualified_account + .bare_account + .clone(); + let unconfirmed_account_2 = make_non_guaranteed_unconfirmed_adjustment(222); + let payable_account_2 = unconfirmed_account_2 + .weighted_account + .qualified_account + .bare_account + .clone(); + let accounts = vec![unconfirmed_account_1, unconfirmed_account_2]; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(1111111) + .determine_limit_result(2222222); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + + let result: Vec = + ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( + accounts, + &disqualification_arbiter, + ); + + let expected_result = vec![ + AdjustedAccountBeforeFinalization::new(payable_account_1, 1111111), + AdjustedAccountBeforeFinalization::new(payable_account_2, 2222222), + ]; + assert_eq!(result, expected_result) + } + + #[test] + fn handle_last_account_works_for_remaining_cw_balance_compared_to_the_disqualification_limit() { + let cw_service_fee_balance_minor = 123_000; + let expected_proposed_balances = vec![ + cw_service_fee_balance_minor - 1, + cw_service_fee_balance_minor, + cw_service_fee_balance_minor, + ]; + let qualified_account = make_non_guaranteed_qualified_payable(456); + let weighted_account = WeightedPayable::new(qualified_account, 1111111); + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(cw_service_fee_balance_minor - 1) + .determine_limit_result(cw_service_fee_balance_minor) + .determine_limit_result(cw_service_fee_balance_minor + 1); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let subject = ServiceFeeAdjusterReal::default(); + expected_proposed_balances + .iter() + .for_each(|expected_proposed_balance| { + let result = subject.generate_adjustments( + vec![weighted_account.clone()], + &disqualification_arbiter, + cw_service_fee_balance_minor, + ); + + assert_eq!( + result, + vec![UnconfirmedAdjustment::new( + weighted_account.clone(), + *expected_proposed_balance + )] + ) + }); + } } From 80f5266d2020ed6dd969db99d9dd54ed572a5f14 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 14 Apr 2024 12:15:17 +0200 Subject: [PATCH 162/250] GH-711-b: more tests fixed and reduced unnecessary code in the adjustments generating place --- multinode_integration_tests/ci/all.sh | 2 +- .../src/masq_cores_server.rs | 2 +- .../tests/verify_bill_payment.rs | 2 +- .../db_access_objects/receivable_dao.rs | 2 +- node/src/accountant/mod.rs | 2 +- .../payment_adjuster/adjustment_runners.rs | 43 +++- .../disqualification_arbiter.rs | 4 +- .../diagnostics.rs | 39 ++- .../log_functions.rs} | 2 +- .../logging_and_diagnostics/mod.rs | 3 + .../miscellaneous/data_structures.rs | 4 +- .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 44 ++-- .../{loading_test => non_unit_tests}/mod.rs | 2 +- .../payment_adjuster/preparatory_analyser.rs | 2 +- .../payment_adjuster/service_fee_adjuster.rs | 226 +++++++++++------- node/src/accountant/scanners/mod.rs | 2 +- .../blockchain_interface_web3/mod.rs | 4 +- node/src/bootstrapper.rs | 6 +- node/src/daemon/launch_verifier.rs | 2 +- node/src/database/db_initializer.rs | 2 +- node/src/db_config/db_encryption_layer.rs | 2 +- node/src/neighborhood/gossip_acceptor.rs | 6 +- node/src/neighborhood/mod.rs | 2 +- node/src/sub_lib/accountant.rs | 2 +- node/src/sub_lib/neighborhood.rs | 2 +- node/src/sub_lib/ttl_hashmap.rs | 2 +- node/src/test_utils/mod.rs | 6 +- .../src/test_utils/neighborhood_test_utils.rs | 2 +- .../node_exits_from_crash_message_test.rs | 2 +- 30 files changed, 263 insertions(+), 160 deletions(-) rename node/src/accountant/payment_adjuster/{ => logging_and_diagnostics}/diagnostics.rs (83%) rename node/src/accountant/payment_adjuster/{log_fns.rs => logging_and_diagnostics/log_functions.rs} (98%) create mode 100644 node/src/accountant/payment_adjuster/logging_and_diagnostics/mod.rs rename node/src/accountant/payment_adjuster/{loading_test => non_unit_tests}/mod.rs (99%) diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index 6ce38689c..b8b6212e0 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -7,7 +7,7 @@ export PATH="$PATH:$HOME/.cargo/bin" export RUST_BACKTRACE=full -# Docker-beside-Docker: Running these tests requires building and starting Docker containers that refer to an +# Docker-beside-Docker: Running these non_unit_tests requires building and starting Docker containers that refer to an # existing executable on the host system. If you're not running in a Docker container, the executable you want # will be in your own filesystem, and these scripts can find everything they need without assistance. But if you # _are_ running in a Docker container, the containers you start will be your siblings, not your children, and the diff --git a/multinode_integration_tests/src/masq_cores_server.rs b/multinode_integration_tests/src/masq_cores_server.rs index c98fcd661..9585db0e1 100644 --- a/multinode_integration_tests/src/masq_cores_server.rs +++ b/multinode_integration_tests/src/masq_cores_server.rs @@ -30,7 +30,7 @@ use std::thread; use std::thread::JoinHandle; use std::time::Duration; -// TODO: Cover this with tests and put it in the production tree. +// TODO: Cover this with non_unit_tests and put it in the production tree. pub struct DiscriminatorCluster { discriminators: Vec, } diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 52c472694..0238f4d2b 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -566,7 +566,7 @@ struct TestInputsOutputsConfig { ui_ports_opt: Option, // Gets ganache default 100 ETH if None. // Uses an arg where the private key of the wallet and the amount in - // wei must be specified (in these tests the key is hardcoded and + // wei must be specified (in these non_unit_tests the key is hardcoded and // corresponds to the path m/44'/60'/0'/0/0/1 that belongs to // the consuming Node). // Specify number of wei this account should possess at its initialisation diff --git a/node/src/accountant/db_access_objects/receivable_dao.rs b/node/src/accountant/db_access_objects/receivable_dao.rs index dd24fb91a..4187a2fb9 100644 --- a/node/src/accountant/db_access_objects/receivable_dao.rs +++ b/node/src/accountant/db_access_objects/receivable_dao.rs @@ -79,7 +79,7 @@ pub trait ReceivableDao: Send { fn total(&self) -> i128; - // Test-only-like method but because of share with multi-node tests #[cfg(test)] is disallowed + // Test-only-like method but because of share with multi-node non_unit_tests #[cfg(test)] is disallowed fn account_status(&self, wallet: &Wallet) -> Option; as_any_in_trait!(); diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index a78ddcac1..a99973c02 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -4893,7 +4893,7 @@ pub mod exportable_test_parts { let assertion_msg = AssertionsMessage { assertions: Box::new(|accountant: &mut Accountant| { // Will crash a test if our user-defined SQLite fns have been unreachable; - // We cannot rely on failures in the DAO tests, because Account's database connection + // We cannot rely on failures in the DAO non_unit_tests, because Account's database connection // has to be set up specially first (we teach it about the extra functions) as we're // creating the actor diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 50e151b8b..a386fe07d 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -78,9 +78,10 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { if check_sum <= unallocated_cw_balance { // Fast return after a direct conversion into the expected type + let disqualification_arbiter = &payment_adjuster.disqualification_arbiter; return ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( weighted_accounts, - &payment_adjuster.disqualification_arbiter, + disqualification_arbiter, ); } @@ -132,9 +133,15 @@ mod tests { payable_1: WeightedPayable, payable_2: WeightedPayable, cw_service_fee_balance_minor: u128, + expected_proposed_balance_1: u128, + expected_proposed_balance_2: u128, ) { - // The disqualification doesn't take part in here, it is just an explanation for those who - // wonder why the implied surplus may happen + // Explanation: The hypothesis is that the previous iteration disqualified an account after + // which the remaining means are enough for the other accounts. + // We could assign the accounts all they initially requested but a fairer way to do that + // is to give out only that much up to the disqualification limit of these accounts. Later on, + // the accounts that deserves it more will split the rest of the means among them (Their + // weights were higher). let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); @@ -152,26 +159,40 @@ mod tests { vec![ AdjustedAccountBeforeFinalization { original_account: payable_1.qualified_account.bare_account, - proposed_adjusted_balance_minor: initial_balance_minor_1 + proposed_adjusted_balance_minor: expected_proposed_balance_1 }, AdjustedAccountBeforeFinalization { original_account: payable_2.qualified_account.bare_account, - proposed_adjusted_balance_minor: initial_balance_minor_2 + proposed_adjusted_balance_minor: expected_proposed_balance_2 } ] ) } + fn weighted_payable_setup_for_surplus_test( + n: u64, + initial_balance_minor: u128, + ) -> WeightedPayable { + let mut account = make_weighed_payable(n, initial_balance_minor); + account.qualified_account.payment_threshold_intercept_minor = 3_000_000_000; + account.qualified_account.creditor_thresholds = CreditorThresholds::new(1_000_000_000); + account + } + #[test] fn service_fee_only_runner_cw_balance_equals_requested_money_after_dsq_in_previous_iteration() { let cw_service_fee_balance_minor = 10_000_000_000; - let payable_1 = make_weighed_payable(111, 5_000_000_000); - let payable_2 = make_weighed_payable(222, 5_000_000_000); + let payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); + let payable_2 = weighted_payable_setup_for_surplus_test(222, 5_000_000_000); + let expected_proposed_balance_1 = 3_000_000_000; + let expected_proposed_balance_2 = 3_000_000_000; test_surplus_incurred_after_disqualification_in_previous_iteration( payable_1, payable_2, cw_service_fee_balance_minor, + expected_proposed_balance_1, + expected_proposed_balance_2, ) } @@ -179,13 +200,17 @@ mod tests { fn service_fee_only_runner_handles_means_bigger_requested_money_after_dsq_in_previous_iteration( ) { let cw_service_fee_balance_minor = 10_000_000_000; - let payable_1 = make_weighed_payable(111, 5_000_000_000); - let payable_2 = make_weighed_payable(222, 4_999_999_999); + let payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); + let payable_2 = weighted_payable_setup_for_surplus_test(222, 4_999_999_999); + let expected_proposed_balance_1 = 3_000_000_000; + let expected_proposed_balance_2 = 2_999_999_999; test_surplus_incurred_after_disqualification_in_previous_iteration( payable_1, payable_2, cw_service_fee_balance_minor, + expected_proposed_balance_1, + expected_proposed_balance_2, ) } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 08eb77e66..427d86011 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -1,10 +1,10 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ account_nominated_for_disqualification_diagnostics, try_finding_an_account_to_disqualify_diagnostics, }; -use crate::accountant::payment_adjuster::log_fns::info_log_for_disqualified_account; +use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; diff --git a/node/src/accountant/payment_adjuster/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs similarity index 83% rename from node/src/accountant/payment_adjuster/diagnostics.rs rename to node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 6a8348ce7..9ea1ef441 100644 --- a/node/src/accountant/payment_adjuster/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -95,7 +95,7 @@ pub mod ordinary_diagnostic_functions { use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; @@ -140,13 +140,46 @@ pub mod ordinary_diagnostic_functions { ); } + pub fn minimal_acceptable_balance_assigned_diagnostics( + weighted_account: &WeightedPayable, + disqualification_limit: u128, + ) { + diagnostics!( + weighted_account.qualified_account.bare_account.wallet, + "MINIMAL ACCEPTABLE BALANCE ASSIGNED", + "Used disqualification limit for given account {}", + disqualification_limit.separate_with_commas() + ) + } + + pub fn handle_last_account_diagnostics( + account: &WeightedPayable, + cw_service_fee_balance_minor: u128, + disqualification_limit_opt: Option, + ) { + diagnostics!( + account.qualified_account.bare_account.wallet, + "HANDLING LAST ACCOUNT", + "Remaining CW balance {} is {}", + cw_service_fee_balance_minor, + if let Some(dsq_limit) = disqualification_limit_opt { + format!( + "larger than the disqualification limit {} which is therefore assigned instead", + dsq_limit + ) + } else { + "below the disqualification limit and assigned in full extend".to_string() + } + ) + } + pub fn exhausting_cw_balance_diagnostics( non_finalized_account_info: &AdjustedAccountBeforeFinalization, possible_extra_addition: u128, ) { diagnostics!( "EXHAUSTING CW ON PAYMENT", - "For account {} from proposed {} to the possible maximum of {}", + "Account {} from proposed {} to the possible maximum of {}", non_finalized_account_info.original_account.wallet, non_finalized_account_info.proposed_adjusted_balance_minor, non_finalized_account_info.proposed_adjusted_balance_minor + possible_extra_addition @@ -211,7 +244,7 @@ pub mod ordinary_diagnostic_functions { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::diagnostics::PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS; + use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS; #[test] fn constants_are_correct() { diff --git a/node/src/accountant/payment_adjuster/log_fns.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs similarity index 98% rename from node/src/accountant/payment_adjuster/log_fns.rs rename to node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index 2996c1172..bf687f83d 100644 --- a/node/src/accountant/payment_adjuster/log_fns.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -175,7 +175,7 @@ pub fn log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger: &Lo #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; - use crate::accountant::payment_adjuster::log_fns::{ + use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ info_log_for_disqualified_account, LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, REFILL_RECOMMENDATION, }; diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/mod.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/mod.rs new file mode 100644 index 000000000..3f96fc9fe --- /dev/null +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/mod.rs @@ -0,0 +1,3 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +pub mod diagnostics; +pub mod log_functions; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index c3baf7222..1b26ffa9a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -19,7 +19,7 @@ impl WeightedPayable { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum AdjustmentIterationResult { AllAccountsProcessed(Vec), IterationWithSpecialHandling { @@ -52,7 +52,7 @@ impl RecursionResults { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum SpecialHandling { InsignificantAccountEliminated, OutweighedAccounts(Vec), diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index b3a839907..1f9287e2d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -2,7 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 2b32cf0b9..c901fdda5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -3,13 +3,12 @@ // If possible, let these modules be private mod adjustment_runners; mod criterion_calculators; -mod diagnostics; mod disqualification_arbiter; mod inner; -#[cfg(test)] -mod loading_test; -mod log_fns; +mod logging_and_diagnostics; mod miscellaneous; +#[cfg(test)] +mod non_unit_tests; mod preparatory_analyser; mod service_fee_adjuster; #[cfg(test)] @@ -21,15 +20,15 @@ use crate::accountant::payment_adjuster::adjustment_runners::{ }; use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; -use crate::accountant::payment_adjuster::diagnostics::{collection_diagnostics, diagnostics}; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::{collection_diagnostics, diagnostics}; use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; -use crate::accountant::payment_adjuster::log_fns::{ +use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ @@ -980,7 +979,7 @@ mod tests { ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 2799999999999999 + 2300000000000000 ); assert!(result.is_empty()); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); @@ -1279,7 +1278,7 @@ mod tests { // This function should take just such essential args as balances and those that play rather // a secondary role, yet an important one in the verification processes for proposed adjusted // balances. Refrain from employing more of the weights-affecting parameters as they would - // only burden us with their consideration in these tests. + // only burden us with their consideration in these non_unit_tests. fn make_plucked_qualified_account( wallet_addr_fragment: &str, balance_minor: u128, @@ -1519,7 +1518,7 @@ mod tests { init_test_logging(); let test_name = "only_service_fee_balance_limits_the_payments_count"; let now = SystemTime::now(); - // Account to be adjusted to keep as much as how much is left in the cw balance + // Account to be adjusted to keep as much as it is left in the cw balance let balance_1 = multiple_by_billion(333_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 200_000_000, 50_000_000); let wallet_1 = account_1.bare_account.wallet.clone(); @@ -1534,16 +1533,15 @@ mod tests { let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let service_fee_balance_in_minor_units = balance_3 + balance_2; + let service_fee_balance_in_minor_units = balance_1 + balance_2 - 55; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(service_fee_balance_in_minor_units); let response_skeleton_opt = Some(ResponseSkeleton { - client_id: 111, + client_id: 11, context_id: 234, }); - // Another place where I pick a populated response skeleton for hardening let adjustment_setup = PreparedAdjustment { qualified_payables, agent: Box::new(agent), @@ -1554,27 +1552,25 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); let expected_accounts = { - let account_1_adjusted = PayableAccount { - balance_wei: 272_000_000_000, - ..account_1.bare_account - }; - vec![account_1_adjusted, account_2.bare_account] + let mut account_1_adjusted = account_1; + account_1_adjusted.bare_account.balance_wei -= 55; + vec![account_2.bare_account, account_1_adjusted.bare_account] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); assert_eq!( result.response_skeleton_opt, Some(ResponseSkeleton { - client_id: 111, + client_id: 11, context_id: 234 }) ); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); TestLogHandler::new().exists_log_containing(&format!( - "INFO: {test_name}: Shortage of MASQ \ - in your consuming wallet impacts on payable 0x000000000000000000000000000000000067686b, \ - ruled out from this round of payments. The proposed adjustment 79,556,958,958 wei was less \ - than half of the recorded debt, 600,000,000,000 wei" + "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ + 0x0000000000000000000000000000000000676869, ruled out from this round of payments. \ + The proposed adjustment 159,743,040,685,224,815 wei was below the disqualification \ + limit 300,000,000,000,000,000 wei" )); } @@ -2015,7 +2011,7 @@ mod tests { Box::new(blockchain_agent) } - // The following tests together prove the use of correct calculators in the production code + // The following non_unit_tests together prove the use of correct calculators in the production code #[test] fn each_of_defaulted_calculators_returns_different_value() { diff --git a/node/src/accountant/payment_adjuster/loading_test/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs similarity index 99% rename from node/src/accountant/payment_adjuster/loading_test/mod.rs rename to node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 21438994d..c392accd2 100644 --- a/node/src/accountant/payment_adjuster/loading_test/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -317,7 +317,7 @@ fn render_results_to_file_and_attempt_basic_assertions( number_of_requested_scenarios: usize, overall_output_collector: TestOverallOutputCollector, ) { - let file_dir = ensure_node_home_directory_exists("payment_adjuster", "loading_test"); + let file_dir = ensure_node_home_directory_exists("payment_adjuster", "non_unit_tests"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); introduction(&mut file); let test_overall_output_collector = diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 1cdedf471..047887aac 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -1,7 +1,7 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; -use crate::accountant::payment_adjuster::log_fns::{ +use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 85a8af345..e952bb7c3 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,8 +1,5 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::diagnostics::ordinary_diagnostic_functions::{ - proposed_adjusted_balance_diagnostics, -}; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ InsignificantAccountEliminated, OutweighedAccounts, @@ -11,6 +8,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, }; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: +ordinary_diagnostic_functions::{minimal_acceptable_balance_assigned_diagnostics, handle_last_account_diagnostics, outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; @@ -41,11 +40,17 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self.generate_adjustments( - weighted_accounts, - disqualification_arbiter, - cw_service_fee_balance_minor, - ); + let non_finalized_adjusted_accounts = self + .adjustment_computer + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + // let non_finalized_adjusted_accounts = match self.generate_adjustments( + // weighted_accounts, + // disqualification_arbiter, + // cw_service_fee_balance_minor, + // ){ + // Either::Left(multiple_accounts)=> multiple_accounts, + // Either::Right(single_account) => return single_account + // }; let still_unchecked_for_disqualified = match Self::handle_possibly_outweighed_accounts( disqualification_arbiter, @@ -91,6 +96,10 @@ impl ServiceFeeAdjusterReal { .map(|weighted_account| { let disqualification_limit = disqualification_arbiter .calculate_disqualification_edge(&weighted_account.qualified_account); + minimal_acceptable_balance_assigned_diagnostics( + &weighted_account, + disqualification_limit, + ); UnconfirmedAdjustment::new(weighted_account, disqualification_limit) }) .collect(); @@ -104,27 +113,31 @@ impl ServiceFeeAdjusterReal { } } - fn generate_adjustments( - &self, - weighted_accounts: Vec, - disqualification_arbiter: &DisqualificationArbiter, - cw_service_fee_balance_minor: u128, - ) -> Vec { - if weighted_accounts.len() == 1 { - let last_account = { - let mut weighted_accounts = weighted_accounts; - weighted_accounts.remove(0) - }; - vec![Self::handle_last_account( - last_account, - disqualification_arbiter, - cw_service_fee_balance_minor, - )] - } else { - self.adjustment_computer - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor) - } - } + //TODO delete me + // fn generate_adjustments( + // &self, + // weighted_accounts: Vec, + // disqualification_arbiter: &DisqualificationArbiter, + // cw_service_fee_balance_minor: u128, + // ) -> Either, AdjustmentIterationResult> { + // let remaining_count = weighted_accounts.len(); + // if remaining_count > 1 { + // Either::Left(self.adjustment_computer + // .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor)) + // } else if remaining_count == 1 { + // let last_account = { + // let mut weighted_accounts = weighted_accounts; + // weighted_accounts.remove(0) + // }; + // Either::Right(Self::handle_last_account( + // last_account, + // disqualification_arbiter, + // cw_service_fee_balance_minor, + // )) + // } else { + // unreachable!("Should end by processing one or multiple accounts") + // } + // } // The term "outweighed account" comes from a phenomenon with account weight increasing // significantly based on a different parameter than the debt size. Untreated, we would @@ -209,38 +222,59 @@ impl ServiceFeeAdjusterReal { Vec, Vec, ) { - let (outweighed, properly_adjusted_accounts): (Vec<_>, Vec<_>) = unconfirmed_adjustments - .into_iter() - .partition(|adjustment_info| { - adjustment_info.proposed_adjusted_balance_minor - > adjustment_info + let init: (Vec, Vec) = (vec![], vec![]); + let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments.into_iter().fold( + init, + |(mut outweighed, mut properly_adjusted_accounts), current| { + if current.proposed_adjusted_balance_minor + > current .weighted_account .qualified_account .bare_account .balance_wei - }); - - let outweighed_adjusted = Self::assign_accounts_their_minimal_acceptable_balance( - outweighed, - disqualification_arbiter, + { + outweighed_accounts_diagnostics(¤t); + outweighed.push(current) + } else { + properly_adjusted_accounts.push(current) + } + (outweighed, properly_adjusted_accounts) + }, ); + let outweighed_adjusted = if outweighed.is_empty() { + vec![] + } else { + Self::assign_accounts_their_minimal_acceptable_balance( + outweighed, + disqualification_arbiter, + ) + }; + (outweighed_adjusted, properly_adjusted_accounts) } - fn handle_last_account( - account: WeightedPayable, - disqualification_arbiter: &DisqualificationArbiter, - cw_service_fee_balance_minor: u128, - ) -> UnconfirmedAdjustment { - let disqualification_limit = - disqualification_arbiter.calculate_disqualification_edge(&account.qualified_account); - if disqualification_limit >= cw_service_fee_balance_minor { - UnconfirmedAdjustment::new(account, cw_service_fee_balance_minor) - } else { - UnconfirmedAdjustment::new(account, disqualification_limit) - } - } + //TODO delete me + // fn handle_last_account( + // account: WeightedPayable, + // disqualification_arbiter: &DisqualificationArbiter, + // cw_service_fee_balance_minor: u128, + // ) -> AdjustmentIterationResult { + // let disqualification_limit = + // disqualification_arbiter.calculate_disqualification_edge(&account.qualified_account); + // let non_finalized_account = if disqualification_limit >= cw_service_fee_balance_minor { + // handle_last_account_diagnostics(&account, cw_service_fee_balance_minor, None); + // AdjustedAccountBeforeFinalization::new(account.qualified_account.bare_account, cw_service_fee_balance_minor) + // } else { + // handle_last_account_diagnostics( + // &account, + // cw_service_fee_balance_minor, + // Some(disqualification_limit), + // ); + // AdjustedAccountBeforeFinalization::new(account.qualified_account.bare_account, disqualification_limit) + // }; + // AdjustmentIterationResult::AllAccountsProcessed(vec![non_finalized_account]) + // } } #[derive(Default)] @@ -257,14 +291,13 @@ impl AdjustmentComputer { let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance); - eprintln!("multiplication coeff {}", multiplication_coefficient); let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( cw_service_fee_balance, weights_total, multiplication_coefficient, ); - eprintln!("proportional fragment {}", proportional_cw_balance_fragment); + let compute_proposed_adjusted_balance = |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; @@ -307,13 +340,15 @@ impl AdjustmentComputer { mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, + WeightedPayable, }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, DisqualificationGaugeMock, }; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use itertools::Either; #[test] fn adjust_account_balance_if_outweighed_limits_them_by_the_standard_disqualification_edge() { @@ -450,39 +485,50 @@ mod tests { assert_eq!(result, expected_result) } - #[test] - fn handle_last_account_works_for_remaining_cw_balance_compared_to_the_disqualification_limit() { - let cw_service_fee_balance_minor = 123_000; - let expected_proposed_balances = vec![ - cw_service_fee_balance_minor - 1, - cw_service_fee_balance_minor, - cw_service_fee_balance_minor, - ]; - let qualified_account = make_non_guaranteed_qualified_payable(456); - let weighted_account = WeightedPayable::new(qualified_account, 1111111); - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(cw_service_fee_balance_minor - 1) - .determine_limit_result(cw_service_fee_balance_minor) - .determine_limit_result(cw_service_fee_balance_minor + 1); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let subject = ServiceFeeAdjusterReal::default(); - expected_proposed_balances - .iter() - .for_each(|expected_proposed_balance| { - let result = subject.generate_adjustments( - vec![weighted_account.clone()], - &disqualification_arbiter, - cw_service_fee_balance_minor, - ); - - assert_eq!( - result, - vec![UnconfirmedAdjustment::new( - weighted_account.clone(), - *expected_proposed_balance - )] - ) - }); - } + //TODO delete us + // #[test] + // fn handle_last_account_works_for_remaining_cw_balance_compared_to_the_disqualification_limit() { + // let cw_service_fee_balance_minor = 123_000; + // let expected_proposed_balances = vec![ + // cw_service_fee_balance_minor - 1, + // cw_service_fee_balance_minor, + // cw_service_fee_balance_minor, + // ]; + // let qualified_account = make_non_guaranteed_qualified_payable(456); + // let weighted_account = WeightedPayable::new(qualified_account, 1111111); + // let disqualification_gauge = DisqualificationGaugeMock::default() + // .determine_limit_result(cw_service_fee_balance_minor - 1) + // .determine_limit_result(cw_service_fee_balance_minor) + // .determine_limit_result(cw_service_fee_balance_minor + 1); + // let disqualification_arbiter = + // DisqualificationArbiter::new(Box::new(disqualification_gauge)); + // let subject = ServiceFeeAdjusterReal::default(); + // expected_proposed_balances + // .iter() + // .for_each(|expected_proposed_balance| { + // let result = subject.generate_adjustments( + // vec![weighted_account.clone()], + // &disqualification_arbiter, + // cw_service_fee_balance_minor, + // ); + // + // assert_eq!( + // result, + // Either::Right(AdjustmentIterationResult::AllAccountsProcessed(vec![AdjustedAccountBeforeFinalization::new( + // weighted_account.qualified_account.bare_account.clone(), + // *expected_proposed_balance + // )])) + // ) + // }); + // } + // + // #[test] + // #[should_panic(expected = "internal error: entered unreachable code: Should end by processing \ + // one or multiple accounts")] + // fn generate_adjustments_with_no_account_remaining(){ + // let subject = ServiceFeeAdjusterReal::default(); + // let disqualification_arbiter = DisqualificationArbiter::default(); + // + // let _ = subject.generate_adjustments(vec![], &disqualification_arbiter, 123456789); + // } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 76ec15047..34a85f65f 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -963,7 +963,7 @@ impl ReceivableScanner { pub enum BeginScanError { NothingToProcess, ScanAlreadyRunning(SystemTime), - CalledFromNullScanner, // Exclusive for tests + CalledFromNullScanner, // Exclusive for non_unit_tests } impl BeginScanError { 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 79e4bef85..ea6cb7331 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -1885,7 +1885,7 @@ mod tests { } //an adapted test from old times when we had our own signing method - //I don't have data for the new chains so I omit them in this kind of tests + //I don't have data for the new chains so I omit them in this kind of non_unit_tests #[test] fn signs_various_transactions_for_eth_mainnet() { let signatures = &[ @@ -1919,7 +1919,7 @@ mod tests { } //an adapted test from old times when we had our own signing method - //I don't have data for the new chains so I omit them in this kind of tests + //I don't have data for the new chains so I omit them in this kind of non_unit_tests #[test] fn signs_various_transactions_for_ropsten() { let signatures = &[ diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 5d40c2379..90bdd4ba0 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -97,7 +97,7 @@ pub struct CryptDEPair { pub main: &'static dyn CryptDE, // This has the public key with which this Node instructs exit Nodes to encrypt responses. // In production, it is unrelated to the main public key to prevent the exit Node from - // identifying the originating Node. In tests using --fake-public-key, the alias public key + // identifying the originating Node. In non_unit_tests using --fake-public-key, the alias public key // is the main public key reversed. pub alias: &'static dyn CryptDE, } @@ -493,7 +493,7 @@ impl ConfiguredByPrivilege for Bootstrapper { multi_config: &MultiConfig, _: &mut StdStreams, ) -> Result<(), ConfiguratorError> { - // NOTE: The following line of code is not covered by unit tests + // NOTE: The following line of code is not covered by unit non_unit_tests fdlimit::raise_fd_limit(); let unprivileged_config = NodeConfiguratorStandardUnprivileged::new(&self.config).configure(multi_config)?; @@ -512,7 +512,7 @@ impl ConfiguredByPrivilege for Bootstrapper { ); self.config.node_descriptor = node_descriptor; // Before you remove local-descriptor reporting for non-Standard neighborhood modes, make - // sure you modify the multinode tests so that they can tell A) when a Node has started up, + // sure you modify the multinode non_unit_tests so that they can tell A) when a Node has started up, // and B) what its public key is. match &self.config.neighborhood_config.mode { NeighborhoodMode::Standard(node_addr, _, _) diff --git a/node/src/daemon/launch_verifier.rs b/node/src/daemon/launch_verifier.rs index f0a4125c9..4960eab11 100644 --- a/node/src/daemon/launch_verifier.rs +++ b/node/src/daemon/launch_verifier.rs @@ -15,7 +15,7 @@ use websocket::client::ParseError; use websocket::sync::Client; use websocket::{ClientBuilder, OwnedMessage, WebSocketResult}; -// Note: if the INTERVALs are half the DELAYs or greater, the tests below will need to change, +// Note: if the INTERVALs are half the DELAYs or greater, the non_unit_tests below will need to change, // because they depend on being able to fail twice and still succeed. const DELAY_FOR_RESPONSE_MS: u64 = 10000; const RESPONSE_CHECK_INTERVAL_MS: u64 = 250; diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index 2fad52508..dc9308256 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -703,7 +703,7 @@ pub mod test_utils { intentionally_blank!() /*all existing test calls only initialize() in the mocked version, but we need to call initialize_to_version() for the real object - in order to carry out some important tests too*/ + in order to carry out some important non_unit_tests too*/ } } diff --git a/node/src/db_config/db_encryption_layer.rs b/node/src/db_config/db_encryption_layer.rs index 9c8ff4caf..714384afa 100644 --- a/node/src/db_config/db_encryption_layer.rs +++ b/node/src/db_config/db_encryption_layer.rs @@ -81,5 +81,5 @@ impl DbEncryptionLayer { Bip39::encrypt_bytes(&decrypted_value, new_password).expect("Encryption failed") } - // These methods were extracted from SecureConfigLayer and are covered by the tests there. + // These methods were extracted from SecureConfigLayer and are covered by the non_unit_tests there. } diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 2548a7593..24881d3b1 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -48,7 +48,7 @@ trait NamedType { fn type_name(&self) -> &'static str; } -trait GossipHandler: NamedType + Send /* Send because lazily-written tests require it */ { +trait GossipHandler: NamedType + Send /* Send because lazily-written non_unit_tests require it */ { fn qualifies( &self, database: &NeighborhoodDatabase, @@ -1221,7 +1221,7 @@ impl RejectHandler { } } -pub trait GossipAcceptor: Send /* Send because lazily-written tests require it */ { +pub trait GossipAcceptor: Send /* Send because lazily-written non_unit_tests require it */ { fn handle( &self, database: &mut NeighborhoodDatabase, @@ -1358,7 +1358,7 @@ mod tests { Standard, OriginateOnly, // GossipAcceptor doesn't care about ConsumeOnly; that's routing, not Gossip - // ZeroHop is decentralized and should never appear in GossipAcceptor tests + // ZeroHop is decentralized and should never appear in GossipAcceptor non_unit_tests } fn make_default_neighborhood_metadata() -> NeighborhoodMetadata { diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 4a0ba99ba..389aed942 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -5470,7 +5470,7 @@ mod tests { } /* - For the next two tests, the database looks like this: + For the next two non_unit_tests, the database looks like this: +---A---+ | | diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 8bb201d43..3cee16b97 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -59,7 +59,7 @@ impl Default for PaymentThresholds { } } -//this code is used in tests in Accountant +//this code is used in non_unit_tests in Accountant impl PaymentThresholds { pub fn maturity_and_grace(&self, now: i64) -> i64 { now - checked_conversion::(self.maturity_threshold_sec) diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 64c30c2fe..20cec8081 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -175,7 +175,7 @@ impl Default for NodeDescriptor { } } -//the public key's role as a separate arg is to enable the produced descriptor to be constant and reliable in tests +//the public key's role as a separate arg is to enable the produced descriptor to be constant and reliable in non_unit_tests impl From<(&PublicKey, &NodeAddr, Chain, &dyn CryptDE)> for NodeDescriptor { fn from(tuple: (&PublicKey, &NodeAddr, Chain, &dyn CryptDE)) -> Self { let (public_key, node_addr, blockchain, cryptde) = tuple; diff --git a/node/src/sub_lib/ttl_hashmap.rs b/node/src/sub_lib/ttl_hashmap.rs index faa79b68a..9420815f7 100644 --- a/node/src/sub_lib/ttl_hashmap.rs +++ b/node/src/sub_lib/ttl_hashmap.rs @@ -141,7 +141,7 @@ mod tests { #[test] fn ttl_hashmap_get_preserves_otherwise_expired_entry() { - // Note: You may think that these delays are far too long for unit tests, and that you can + // Note: You may think that these delays are far too long for unit non_unit_tests, and that you can // reduce them proportionally and get faster test execution. Before you do, though, be sure the // reduced test runs reliably on the Mac. We were seeing granularities of 200ms (that is, if you // order a 5ms sleep, you get 200ms) in early March 2019. diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index a7fe4ee0d..2db0a08dd 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -1054,7 +1054,7 @@ pub mod unshared_test_utils { // In some implementations of mocks that have methods demanding args, the best we can do in order to // capture and examine these args in assertions is to receive the ArbitraryIdStamp of the given // argument. - // If such strategy is once decided for, transfers of this id will have to happen in all the tests + // If such strategy is once decided for, transfers of this id will have to happen in all the non_unit_tests // relying on this mock, while also calling the intended method. So even in cases where we certainly // are not really interested in checking that id, if we ignored that, the call of this method would // blow up because the field that stores it is likely optional, with the value defaulted to None. @@ -1062,7 +1062,7 @@ pub mod unshared_test_utils { // As prevention of confusion from putting a requirement on devs to set the id stamp even though // they're not planning to use it, we have a null type of that stamp to be there at most cases. // As a result, we don't risk a direct punishment (for the None value being the problem) but also - // we'll set the assertion on fire if it doesn't match the expected id in tests where we suddenly + // we'll set the assertion on fire if it doesn't match the expected id in non_unit_tests where we suddenly // do care None => ArbitraryIdStamp::null(), } @@ -1122,7 +1122,7 @@ pub mod unshared_test_utils { // Objects of that trait have some native field about them that can be set to // different values so that we can distinguish different instances in an assertion. - // There are no tests involving objects of that trait where instances are passed + // There are no non_unit_tests involving objects of that trait where instances are passed // as parameters to a mock and need to be asserted on as part of a ..._params_arc // collection. diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index 25dfeeadb..bb0d779df 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -208,7 +208,7 @@ impl PartialEq for NodeRecord { } impl NeighborhoodDatabase { - // These methods are intended for use only in tests. Do not use them in production code. + // These methods are intended for use only in non_unit_tests. Do not use them in production code. pub fn add_arbitrary_half_neighbor( &mut self, node_key: &PublicKey, diff --git a/node/tests/node_exits_from_crash_message_test.rs b/node/tests/node_exits_from_crash_message_test.rs index 8f0c4b1b6..15bcaffaa 100644 --- a/node/tests/node_exits_from_crash_message_test.rs +++ b/node/tests/node_exits_from_crash_message_test.rs @@ -14,7 +14,7 @@ use websocket::{ClientBuilder, OwnedMessage}; //blockchain_bridge, //dispatcher, //configurator, -//don't add more tests unless you know what you're doing +//don't add more non_unit_tests unless you know what you're doing #[test] fn node_exits_from_blockchain_bridge_panic_integration() { From 874cd2e43dcd15ac082cbec235bcc005f9c1c691 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 14 Apr 2024 15:20:42 +0200 Subject: [PATCH 163/250] GH-711-b: all tests fixed for this last solid body of code --- .../logging_and_diagnostics/diagnostics.rs | 6 +- .../logging_and_diagnostics/log_functions.rs | 9 +- .../miscellaneous/data_structures.rs | 5 + .../miscellaneous/helper_functions.rs | 41 ++- node/src/accountant/payment_adjuster/mod.rs | 318 ++++-------------- .../payment_adjuster/preparatory_analyser.rs | 18 +- .../payment_adjuster/service_fee_adjuster.rs | 108 +----- 7 files changed, 131 insertions(+), 374 deletions(-) diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 9ea1ef441..df8d8867f 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -216,9 +216,9 @@ pub mod ordinary_diagnostic_functions { ) { diagnostics!( "PICKED DISQUALIFIED ACCOUNT", - "From {:?} picked {}", - disqualification_suspected_accounts, - wallet + "Picked {} from nominated accounts {:?}", + wallet, + disqualification_suspected_accounts ); } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index bf687f83d..12e4ba1c7 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -17,8 +17,8 @@ const REFILL_RECOMMENDATION: &str = "\ Please be aware that abandoning your debts is going to result in delinquency bans. In order to \ consume services without limitations, you will need to place more funds into your consuming wallet."; const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ -Passed successfully adjustment by transaction fee, but by a second look, noticing of critical \ -shortage of MASQ balance. Operation will abort."; +Passed successfully adjustment by transaction fee, however, that was not enough regarding the other \ +fee. Now, a critical shortage of MASQ balance has been noticed. Operation will abort."; const BLANK_SPACE: &str = ""; @@ -193,8 +193,9 @@ mod tests { ); assert_eq!( LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, - "Passed successfully adjustment by transaction fee, but by a second look, noticing of \ - critical shortage of MASQ balance. Operation will abort." + "Passed successfully adjustment by transaction fee, however, that was not enough \ + regarding the other fee. Now, a critical shortage of MASQ balance has been noticed. \ + Operation will abort." ) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 1b26ffa9a..510e4864a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -102,6 +102,11 @@ impl TransactionCountsWithin16bits { } } +pub struct AccountsEliminatedByTxFeeInfo { + pub count: usize, + pub sum_of_balances: u128, +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 1f9287e2d..e7887d0a8 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -5,9 +5,7 @@ use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, -}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AccountsEliminatedByTxFeeInfo, AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable}; use crate::accountant::QualifiedPayableAccount; use itertools::{Either, Itertools}; @@ -37,17 +35,36 @@ pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { pub fn dump_unaffordable_accounts_by_txn_fee( weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, -) -> Vec { +) -> (Vec, AccountsEliminatedByTxFeeInfo) { + let to_be_dumped = weighted_accounts_in_descending_order + .iter() + .skip(affordable_transaction_count as usize) + .collect::>(); + let elimination_info = { + let count = to_be_dumped.len(); + let sum_of_balances = sum_as(&to_be_dumped, |account| { + account.qualified_account.bare_account.balance_wei + }); + AccountsEliminatedByTxFeeInfo { + count, + sum_of_balances, + } + }; + diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", - "keeping {} out of {} accounts", + "Keeping {} out of {} accounts. Dumping these accounts: {:?}", affordable_transaction_count, - weighted_accounts_in_descending_order.len() + weighted_accounts_in_descending_order.len(), + to_be_dumped ); - weighted_accounts_in_descending_order + + let kept_accounts = weighted_accounts_in_descending_order .into_iter() .take(affordable_transaction_count as usize) - .collect() + .collect(); + + (kept_accounts, elimination_info) } pub fn compute_mul_coefficient_preventing_fractional_numbers( @@ -76,7 +93,7 @@ fn find_largest_u128(slice: &[u128]) -> u128 { .fold(0, |largest_so_far, num| largest_so_far.max(*num)) } -pub fn exhaust_cw_till_the_last_drop( +pub fn exhaust_cw_balance_entirely( approved_accounts: Vec, original_cw_service_fee_balance_minor: u128, ) -> Vec { @@ -202,7 +219,7 @@ mod tests { AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_till_the_last_drop, + compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_balance_entirely, find_largest_exceeding_balance, find_largest_u128, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; @@ -376,7 +393,7 @@ mod tests { ]; let result = - exhaust_cw_till_the_last_drop(non_finalized_adjusted_accounts, original_cw_balance); + exhaust_cw_balance_entirely(non_finalized_adjusted_accounts, original_cw_balance); let expected_resulted_balances = vec![ (wallet_1, original_requested_balance_1), @@ -421,7 +438,7 @@ mod tests { ]; let result = - exhaust_cw_till_the_last_drop(non_finalized_adjusted_accounts, original_cw_balance); + exhaust_cw_balance_entirely(non_finalized_adjusted_accounts, original_cw_balance); let expected_resulted_balances = vec![ (wallet_1, original_requested_balance_1), diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c901fdda5..1b7f43afe 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -40,7 +40,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, - exhaust_cw_till_the_last_drop, find_largest_exceeding_balance, + exhaust_cw_balance_entirely, find_largest_exceeding_balance, sort_in_descendant_order_by_weights, sum_as, zero_affordable_accounts_found, }; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; @@ -113,6 +113,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { let service_fee_balance_minor = agent.service_fee_balance_minor(); match self.analyzer.check_need_of_adjustment_by_service_fee( &self.disqualification_arbiter, + None, Either::Left(qualified_payables), service_fee_balance_minor, &self.logger, @@ -136,11 +137,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { let largest_exceeding_balance_recently_qualified = find_largest_exceeding_balance(&qualified_payables); - eprintln!( - "INITIAL SV CW FEE BAL {}", - initial_service_fee_balance_minor - ); - self.initialize_inner( initial_service_fee_balance_minor, required_adjustment, @@ -224,7 +220,7 @@ impl PaymentAdjusterReal { Either::Left(non_exhausted_accounts) => { let original_cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); - let exhaustive_affordable_accounts = exhaust_cw_till_the_last_drop( + let exhaustive_affordable_accounts = exhaust_cw_balance_entirely( non_exhausted_accounts, original_cw_service_fee_balance_minor, ); @@ -258,16 +254,18 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_txn_fee( - weighted_accounts_in_descending_order, - already_known_affordable_transaction_count, - ); + let (weighted_accounts_affordable_by_transaction_fee, elimination_info) = + dump_unaffordable_accounts_by_txn_fee( + weighted_accounts_in_descending_order, + already_known_affordable_transaction_count, + ); let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); let is_service_fee_adjustment_needed = match self.analyzer.check_need_of_adjustment_by_service_fee( &self.disqualification_arbiter, + Some(elimination_info), Either::Right(&weighted_accounts_affordable_by_transaction_fee), cw_service_fee_balance, &self.logger, @@ -979,7 +977,7 @@ mod tests { ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 2300000000000000 + 2799999999999999 ); assert!(result.is_empty()); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); @@ -1083,6 +1081,7 @@ mod tests { let disqualification_arbiter = &subject.disqualification_arbiter; let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( disqualification_arbiter, + None, Either::Left(&qualified_payables), service_fee_balance_in_minor_units, &subject.logger, @@ -1531,7 +1530,12 @@ mod tests { let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); let wallet_3 = account_3.bare_account.wallet.clone(); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(900_000_000)) + .calculate_result(multiple_by_billion(1_100_000_000)) + .calculate_result(multiple_by_billion(600_000_000)); let mut subject = PaymentAdjusterReal::new(); + subject.calculators = vec![Box::new(calculator_mock)]; subject.logger = Logger::new(test_name); let service_fee_balance_in_minor_units = balance_1 + balance_2 - 55; let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1569,200 +1573,45 @@ mod tests { TestLogHandler::new().exists_log_containing(&format!( "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ 0x0000000000000000000000000000000000676869, ruled out from this round of payments. \ - The proposed adjustment 159,743,040,685,224,815 wei was below the disqualification \ + The proposed adjustment 149,199,999,999,999,977 wei was below the disqualification \ limit 300,000,000,000,000,000 wei" )); } - //TODO Should I keep the next test? It works but it might be unnecessary now... - // - // struct CompetitiveAccountsTestInputs { - // common: WalletsSetup, - // account_1_balance_positive_correction_minor: u128, - // account_2_balance_positive_correction_minor: u128, - // } - // - // #[derive(Clone)] - // struct WalletsSetup { - // wallet_1: Wallet, - // wallet_2: Wallet, - // } - // - // fn test_two_competitive_accounts_with_one_disqualified<'a>( - // test_scenario_name: &str, - // inputs: CompetitiveAccountsTestInputs, - // expected_wallet_of_the_winning_account: &'a Wallet, - // ) { - // let now = SystemTime::now(); - // let standard_balance_per_account = multiple_by_billion(2_000_000); - // let garbage_timestamp = now.checked_sub(Duration::from_secs(120000)).unwrap(); - // // Ideally, cancel out the impact of those other parameters than purely balances. - // let account_1 = PayableAccount { - // wallet: inputs.common.wallet_1.clone(), - // balance_wei: standard_balance_per_account - // + inputs.account_1_balance_positive_correction_minor, - // last_paid_timestamp: garbage_timestamp, - // pending_payable_opt: None, - // }; - // let mut account_2 = account_1.clone(); - // account_2.wallet = inputs.common.wallet_2.clone(); - // account_2.balance_wei = standard_balance_per_account + inputs.account_2_balance_positive_correction_minor; - // let payables = vec![account_1, account_2]; - // let qualified_payables = - // make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); - // let disqualification_limit = DisqualificationArbiter::default().calculate_disqualification_edge(&qualified_payables[0]); - // let cw_service_fee_balance_minor = 2 * disqualification_limit - 1; - // let mut subject = PaymentAdjusterReal::new(); - // let agent = BlockchainAgentMock::default() - // .service_fee_balance_minor_result(cw_service_fee_balance_minor); - // let adjustment_setup = PreparedAdjustment { - // qualified_payables, - // agent: Box::new(agent), - // adjustment: Adjustment::ByServiceFee, - // response_skeleton_opt: None, - // }; - // - // let mut result = subject - // .adjust_payments(adjustment_setup, now) - // .unwrap() - // .affordable_accounts; - // - // let winning_account = result.remove(0); - // assert_eq!( - // &winning_account.wallet, expected_wallet_of_the_winning_account, - // "{}: expected wallet {} but got {}", - // test_scenario_name, winning_account.wallet, expected_wallet_of_the_winning_account - // ); - // assert_eq!( - // winning_account.balance_wei, standard_balance_per_account, - // "{}: expected full cw balance {}, but the account had {}", - // test_scenario_name, standard_balance_per_account, winning_account.balance_wei - // ); - // assert!( - // result.is_empty(), - // "{}: is not empty, {:?} remains", - // test_scenario_name, - // result - // ) - // } - // - // #[test] - // fn granularity_test_one_unit_missing_in_service_fee_balance_for_two_accounts_means_only_one_wins() { - // fn prepare_scenario_name(description: &str) -> String { - // format!( - // "testing_granularity_on_one_unit_missing_in_service_fee_for_two_accounts_means_\ - // only_one_wins: {}", - // description - // ) - // } - // - // let w1 = make_wallet("abc"); - // let w2 = make_wallet("def"); - // let common_input = WalletsSetup { - // wallet_1: w1.clone(), - // wallet_2: w2.clone(), - // }; - // // scenario A - // let first_scenario_name = prepare_scenario_name("when equally significant"); - // let expected_wallet_of_the_winning_account = &w2; - // - // test_two_competitive_accounts_with_one_disqualified( - // &first_scenario_name, - // CompetitiveAccountsTestInputs { - // common: common_input.clone(), - // account_1_balance_positive_correction_minor: 0, - // account_2_balance_positive_correction_minor: 0, - // }, - // expected_wallet_of_the_winning_account, - // ); - // //-------------------------------------------------------------------- - // // scenario B - // let second_scenario_name = - // prepare_scenario_name("first more significant by balance"); - // let expected_wallet_of_the_winning_account = &w2; - // - // test_two_competitive_accounts_with_one_disqualified( - // &second_scenario_name, - // CompetitiveAccountsTestInputs { - // common: common_input.clone(), - // account_1_balance_positive_correction_minor: 1, - // account_2_balance_positive_correction_minor: 0, - // }, - // expected_wallet_of_the_winning_account, - // ); - // //-------------------------------------------------------------------- - // // scenario C - // let second_scenario_name = - // prepare_scenario_name("second more significant by balance"); - // let expected_wallet_of_the_winning_account = &w1; - // - // test_two_competitive_accounts_with_one_disqualified( - // &second_scenario_name, - // CompetitiveAccountsTestInputs { - // common: common_input, - // account_1_balance_positive_correction_minor: 0, - // account_2_balance_positive_correction_minor: 1, - // }, - // expected_wallet_of_the_winning_account, - // ); - // } - #[test] fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { init_test_logging(); let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; let now = SystemTime::now(); + let balance_1 = multiple_by_billion(100_000_000_000); + let account_1 = + make_plucked_qualified_account("abc", balance_1, 60_000_000_000, 10_000_000_000); + // Thrown away as the first one due to shortage of transaction fee, + // as it is the least significant by criteria at the moment + let balance_2 = multiple_by_billion(500_000_000_000); + let account_2 = + make_plucked_qualified_account("def", balance_2, 100_000_000_000, 30_000_000_000); // Thrown away as the second one due to shortage of service fee, // for the proposed adjusted balance insignificance (the third account withdraws // most of the available balance from the consuming wallet for itself) - let account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 10_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - // Thrown away as the first one due to shortage of transaction fee, - // as it is the least significant by criteria at the moment - let account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("def"), - balance_wei: 55_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(1000)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let wallet_3 = make_wallet("ghi"); - let last_paid_timestamp_3 = now.checked_sub(Duration::from_secs(29000)).unwrap(); - let account_3 = QualifiedPayableAccount::new( - PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let qualified_payables = vec![account_1, account_2, account_3.clone()]; + let balance_3 = multiple_by_billion(250_000_000_000); + let account_3 = + make_plucked_qualified_account("ghi", balance_3, 90_000_000_000, 20_000_000_000); + let qualified_payables = vec![account_1.clone(), account_2, account_3]; + let calculator_mock = CriterionCalculatorMock::default() + .calculate_result(multiple_by_billion(900_000_000_000)) + .calculate_result(multiple_by_billion(500_000_000_000)) + .calculate_result(multiple_by_billion(750_000_000_000)); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let service_fee_balance_in_minor = 300_000_000_000_000_u128; + let service_fee_balance_in_minor = balance_1 - multiple_by_billion(10_000_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor); - Box::new(mock) - }; + let agent = BlockchainAgentMock::default() + .set_arbitrary_id_stamp(agent_id_stamp) + .service_fee_balance_minor_result(service_fee_balance_in_minor); let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1771,15 +1620,12 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - assert_eq!( - result.affordable_accounts, - vec![PayableAccount { - wallet: wallet_3, - balance_wei: service_fee_balance_in_minor, - last_paid_timestamp: last_paid_timestamp_3, - pending_payable_opt: None, - }] - ); + let expected_accounts = { + let mut account = account_1; + account.bare_account.balance_wei = service_fee_balance_in_minor; + vec![account.bare_account] + }; + assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); let log_msg = format!( @@ -1789,13 +1635,13 @@ mod tests { | Original | Adjusted | -|0x0000000000000000000000000000000000676869 333,000,000,000,000 -| 300,000,000,000,000 +|0x0000000000000000000000000000000000616263 100,000,000,000,000,000,000 +| 90,000,000,000,000,000,000 | |Ruled Out Accounts Original | -|0x0000000000000000000000000000000000616263 10,000,000,000,000 -|0x0000000000000000000000000000000000646566 55,000,000,000" +|0x0000000000000000000000000000000000646566 500,000,000,000,000,000,000 +|0x0000000000000000000000000000000000676869 250,000,000,000,000,000,000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); } @@ -1806,50 +1652,29 @@ mod tests { init_test_logging(); let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); + let balance_1 = multiple_by_billion(500_000_000_000); + let account_1 = + make_plucked_qualified_account("abc", balance_1, 300_000_000_000, 100_000_000_000); // This account is eliminated in the transaction fee cut - let account_1 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("abc"), - balance_wei: 111_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(3333)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_2 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("def"), - balance_wei: 333_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(4444)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let account_3 = QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet("ghi"), - balance_wei: 222_000_000_000_000, - last_paid_timestamp: now.checked_sub(Duration::from_secs(5555)).unwrap(), - pending_payable_opt: None, - }, - todo!(), - todo!(), - ); - let qualified_payables = vec![account_1, account_2, account_3]; + let balance_2 = multiple_by_billion(111_000_000_000); + let account_2 = + make_plucked_qualified_account("def", balance_2, 50_000_000_000, 10_000_000_000); + let balance_3 = multiple_by_billion(300_000_000_000); + let account_3 = + make_plucked_qualified_account("ghi", balance_3, 150_000_000_000, 50_000_000_000); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); + let disqualification_arbiter = DisqualificationArbiter::default(); + let disqualification_limit_2 = + disqualification_arbiter.calculate_disqualification_edge(&account_2); // This is exactly the amount which will provoke an error - let service_fee_balance_in_minor_units = (111_000_000_000_000 / 2) - 1; - let agent = { - let mock = BlockchainAgentMock::default() - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); - Box::new(mock) - }; + let service_fee_balance_in_minor_units = disqualification_limit_2 - 1; + let qualified_payables = vec![account_1, account_2, account_3]; + let agent = BlockchainAgentMock::default() + .service_fee_balance_minor_result(service_fee_balance_in_minor_units); let adjustment_setup = PreparedAdjustment { qualified_payables, - agent, + agent: Box::new(agent), adjustment: Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1865,15 +1690,16 @@ mod tests { assert_eq!( err, PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: 2, - total_amount_demanded_minor: 333_000_000_000_000 + 222_000_000_000_000, + number_of_accounts: 3, + total_amount_demanded_minor: balance_1 + balance_2 + balance_3, cw_service_fee_balance_minor: service_fee_balance_in_minor_units } ); - TestLogHandler::new() - .exists_log_containing(&format!( - "ERROR: {test_name}: Passed successfully adjustment by transaction fee but noticing \ - critical scarcity of MASQ balance. Operation will abort.")); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Passed successfully adjustment by transaction fee, however, that \ + was not enough regarding the other fee. Now, a critical shortage of MASQ balance has \ + been noticed. Operation will abort." + )); } struct TestConfigForServiceFeeBalances { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 047887aac..5b1e9d593 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -5,7 +5,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - TransactionCountsWithin16bits, WeightedPayable, + AccountsEliminatedByTxFeeInfo, TransactionCountsWithin16bits, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::PaymentAdjusterError; @@ -84,6 +84,7 @@ impl PreparatoryAnalyzer { pub fn check_need_of_adjustment_by_service_fee( &self, disqualification_arbiter: &DisqualificationArbiter, + accounts_eliminated_by_tx_fee_info_opt: Option, payables: Either<&[QualifiedPayableAccount], &[WeightedPayable]>, cw_service_fee_balance_minor: u128, logger: &Logger, @@ -98,6 +99,7 @@ impl PreparatoryAnalyzer { } else { self.analyse_smallest_adjustment_possibility( disqualification_arbiter, + accounts_eliminated_by_tx_fee_info_opt, &qualified_payables, cw_service_fee_balance_minor, )?; @@ -130,6 +132,7 @@ impl PreparatoryAnalyzer { fn analyse_smallest_adjustment_possibility( &self, disqualification_arbiter: &DisqualificationArbiter, + accounts_eliminated_by_tx_fee_info_opt: Option, qualified_payables: &[&QualifiedPayableAccount], cw_service_fee_balance_minor: u128, ) -> Result<(), PaymentAdjusterError> { @@ -141,9 +144,18 @@ impl PreparatoryAnalyzer { } else { let total_amount_demanded_minor = sum_as(qualified_payables, |qp| qp.bare_account.balance_wei); + let (number_of_accounts, total_amount_demanded_minor) = + if let Some(info) = accounts_eliminated_by_tx_fee_info_opt { + ( + qualified_payables.len() + info.count, + total_amount_demanded_minor + info.sum_of_balances, + ) + } else { + (qualified_payables.len(), total_amount_demanded_minor) + }; Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: qualified_payables.len(), + number_of_accounts, total_amount_demanded_minor, cw_service_fee_balance_minor, }, @@ -214,6 +226,7 @@ mod tests { let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, + None, &accounts_in_expected_format, cw_service_fee_balance, ); @@ -300,6 +313,7 @@ mod tests { let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, + None, &accounts_in_expected_format, cw_service_fee_balance, ); diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index e952bb7c3..1e2122dcd 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -43,14 +43,6 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { let non_finalized_adjusted_accounts = self .adjustment_computer .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - // let non_finalized_adjusted_accounts = match self.generate_adjustments( - // weighted_accounts, - // disqualification_arbiter, - // cw_service_fee_balance_minor, - // ){ - // Either::Left(multiple_accounts)=> multiple_accounts, - // Either::Right(single_account) => return single_account - // }; let still_unchecked_for_disqualified = match Self::handle_possibly_outweighed_accounts( disqualification_arbiter, @@ -113,32 +105,6 @@ impl ServiceFeeAdjusterReal { } } - //TODO delete me - // fn generate_adjustments( - // &self, - // weighted_accounts: Vec, - // disqualification_arbiter: &DisqualificationArbiter, - // cw_service_fee_balance_minor: u128, - // ) -> Either, AdjustmentIterationResult> { - // let remaining_count = weighted_accounts.len(); - // if remaining_count > 1 { - // Either::Left(self.adjustment_computer - // .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor)) - // } else if remaining_count == 1 { - // let last_account = { - // let mut weighted_accounts = weighted_accounts; - // weighted_accounts.remove(0) - // }; - // Either::Right(Self::handle_last_account( - // last_account, - // disqualification_arbiter, - // cw_service_fee_balance_minor, - // )) - // } else { - // unreachable!("Should end by processing one or multiple accounts") - // } - // } - // The term "outweighed account" comes from a phenomenon with account weight increasing // significantly based on a different parameter than the debt size. Untreated, we would // grant the account (much) more money than what the accountancy has actually recorded. @@ -253,28 +219,6 @@ impl ServiceFeeAdjusterReal { (outweighed_adjusted, properly_adjusted_accounts) } - - //TODO delete me - // fn handle_last_account( - // account: WeightedPayable, - // disqualification_arbiter: &DisqualificationArbiter, - // cw_service_fee_balance_minor: u128, - // ) -> AdjustmentIterationResult { - // let disqualification_limit = - // disqualification_arbiter.calculate_disqualification_edge(&account.qualified_account); - // let non_finalized_account = if disqualification_limit >= cw_service_fee_balance_minor { - // handle_last_account_diagnostics(&account, cw_service_fee_balance_minor, None); - // AdjustedAccountBeforeFinalization::new(account.qualified_account.bare_account, cw_service_fee_balance_minor) - // } else { - // handle_last_account_diagnostics( - // &account, - // cw_service_fee_balance_minor, - // Some(disqualification_limit), - // ); - // AdjustedAccountBeforeFinalization::new(account.qualified_account.bare_account, disqualification_limit) - // }; - // AdjustmentIterationResult::AllAccountsProcessed(vec![non_finalized_account]) - // } } #[derive(Default)] @@ -340,15 +284,12 @@ impl AdjustmentComputer { mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, - WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, DisqualificationGaugeMock, }; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use itertools::Either; #[test] fn adjust_account_balance_if_outweighed_limits_them_by_the_standard_disqualification_edge() { @@ -484,51 +425,4 @@ mod tests { ]; assert_eq!(result, expected_result) } - - //TODO delete us - // #[test] - // fn handle_last_account_works_for_remaining_cw_balance_compared_to_the_disqualification_limit() { - // let cw_service_fee_balance_minor = 123_000; - // let expected_proposed_balances = vec![ - // cw_service_fee_balance_minor - 1, - // cw_service_fee_balance_minor, - // cw_service_fee_balance_minor, - // ]; - // let qualified_account = make_non_guaranteed_qualified_payable(456); - // let weighted_account = WeightedPayable::new(qualified_account, 1111111); - // let disqualification_gauge = DisqualificationGaugeMock::default() - // .determine_limit_result(cw_service_fee_balance_minor - 1) - // .determine_limit_result(cw_service_fee_balance_minor) - // .determine_limit_result(cw_service_fee_balance_minor + 1); - // let disqualification_arbiter = - // DisqualificationArbiter::new(Box::new(disqualification_gauge)); - // let subject = ServiceFeeAdjusterReal::default(); - // expected_proposed_balances - // .iter() - // .for_each(|expected_proposed_balance| { - // let result = subject.generate_adjustments( - // vec![weighted_account.clone()], - // &disqualification_arbiter, - // cw_service_fee_balance_minor, - // ); - // - // assert_eq!( - // result, - // Either::Right(AdjustmentIterationResult::AllAccountsProcessed(vec![AdjustedAccountBeforeFinalization::new( - // weighted_account.qualified_account.bare_account.clone(), - // *expected_proposed_balance - // )])) - // ) - // }); - // } - // - // #[test] - // #[should_panic(expected = "internal error: entered unreachable code: Should end by processing \ - // one or multiple accounts")] - // fn generate_adjustments_with_no_account_remaining(){ - // let subject = ServiceFeeAdjusterReal::default(); - // let disqualification_arbiter = DisqualificationArbiter::default(); - // - // let _ = subject.generate_adjustments(vec![], &disqualification_arbiter, 123456789); - // } } From d03cceaee95a9acd585ddb5369ced3b40834adf6 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 14 Apr 2024 20:49:24 +0200 Subject: [PATCH 164/250] GH-711-b: continuing in achieving leaner design; before implemnting on-time dsq limit carried around --- .../disqualification_arbiter.rs | 17 +- .../miscellaneous/data_structures.rs | 15 +- node/src/accountant/payment_adjuster/mod.rs | 112 ++++------- .../payment_adjuster/service_fee_adjuster.rs | 187 +++++++++--------- 4 files changed, 151 insertions(+), 180 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 427d86011..0daceb601 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -27,11 +27,11 @@ impl DisqualificationArbiter { disqualification_gauge, } } - pub fn try_finding_an_account_to_disqualify_in_this_iteration( + pub fn find_an_account_to_disqualify_in_this_iteration( &self, unconfirmed_adjustments: &[UnconfirmedAdjustment], logger: &Logger, - ) -> Option { + ) -> Wallet { let disqualification_suspected_accounts = self.list_accounts_nominated_for_disqualification(unconfirmed_adjustments); @@ -54,12 +54,13 @@ impl DisqualificationArbiter { info_log_for_disqualified_account(logger, account_to_disqualify); - Some(wallet) + wallet } else { - None + todo!() } } + //TODO this should go away pub fn calculate_disqualification_edge( &self, qualified_payable: &QualifiedPayableAccount, @@ -489,16 +490,14 @@ mod tests { .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); let subject = DisqualificationArbiter::default(); - let result = subject.try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - &logger, - ); + let result = subject + .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); unconfirmed_adjustments.iter().for_each(|payable| { // Condition of disqualification at the horizontal threshold assert!(payable.proposed_adjusted_balance_minor < 120_000_000_000) }); - assert_eq!(result, Some(wallet_3)); + assert_eq!(result, wallet_3); } fn make_pairs_of_unconfirmed_adjustments_and_dsq_edges( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 510e4864a..c51c51e87 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -20,12 +20,9 @@ impl WeightedPayable { } #[derive(Debug, PartialEq, Eq)] -pub enum AdjustmentIterationResult { - AllAccountsProcessed(Vec), - IterationWithSpecialHandling { - case: SpecialHandling, - remaining_undecided_accounts: Vec, - }, +pub struct AdjustmentIterationResult { + pub decided_accounts: DecidedAccounts, + pub remaining_undecided_accounts: Vec, } pub struct RecursionResults { @@ -53,9 +50,9 @@ impl RecursionResults { } #[derive(Debug, PartialEq, Eq)] -pub enum SpecialHandling { - InsignificantAccountEliminated, - OutweighedAccounts(Vec), +pub enum DecidedAccounts { + LowGainingAccountEliminated, + SomeAccountsProcessed(Vec), } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 1b7f43afe..1afbbbbb7 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -31,8 +31,8 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ - InsignificantAccountEliminated, OutweighedAccounts, +use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ + LowGainingAccountEliminated, SomeAccountsProcessed, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, @@ -329,46 +329,32 @@ impl PaymentAdjusterReal { &mut self, adjustment_iteration_result: AdjustmentIterationResult, ) -> RecursionResults { - match adjustment_iteration_result { - AdjustmentIterationResult::AllAccountsProcessed(decided_accounts) => { - RecursionResults::new(decided_accounts, vec![]) + let remaining_undecided_accounts = adjustment_iteration_result.remaining_undecided_accounts; + let here_decided_accounts = match adjustment_iteration_result.decided_accounts { + LowGainingAccountEliminated => { + if remaining_undecided_accounts.is_empty() { + return RecursionResults::new(vec![], vec![]); + } + + vec![] } - AdjustmentIterationResult::IterationWithSpecialHandling { - case, - remaining_undecided_accounts, - } => { - let here_decided_accounts = match case { - InsignificantAccountEliminated => { - if remaining_undecided_accounts.is_empty() { - return RecursionResults::new(vec![], vec![]); - } - - vec![] - } - OutweighedAccounts(outweighed) => { - if remaining_undecided_accounts.is_empty() { - // The only known reason for this would be an account disqualification, - // after which the unallocated cw balance begins to suffice for the rest - // of those unresolved accounts. - // Because it is definitely possible, there is a check aimed at this - // in the AdjustmentRunner's adjust_accounts() - unreachable!("This shouldn't be possible due to a preceding check"); - } - - self.adjust_remaining_unallocated_cw_balance_down(&outweighed); - outweighed - } - }; - - let down_stream_decided_accounts = self - .calculate_criteria_and_propose_adjustments_recursively( - remaining_undecided_accounts, - ServiceFeeOnlyAdjustmentRunner {}, - ); + SomeAccountsProcessed(decided_accounts) => { + if remaining_undecided_accounts.is_empty() { + return RecursionResults::new(decided_accounts, vec![]); + } - RecursionResults::new(here_decided_accounts, down_stream_decided_accounts) + self.adjust_remaining_unallocated_cw_balance_down(&decided_accounts); + decided_accounts } - } + }; + + let down_stream_decided_accounts = self + .calculate_criteria_and_propose_adjustments_recursively( + remaining_undecided_accounts, + ServiceFeeOnlyAdjustmentRunner {}, + ); + + RecursionResults::new(here_decided_accounts, down_stream_decided_accounts) } fn calculate_weights_for_accounts( @@ -529,11 +515,11 @@ mod tests { use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ - InsignificantAccountEliminated, OutweighedAccounts, + use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ + LowGainingAccountEliminated, SomeAccountsProcessed, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, SpecialHandling, + AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DecidedAccounts, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ @@ -895,7 +881,8 @@ mod tests { } #[test] - fn tinier_but_larger_in_weight_account_is_prioritized_outweighed_up_to_its_original_balance() { + fn tinier_but_larger_in_weight_account_is_prioritized_and_gains_up_to_its_disqualification_limit( + ) { let cw_service_fee_balance_minor = multiple_by_billion(3_600_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); let mut account_1 = make_qualified_payable_by_wallet("abc"); @@ -1117,23 +1104,6 @@ mod tests { assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated) } - #[test] - #[should_panic( - expected = "internal error: entered unreachable code: This shouldn't be possible due to a preceding check" - )] - fn outweighed_accounts_with_no_remaining_accounts_is_not_possible() { - let mut subject = PaymentAdjusterReal::new(); - let iteration_result = AdjustmentIterationResult::IterationWithSpecialHandling { - case: OutweighedAccounts(vec![AdjustedAccountBeforeFinalization::new( - make_payable_account(123), - 123456, - )]), - remaining_undecided_accounts: vec![], - }; - - let _ = subject.resolve_current_iteration_result(iteration_result); - } - #[test] fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them() { @@ -1310,7 +1280,7 @@ mod tests { make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000); let balance_3 = multiple_by_billion(6_666_666_666); let qualified_account_3 = - make_plucked_qualified_account("ghi", balance_3, 3_000_000_000, 1_111_111_111); + make_plucked_qualified_account("ghi", balance_3, 2_000_000_000, 1_111_111_111); let qualified_payables = vec![ qualified_account_1.clone(), qualified_account_2.clone(), @@ -1338,9 +1308,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_adjusted_balance_1 = 4_444_444_444_000_000_001; + let expected_adjusted_balance_1 = 4_833_333_333_000_000_000; let expected_adjusted_balance_2 = 5_500_000_000_000_000_000; - let expected_adjusted_balance_3 = 6_166_666_665_999_999_999; + let expected_adjusted_balance_3 = 5_777_777_777_000_000_000; let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, @@ -1498,11 +1468,11 @@ mod tests { // Account 2, the least important one, was eliminated for a lack of transaction fee in the cw let expected_accounts = { let account_1_adjusted = PayableAccount { - balance_wei: 71_000_000_000_000_001, + balance_wei: 81_000_000_000_000_000, ..account_1.bare_account }; let account_3_adjusted = PayableAccount { - balance_wei: 166_999_999_999_999_999, + balance_wei: 157_000_000_000_000_000, ..account_3.bare_account }; vec![account_1_adjusted, account_3_adjusted] @@ -1573,7 +1543,7 @@ mod tests { TestLogHandler::new().exists_log_containing(&format!( "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ 0x0000000000000000000000000000000000676869, ruled out from this round of payments. \ - The proposed adjustment 149,199,999,999,999,977 wei was below the disqualification \ + The proposed adjustment 189,999,999,999,999,944 wei was below the disqualification \ limit 300,000,000,000,000,000 wei" )); } @@ -2034,9 +2004,11 @@ mod tests { .perform_adjustment_by_service_fee_params(&perform_adjustment_by_service_fee_params_arc) // This is just a sentinel for an actual result. // We care only for the params - .perform_adjustment_by_service_fee_result( - AdjustmentIterationResult::AllAccountsProcessed(vec![]), - ); + // TODO check this carefully...ugly + .perform_adjustment_by_service_fee_result(AdjustmentIterationResult { + decided_accounts: SomeAccountsProcessed(vec![]), + remaining_undecided_accounts: vec![], + }); subject.service_fee_adjuster = Box::new(service_fee_adjuster_mock); let result = subject.run_adjustment(qualified_payables.to_vec()); @@ -2095,7 +2067,7 @@ mod tests { }) .zip(defaulted_payment_adjuster.calculators.into_iter()) .for_each( - |((qualified_payments_and_expected_computed_weights), calculator)| { + |(qualified_payments_and_expected_computed_weights, calculator)| { let (qualified_payments, expected_computed_weights): (Vec<_>, Vec<_>) = qualified_payments_and_expected_computed_weights .into_iter() diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 1e2122dcd..f9b72573b 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,15 +1,15 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::SpecialHandling::{ - InsignificantAccountEliminated, OutweighedAccounts, +use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ + LowGainingAccountEliminated, SomeAccountsProcessed, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: -ordinary_diagnostic_functions::{minimal_acceptable_balance_assigned_diagnostics, handle_last_account_diagnostics, outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; +ordinary_diagnostic_functions::{minimal_acceptable_balance_assigned_diagnostics, outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; @@ -40,28 +40,19 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let non_finalized_adjusted_accounts = self + let unconfirmed_adjustments = self .adjustment_computer .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - let still_unchecked_for_disqualified = match Self::handle_possibly_outweighed_accounts( + match Self::handle_sufficiently_filled_accounts( disqualification_arbiter, - non_finalized_adjusted_accounts, - ) { - Either::Left(first_check_passing_accounts) => first_check_passing_accounts, - Either::Right(with_some_outweighed) => return with_some_outweighed, - }; - - let verified_accounts = match Self::consider_account_disqualification( - disqualification_arbiter, - still_unchecked_for_disqualified, - logger, + unconfirmed_adjustments, ) { - Either::Left(verified_accounts) => verified_accounts, - Either::Right(with_some_disqualified) => return with_some_disqualified, - }; - - AdjustmentIterationResult::AllAccountsProcessed(verified_accounts) + Either::Left(without_gainers) => { + Self::disqualify_single_account(disqualification_arbiter, without_gainers, logger) + } + Either::Right(with_gainers) => with_gainers, + } } } @@ -105,6 +96,7 @@ impl ServiceFeeAdjusterReal { } } + //TODO review this text // The term "outweighed account" comes from a phenomenon with account weight increasing // significantly based on a different parameter than the debt size. Untreated, we would // grant the account (much) more money than what the accountancy has actually recorded. @@ -124,64 +116,58 @@ impl ServiceFeeAdjusterReal { // eventually, there is still the ending operation where the already prepared accounts // are reconsidered to be give more bits from the fund of unallocated money, all down // to zero. - fn handle_possibly_outweighed_accounts( + fn handle_sufficiently_filled_accounts( disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let (outweighed, properly_adjusted_accounts) = Self::adjust_account_balance_if_outweighed( + let (sufficient_gainers, low_gainers) = Self::filter_and_process_sufficient_gainers( disqualification_arbiter, unconfirmed_adjustments, ); - if outweighed.is_empty() { - Either::Left(properly_adjusted_accounts) + if sufficient_gainers.is_empty() { + Either::Left(low_gainers) } else { let remaining_undecided_accounts: Vec = - convert_collection(properly_adjusted_accounts); - let pre_processed_outweighed: Vec = - convert_collection(outweighed); - Either::Right(AdjustmentIterationResult::IterationWithSpecialHandling { - case: OutweighedAccounts(pre_processed_outweighed), + convert_collection(low_gainers); + let pre_processed_decided_accounts: Vec = + convert_collection(sufficient_gainers); + Either::Right(AdjustmentIterationResult { + decided_accounts: SomeAccountsProcessed(pre_processed_decided_accounts), remaining_undecided_accounts, }) } } - fn consider_account_disqualification( + fn disqualify_single_account( disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, logger: &Logger, - ) -> Either, AdjustmentIterationResult> { - if let Some(disqualified_account_wallet) = disqualification_arbiter - .try_finding_an_account_to_disqualify_in_this_iteration( - &unconfirmed_adjustments, - logger, - ) - { - let remaining = unconfirmed_adjustments - .into_iter() - .filter(|account_info| { - account_info - .weighted_account - .qualified_account - .bare_account - .wallet - != disqualified_account_wallet - }) - .collect(); - - let remaining_reverted = convert_collection(remaining); - - Either::Right(AdjustmentIterationResult::IterationWithSpecialHandling { - case: InsignificantAccountEliminated, - remaining_undecided_accounts: remaining_reverted, + ) -> AdjustmentIterationResult { + let disqualified_account_wallet = disqualification_arbiter + .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, logger); + + let remaining = unconfirmed_adjustments + .into_iter() + .filter(|account_info| { + account_info + .weighted_account + .qualified_account + .bare_account + .wallet + != disqualified_account_wallet }) - } else { - Either::Left(convert_collection(unconfirmed_adjustments)) + .collect(); + + let remaining_reverted = convert_collection(remaining); + + AdjustmentIterationResult { + decided_accounts: LowGainingAccountEliminated, + remaining_undecided_accounts: remaining_reverted, } } - fn adjust_account_balance_if_outweighed( + fn filter_and_process_sufficient_gainers( disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, ) -> ( @@ -189,35 +175,45 @@ impl ServiceFeeAdjusterReal { Vec, ) { let init: (Vec, Vec) = (vec![], vec![]); - let (outweighed, properly_adjusted_accounts) = unconfirmed_adjustments.into_iter().fold( + let (sufficient_gainers, low_gainers) = unconfirmed_adjustments.into_iter().fold( init, - |(mut outweighed, mut properly_adjusted_accounts), current| { - if current.proposed_adjusted_balance_minor - > current - .weighted_account - .qualified_account - .bare_account - .balance_wei + |(mut sufficient_gainers, mut low_gainers), current| { + let disqualification_limit = disqualification_arbiter + .calculate_disqualification_edge(¤t.weighted_account.qualified_account); + if current.proposed_adjusted_balance_minor >= disqualification_limit + //TODO is the operator tested?? { outweighed_accounts_diagnostics(¤t); - outweighed.push(current) + let mut adjusted = current; + adjusted.proposed_adjusted_balance_minor = disqualification_limit; + sufficient_gainers.push(adjusted) } else { - properly_adjusted_accounts.push(current) + low_gainers.push(current) } - (outweighed, properly_adjusted_accounts) + (sufficient_gainers, low_gainers) }, ); - let outweighed_adjusted = if outweighed.is_empty() { + // let outweighed_adjusted = if outweighed.is_empty() { + // vec![] + // } else { + // Self::assign_accounts_their_minimal_acceptable_balance( + // outweighed, + // disqualification_arbiter, + // ) + // }; + + let outweighed_adjusted = if sufficient_gainers.is_empty() { vec![] } else { - Self::assign_accounts_their_minimal_acceptable_balance( - outweighed, - disqualification_arbiter, - ) + // Self::assign_accounts_their_minimal_acceptable_balance( + // outweighed, + // disqualification_arbiter, + // ) + convert_collection(sufficient_gainers) }; - - (outweighed_adjusted, properly_adjusted_accounts) + //TODO Maybe consider to return the two return types just right from the fold + (outweighed_adjusted, low_gainers) } } @@ -284,7 +280,7 @@ impl AdjustmentComputer { mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ @@ -292,42 +288,47 @@ mod tests { }; #[test] - fn adjust_account_balance_if_outweighed_limits_them_by_the_standard_disqualification_edge() { + fn filter_and_process_sufficient_gainers_limits_them_by_the_standard_disqualification_edge() { + let proposed_adjusted_balance_1 = multiple_by_billion(3_000_000_000); let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); account_1 .weighted_account .qualified_account .bare_account .balance_wei = multiple_by_billion(2_000_000_000); - account_1.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000) + 1; + account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; + let proposed_adjusted_balance_2 = multiple_by_billion(4_200_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 .weighted_account .qualified_account .bare_account .balance_wei = multiple_by_billion(5_000_000_000); - account_2.proposed_adjusted_balance_minor = multiple_by_billion(5_000_000_000) + 1; + account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; + let proposed_adjusted_balance_3 = multiple_by_billion(2_000_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 .weighted_account .qualified_account .bare_account .balance_wei = multiple_by_billion(3_000_000_000); - account_3.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); + account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; + let proposed_adjusted_balance_4 = multiple_by_billion(500_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 .weighted_account .qualified_account .bare_account .balance_wei = multiple_by_billion(1_500_000_000); - account_4.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); + account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; + let proposed_adjusted_balance_5 = multiple_by_billion(1_000_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 .weighted_account .qualified_account .bare_account .balance_wei = multiple_by_billion(2_000_000_000); - account_5.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000) - 1; + account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -336,34 +337,36 @@ mod tests { account_5.clone(), ]; let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(multiple_by_billion(1_700_000_000)) - .determine_limit_result(multiple_by_billion(4_000_000_000)) - .determine_limit_result(multiple_by_billion(1_250_555_555)); + .determine_limit_result(multiple_by_billion(1_800_000_000)) + .determine_limit_result(multiple_by_billion(4_200_000_000) - 1) + .determine_limit_result(multiple_by_billion(2_000_000_000) + 1) + .determine_limit_result(multiple_by_billion(500_000_000)) + .determine_limit_result(multiple_by_billion(1_000_000_000) + 1); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let (outweighed_accounts, properly_adjusted_accounts) = - ServiceFeeAdjusterReal::adjust_account_balance_if_outweighed( + let (sufficient_gainers, low_gainers) = + ServiceFeeAdjusterReal::filter_and_process_sufficient_gainers( &disqualification_arbiter, unconfirmed_accounts, ); - assert_eq!(properly_adjusted_accounts, vec![account_3, account_5]); + assert_eq!(low_gainers, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ AdjustedAccountBeforeFinalization { original_account: account_1.weighted_account.qualified_account.bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(1_700_000_000), + proposed_adjusted_balance_minor: multiple_by_billion(1_800_000_000), }, AdjustedAccountBeforeFinalization { original_account: account_2.weighted_account.qualified_account.bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(4_000_000_000), + proposed_adjusted_balance_minor: multiple_by_billion(4_200_000_000) - 1, }, AdjustedAccountBeforeFinalization { original_account: account_4.weighted_account.qualified_account.bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(1_250_555_555), + proposed_adjusted_balance_minor: multiple_by_billion(500_000_000), }, ]; - assert_eq!(outweighed_accounts, expected_adjusted_outweighed_accounts) + assert_eq!(sufficient_gainers, expected_adjusted_outweighed_accounts) } #[test] From 8d7812383cee27392c6dee9286b1bb505cf72f67 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 14 Apr 2024 21:47:45 +0200 Subject: [PATCH 165/250] GH-711-b: single computation of dsq limit in one iteration implemented --- .../disqualification_arbiter.rs | 134 +++++++----------- .../logging_and_diagnostics/log_functions.rs | 4 +- .../account_stages_conversions.rs | 3 +- .../miscellaneous/data_structures.rs | 8 +- node/src/accountant/payment_adjuster/mod.rs | 23 ++- .../payment_adjuster/service_fee_adjuster.rs | 62 ++++---- .../accountant/payment_adjuster/test_utils.rs | 8 +- 7 files changed, 118 insertions(+), 124 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 0daceb601..842a4a7b8 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -27,13 +27,28 @@ impl DisqualificationArbiter { disqualification_gauge, } } + + pub fn calculate_disqualification_edge( + &self, + qualified_payable: &QualifiedPayableAccount, + ) -> u128 { + let balance = qualified_payable.bare_account.balance_wei; + let intercept = qualified_payable.payment_threshold_intercept_minor; + let permanent_debt_allowed = qualified_payable + .creditor_thresholds + .permanent_debt_allowed_wei; + + self.disqualification_gauge + .determine_limit(balance, intercept, permanent_debt_allowed) + } + pub fn find_an_account_to_disqualify_in_this_iteration( &self, unconfirmed_adjustments: &[UnconfirmedAdjustment], logger: &Logger, ) -> Wallet { let disqualification_suspected_accounts = - self.list_accounts_nominated_for_disqualification(unconfirmed_adjustments); + Self::list_accounts_nominated_for_disqualification(unconfirmed_adjustments); if !disqualification_suspected_accounts.is_empty() { let account_to_disqualify = @@ -60,41 +75,24 @@ impl DisqualificationArbiter { } } - //TODO this should go away - pub fn calculate_disqualification_edge( - &self, - qualified_payable: &QualifiedPayableAccount, - ) -> u128 { - self.disqualification_gauge.determine_limit( - qualified_payable.bare_account.balance_wei, - qualified_payable.payment_threshold_intercept_minor, - qualified_payable - .creditor_thresholds - .permanent_debt_allowed_wei, - ) - } - - fn list_accounts_nominated_for_disqualification<'unconfirmed_adj>( - &self, - unconfirmed_adjustments: &'unconfirmed_adj [UnconfirmedAdjustment], - ) -> Vec> { + fn list_accounts_nominated_for_disqualification( + unconfirmed_adjustments: &[UnconfirmedAdjustment], + ) -> Vec { unconfirmed_adjustments .iter() .flat_map(|adjustment_info| { - let disqualification_edge = self.calculate_disqualification_edge( - &adjustment_info.weighted_account.qualified_account, - ); + let disqualification_limit = adjustment_info.disqualification_limit_minor; let proposed_adjusted_balance = adjustment_info.proposed_adjusted_balance_minor; - if proposed_adjusted_balance < disqualification_edge { + if proposed_adjusted_balance < disqualification_limit { account_nominated_for_disqualification_diagnostics( adjustment_info, proposed_adjusted_balance, - disqualification_edge, + disqualification_limit, ); let suspected_account: DisqualificationSuspectedAccount = - (adjustment_info.into(), disqualification_edge).into(); + adjustment_info.into(); Some(suspected_account) } else { @@ -126,20 +124,15 @@ impl DisqualificationArbiter { pub struct DisqualificationSuspectedAccount<'account> { pub wallet: &'account Wallet, pub weight: u128, - // For an Info log message + // The rest is for an INFO log pub proposed_adjusted_balance_minor: u128, - pub disqualification_edge: u128, + pub disqualification_limit_minor: u128, } -impl<'unconfirmed_accounts> From<(&'unconfirmed_accounts UnconfirmedAdjustment, u128)> +impl<'unconfirmed_accounts> From<(&'unconfirmed_accounts UnconfirmedAdjustment)> for DisqualificationSuspectedAccount<'unconfirmed_accounts> { - fn from( - (unconfirmed_account, disqualification_edge): ( - &'unconfirmed_accounts UnconfirmedAdjustment, - u128, - ), - ) -> Self { + fn from(unconfirmed_account: &'unconfirmed_accounts UnconfirmedAdjustment) -> Self { DisqualificationSuspectedAccount { wallet: &unconfirmed_account .weighted_account @@ -148,7 +141,7 @@ impl<'unconfirmed_accounts> From<(&'unconfirmed_accounts UnconfirmedAdjustment, .wallet, weight: unconfirmed_account.weighted_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, - disqualification_edge, + disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor, } } } @@ -388,26 +381,21 @@ mod tests { #[test] fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit() { - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(1_000_000_000) - .determine_limit_result(9_999_999_999); - let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(444); - account_1.proposed_adjusted_balance_minor = 1_000_000_000; - let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(777); - account_2.proposed_adjusted_balance_minor = 9_999_999_999; - let accounts = vec![account_1, account_2]; - let subject = DisqualificationArbiter::new(Box::new(disqualification_gauge)); - - let result = subject.list_accounts_nominated_for_disqualification(&accounts); + let mut account = make_non_guaranteed_unconfirmed_adjustment(444); + account.proposed_adjusted_balance_minor = 1_000_000_000; + account.disqualification_limit_minor = 1_000_000_000; + let accounts = vec![account]; + + let result = + DisqualificationArbiter::list_accounts_nominated_for_disqualification(&accounts); assert!(result.is_empty()) } #[test] fn find_account_with_smallest_weight_works_for_unequal_weights() { - let pairs = - make_pairs_of_unconfirmed_adjustments_and_dsq_edges(vec![1004, 1000, 1002, 1001]); - let dsq_suspected_accounts = make_dsq_suspected_accounts(&pairs); + let adjustments = make_unconfirmed_adjustments(vec![1004, 1000, 1002, 1001]); + let dsq_suspected_accounts = make_dsq_suspected_accounts(&adjustments); let result = DisqualificationArbiter::find_account_with_smallest_weight(&dsq_suspected_accounts); @@ -418,8 +406,8 @@ mod tests { #[test] fn find_account_with_smallest_weight_for_equal_weights_chooses_the_first_of_the_same_size() { - let pairs = make_pairs_of_unconfirmed_adjustments_and_dsq_edges(vec![1111, 1113, 1111]); - let dsq_suspected_accounts = make_dsq_suspected_accounts(&pairs); + let adjustments = make_unconfirmed_adjustments(vec![1111, 1113, 1111]); + let dsq_suspected_accounts = make_dsq_suspected_accounts(&adjustments); let result = DisqualificationArbiter::find_account_with_smallest_weight(&dsq_suspected_accounts); @@ -486,9 +474,13 @@ mod tests { None, ); let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); - let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); let subject = DisqualificationArbiter::default(); + let unconfirmed_adjustments = AdjustmentComputer::default() + .compute_unconfirmed_adjustments( + weights_and_accounts, + &subject, + cw_service_fee_balance_minor, + ); let result = subject .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); @@ -500,43 +492,23 @@ mod tests { assert_eq!(result, wallet_3); } - fn make_pairs_of_unconfirmed_adjustments_and_dsq_edges( - weights: Vec, - ) -> Vec<(UnconfirmedAdjustment, u128)> { + fn make_unconfirmed_adjustments(weights: Vec) -> Vec { weights .into_iter() .enumerate() - .map(|(index, weight)| { - let original_account = make_payable_account(index as u64); - let garbage_intercept = 2_000_000_000; - let garbage_permanent_debt_allowed_wei = 1_111_111_111; - let qualified_account = QualifiedPayableAccount { - bare_account: original_account, - payment_threshold_intercept_minor: garbage_intercept, - creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: garbage_permanent_debt_allowed_wei, - }, - }; - let garbage_proposed_balance = 1_000_000_000; - let garbage_dsq_edge = garbage_intercept - 1_500_000_000; - ( - UnconfirmedAdjustment::new( - WeightedPayable::new(qualified_account, weight), - garbage_proposed_balance, - ), - garbage_dsq_edge, - ) + .map(|(idx, weight)| { + let mut account = make_non_guaranteed_unconfirmed_adjustment(idx as u64); + account.weighted_account.weight = weight; + account }) .collect() } fn make_dsq_suspected_accounts( - accounts_and_dsq_edges: &[(UnconfirmedAdjustment, u128)], + accounts_and_dsq_edges: &[UnconfirmedAdjustment], ) -> Vec { - let with_referred_accounts: Vec<(&UnconfirmedAdjustment, u128)> = accounts_and_dsq_edges - .iter() - .map(|(account, dsq_edge)| (account, *dsq_edge)) - .collect(); + let with_referred_accounts: Vec<&UnconfirmedAdjustment> = + accounts_and_dsq_edges.iter().collect(); convert_collection(with_referred_accounts) } } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index 12e4ba1c7..7ffc391b5 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -131,7 +131,7 @@ pub fn info_log_for_disqualified_account( account .proposed_adjusted_balance_minor .separate_with_commas(), - account.disqualification_edge.separate_with_commas() + account.disqualification_limit_minor.separate_with_commas() ) } @@ -209,7 +209,7 @@ mod tests { wallet: &wallet, weight: 0, proposed_adjusted_balance_minor: 1_555_666_777, - disqualification_edge: 2_000_000_000, + disqualification_limit_minor: 2_000_000_000, }; info_log_for_disqualified_account(&logger, &disqualified_account); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 074292a62..d7d85ab61 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -104,7 +104,8 @@ mod tests { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; let weighted_account = prepare_weighted_account(original_payable_account.clone()); - let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); + let unconfirmed_adjustment = + UnconfirmedAdjustment::new(weighted_account, 111_222_333, 100_000_000); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index c51c51e87..e5d4ba7fb 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -74,13 +74,19 @@ impl AdjustedAccountBeforeFinalization { pub struct UnconfirmedAdjustment { pub weighted_account: WeightedPayable, pub proposed_adjusted_balance_minor: u128, + pub disqualification_limit_minor: u128, } impl UnconfirmedAdjustment { - pub fn new(weighted_account: WeightedPayable, proposed_adjusted_balance_minor: u128) -> Self { + pub fn new( + weighted_account: WeightedPayable, + proposed_adjusted_balance_minor: u128, + disqualification_limit_minor: u128, + ) -> Self { Self { weighted_account, proposed_adjusted_balance_minor, + disqualification_limit_minor, } } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 1afbbbbb7..7cadd5948 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -552,6 +552,7 @@ mod tests { use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use itertools::Either; use lazy_static::lazy_static; + use libc::RESOLVE_NO_XDEV; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use rand::rngs::mock; @@ -910,10 +911,8 @@ mod tests { None, ); let disqualification_gauge = DisqualificationGaugeMock::default() - // Requested for an outweighed account in order to give it the minimal possible balance - // that still will solve a supposed ban .determine_limit_result(disqualification_limit_2) - // Simple testing an unconfirmed account on disqualification + .determine_limit_result(disqualification_limit_1) .determine_limit_result(disqualification_limit_1) .determine_limit_params(&determine_limit_params_arc); subject.disqualification_arbiter = @@ -964,7 +963,7 @@ mod tests { ); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 2799999999999999 + 2_300_000_000_000_000 ); assert!(result.is_empty()); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); @@ -976,6 +975,11 @@ mod tests { threshold_intercept_minor_2, permanent_debt_allowed_minor_2 ), + ( + balance_1, + threshold_intercept_minor_1, + permanent_debt_allowed_minor_1 + ), ( balance_1, threshold_intercept_minor_1, @@ -1000,8 +1004,17 @@ mod tests { cw_service_fee_balance_minor, garbage_largest_exceeding_balance_recently_qualified, )); + let disqualification_gauge_mock = DisqualificationGaugeMock::default() + .determine_limit_result(0) + .determine_limit_result(0); + let garbage_disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge_mock)); let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + .compute_unconfirmed_adjustments( + weighted_accounts, + &garbage_disqualification_arbiter, + cw_service_fee_balance_minor, + ); // The results are sorted from the biggest weights down let proposed_adjusted_balance = unconfirmed_adjustments[0].proposed_adjusted_balance_minor; assert_eq!( diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index f9b72573b..217499e3b 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -40,14 +40,13 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let unconfirmed_adjustments = self - .adjustment_computer - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - - match Self::handle_sufficiently_filled_accounts( + let unconfirmed_adjustments = self.adjustment_computer.compute_unconfirmed_adjustments( + weighted_accounts, disqualification_arbiter, - unconfirmed_adjustments, - ) { + cw_service_fee_balance_minor, + ); + + match Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments) { Either::Left(without_gainers) => { Self::disqualify_single_account(disqualification_arbiter, without_gainers, logger) } @@ -83,7 +82,11 @@ impl ServiceFeeAdjusterReal { &weighted_account, disqualification_limit, ); - UnconfirmedAdjustment::new(weighted_account, disqualification_limit) + UnconfirmedAdjustment::new( + weighted_account, + disqualification_limit, + disqualification_limit, + ) }) .collect(); @@ -117,13 +120,10 @@ impl ServiceFeeAdjusterReal { // are reconsidered to be give more bits from the fund of unallocated money, all down // to zero. fn handle_sufficiently_filled_accounts( - disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let (sufficient_gainers, low_gainers) = Self::filter_and_process_sufficient_gainers( - disqualification_arbiter, - unconfirmed_adjustments, - ); + let (sufficient_gainers, low_gainers) = + Self::filter_and_process_sufficient_gainers(unconfirmed_adjustments); if sufficient_gainers.is_empty() { Either::Left(low_gainers) @@ -168,7 +168,6 @@ impl ServiceFeeAdjusterReal { } fn filter_and_process_sufficient_gainers( - disqualification_arbiter: &DisqualificationArbiter, unconfirmed_adjustments: Vec, ) -> ( Vec, @@ -178,8 +177,7 @@ impl ServiceFeeAdjusterReal { let (sufficient_gainers, low_gainers) = unconfirmed_adjustments.into_iter().fold( init, |(mut sufficient_gainers, mut low_gainers), current| { - let disqualification_limit = disqualification_arbiter - .calculate_disqualification_edge(¤t.weighted_account.qualified_account); + let disqualification_limit = current.disqualification_limit_minor; if current.proposed_adjusted_balance_minor >= disqualification_limit //TODO is the operator tested?? { @@ -224,6 +222,7 @@ impl AdjustmentComputer { pub fn compute_unconfirmed_adjustments( &self, weighted_accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { let weights_total = weights_total(&weighted_accounts); @@ -252,7 +251,14 @@ impl AdjustmentComputer { proposed_adjusted_balance, ); - UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) + let disqualification_limit = disqualification_arbiter + .calculate_disqualification_edge(&weighted_account.qualified_account); + + UnconfirmedAdjustment::new( + weighted_account, + proposed_adjusted_balance, + disqualification_limit, + ) }) .collect() } @@ -297,6 +303,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; + account_1.disqualification_limit_minor = multiple_by_billion(1_800_000_000); let proposed_adjusted_balance_2 = multiple_by_billion(4_200_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 @@ -305,6 +312,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(5_000_000_000); account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; + account_2.disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; let proposed_adjusted_balance_3 = multiple_by_billion(2_000_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 @@ -313,6 +321,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(3_000_000_000); account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; + account_3.disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; let proposed_adjusted_balance_4 = multiple_by_billion(500_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 @@ -321,6 +330,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(1_500_000_000); account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; + account_4.disqualification_limit_minor = multiple_by_billion(500_000_000); let proposed_adjusted_balance_5 = multiple_by_billion(1_000_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 @@ -329,6 +339,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; + account_5.disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -336,20 +347,9 @@ mod tests { account_4.clone(), account_5.clone(), ]; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(multiple_by_billion(1_800_000_000)) - .determine_limit_result(multiple_by_billion(4_200_000_000) - 1) - .determine_limit_result(multiple_by_billion(2_000_000_000) + 1) - .determine_limit_result(multiple_by_billion(500_000_000)) - .determine_limit_result(multiple_by_billion(1_000_000_000) + 1); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); let (sufficient_gainers, low_gainers) = - ServiceFeeAdjusterReal::filter_and_process_sufficient_gainers( - &disqualification_arbiter, - unconfirmed_accounts, - ); + ServiceFeeAdjusterReal::filter_and_process_sufficient_gainers(unconfirmed_accounts); assert_eq!(low_gainers, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ @@ -389,8 +389,8 @@ mod tests { ); let expected_result = vec![ - UnconfirmedAdjustment::new(weighted_account_1, 123456789), - UnconfirmedAdjustment::new(weighted_account_2, 987654321), + UnconfirmedAdjustment::new(weighted_account_1, 123456789, 123456789), + UnconfirmedAdjustment::new(weighted_account_2, 987654321, 987654321), ]; assert_eq!(result, expected_result) } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index bb8f89b01..50a09ff23 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -95,11 +95,13 @@ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustme let qualified_payable = make_non_guaranteed_qualified_payable(n); let proposed_adjusted_balance_minor = (qualified_payable.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; + let disqualification_limit = (3 * proposed_adjusted_balance_minor) / 4; let weight = (n as u128).pow(3); - UnconfirmedAdjustment { - weighted_account: WeightedPayable::new(qualified_payable, weight), + UnconfirmedAdjustment::new( + WeightedPayable::new(qualified_payable, weight), proposed_adjusted_balance_minor, - } + disqualification_limit, + ) } #[derive(Default)] From f06f0348df573314ec795f3283e9057157ede923 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 15 Apr 2024 23:20:27 +0200 Subject: [PATCH 166/250] GH-711-c: terifying refactoring behind me; compiling; some tests failing; done to see and taste if this is the way to leave it 'forever' --- node/src/accountant/mod.rs | 80 ++-- .../payment_adjuster/adjustment_runners.rs | 45 ++- .../balance_and_age_calculator.rs | 31 +- .../criterion_calculators/mod.rs | 2 +- .../disqualification_arbiter.rs | 21 +- .../logging_and_diagnostics/diagnostics.rs | 21 +- .../account_stages_conversions.rs | 30 +- .../miscellaneous/data_structures.rs | 30 +- .../miscellaneous/helper_functions.rs | 31 +- node/src/accountant/payment_adjuster/mod.rs | 374 ++++++++++-------- .../payment_adjuster/non_unit_tests/mod.rs | 46 ++- .../payment_adjuster/preparatory_analyser.rs | 186 ++++++--- .../payment_adjuster/service_fee_adjuster.rs | 61 +-- .../accountant/payment_adjuster/test_utils.rs | 28 +- .../payable_scanner/mod.rs | 16 +- node/src/accountant/scanners/mod.rs | 23 +- node/src/accountant/test_utils.rs | 42 +- 17 files changed, 661 insertions(+), 406 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index a99973c02..6253415ac 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -150,6 +150,21 @@ impl QualifiedPayableAccount { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct AnalyzedPayableAccount { + pub qualified_as: QualifiedPayableAccount, + pub disqualification_limit: u128, +} + +impl AnalyzedPayableAccount { + pub fn new(qualified_as: QualifiedPayableAccount, disqualification_limit: u128) -> Self { + AnalyzedPayableAccount { + qualified_as, + disqualification_limit, + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct CreditorThresholds { pub permanent_debt_allowed_wei: u128, @@ -1051,7 +1066,9 @@ mod tests { }; use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; - use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterError}; + use crate::accountant::payment_adjuster::{ + Adjustment, AdjustmentAnalysis, PaymentAdjusterError, + }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::scanners::BeginScanError; @@ -1059,12 +1076,12 @@ mod tests { ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_guaranteed_qualified_payables, - make_non_guaranteed_qualified_payable, make_payable_account, - make_unqualified_and_qualified_payables, BannedDaoFactoryMock, MessageIdGeneratorMock, - NullScanner, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, - PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, - ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, + bc_from_earning_wallet, bc_from_wallets, make_analyzed_account, + make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, + make_payable_account, make_unqualified_and_qualified_payables, BannedDaoFactoryMock, + MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, PayableDaoMock, + PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, + PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::Accountant; @@ -1110,6 +1127,7 @@ mod tests { use masq_lib::ui_gateway::{ MessageBody, MessagePath, MessageTarget, NodeFromUiMessage, NodeToUiMessage, }; + use masq_lib::utils::convert_collection; use std::any::TypeId; use std::ops::{Add, Sub}; use std::sync::Arc; @@ -1453,11 +1471,19 @@ mod tests { .start() .recipient(); let mut subject = AccountantBuilder::default().build(); + let account_1 = make_payable_account(44_444); + let account_2 = make_payable_account(333_333); + let qualified_payables = vec![ + QualifiedPayableAccount::new(account_1.clone(), 2345, CreditorThresholds::new(1111)), + QualifiedPayableAccount::new(account_2.clone(), 6789, CreditorThresholds::new(2222)), + ]; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_params( &search_for_indispensable_adjustment_params_arc, ) - .search_for_indispensable_adjustment_result(Ok(None)); + .search_for_indispensable_adjustment_result(Ok(Either::Left( + qualified_payables.clone(), + ))); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -1465,15 +1491,10 @@ mod tests { subject.outbound_payments_instructions_sub_opt = Some(instructions_recipient); subject.logger = Logger::new(test_name); let subject_addr = subject.start(); - let account_1 = make_payable_account(44_444); - let account_2 = make_payable_account(333_333); let system = System::new("test"); let expected_agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); - let qualified_payables = vec![ - QualifiedPayableAccount::new(account_1.clone(), 2345, CreditorThresholds::new(1111)), - QualifiedPayableAccount::new(account_2.clone(), 6789, CreditorThresholds::new(2222)), - ]; + let msg = BlockchainAgentWithContextMessage { protected_qualified_payables: protect_qualified_payables_in_test( qualified_payables.clone(), @@ -1559,10 +1580,10 @@ mod tests { let agent_id_stamp_first_phase = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(agent_id_stamp_first_phase); + let protected_qualified_payables = + protect_qualified_payables_in_test(unadjusted_qualified_accounts.clone()); let payable_payments_setup_msg = BlockchainAgentWithContextMessage { - protected_qualified_payables: protect_qualified_payables_in_test( - unadjusted_qualified_accounts.clone(), - ), + protected_qualified_payables, agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; @@ -1577,8 +1598,11 @@ mod tests { agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; + let analyzed_accounts = convert_collection(unadjusted_qualified_accounts.clone()); + let adjustment_analysis = + AdjustmentAnalysis::new(Adjustment::ByServiceFee, analyzed_accounts.clone()); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) + .search_for_indispensable_adjustment_result(Ok(Either::Right(adjustment_analysis))) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(Ok(payments_instructions)); let payable_scanner = PayableScannerBuilder::new() @@ -1598,12 +1622,12 @@ mod tests { let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); let (actual_prepared_adjustment, captured_now) = adjust_payments_params.remove(0); assert_eq!( - actual_prepared_adjustment.adjustment, + actual_prepared_adjustment.adjustment_analysis.adjustment, Adjustment::ByServiceFee ); assert_eq!( - actual_prepared_adjustment.qualified_payables, - unadjusted_qualified_accounts + actual_prepared_adjustment.adjustment_analysis.accounts, + analyzed_accounts ); assert_eq!( actual_prepared_adjustment.agent.arbitrary_id_stamp(), @@ -1736,7 +1760,10 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_meaning_entry_check_passed_but_adjustment_went_wrong"; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Some(Adjustment::ByServiceFee))) + .search_for_indispensable_adjustment_result(Ok(Either::Right(AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + vec![make_analyzed_account(123)], + )))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); test_handling_payment_adjuster_error(test_name, payment_adjuster); @@ -3437,6 +3464,11 @@ mod tests { last_paid_timestamp: past_payable_timestamp_2, pending_payable_opt: None, }; + let qualified_payables = make_guaranteed_qualified_payables( + vec![account_1.clone(), account_2.clone()], + &DEFAULT_PAYMENT_THRESHOLDS, + now, + ); let pending_payable_scan_interval = 200; // Should be slightly less than 1/5 of the time until shutting the system let payable_dao_for_payable_scanner = PayableDaoMock::new() .non_pending_payables_params(&non_pending_payables_params_arc) @@ -3539,7 +3571,9 @@ mod tests { .build(); subject.scanners.receivable = Box::new(NullScanner::new()); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(None)); + .search_for_indispensable_adjustment_result(Ok(Either::Left( + qualified_payables, + ))); let payable_scanner = PayableScannerBuilder::new() .payable_dao(payable_dao_for_payable_scanner) .pending_payable_dao(pending_payable_dao_for_payable_scanner) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index a386fe07d..1f8d2aac0 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -69,7 +69,11 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { weighted_accounts: Vec, ) -> Self::ReturnType { let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.qualified_account.bare_account.balance_wei + weighted_account + .analyzed_account + .qualified_as + .bare_account + .balance_wei }); let unallocated_cw_balance = payment_adjuster @@ -99,8 +103,10 @@ mod tests { }; use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; + use crate::accountant::test_utils::{ + make_analyzed_account, make_non_guaranteed_qualified_payable, + }; + use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -121,11 +127,12 @@ mod tests { } fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { - let mut payable = WeightedPayable { - qualified_account: make_non_guaranteed_qualified_payable(111), - weight: n as u128 * 1234, - }; - payable.qualified_account.bare_account.balance_wei = initial_balance_minor; + let mut payable = WeightedPayable::new(make_analyzed_account(111), n as u128 * 1234); + payable + .analyzed_account + .qualified_as + .bare_account + .balance_wei = initial_balance_minor; payable } @@ -145,8 +152,8 @@ mod tests { let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); - let initial_balance_minor_1 = payable_1.qualified_account.bare_account.balance_wei; - let initial_balance_minor_2 = payable_2.qualified_account.bare_account.balance_wei; + let initial_balance_minor_1 = payable_1.balance_minor(); + let initial_balance_minor_2 = payable_2.balance_minor(); let subject = ServiceFeeOnlyAdjustmentRunner {}; let result = subject.adjust_accounts( @@ -158,11 +165,11 @@ mod tests { result, vec![ AdjustedAccountBeforeFinalization { - original_account: payable_1.qualified_account.bare_account, + original_account: payable_1.analyzed_account.qualified_as.bare_account, proposed_adjusted_balance_minor: expected_proposed_balance_1 }, AdjustedAccountBeforeFinalization { - original_account: payable_2.qualified_account.bare_account, + original_account: payable_2.analyzed_account.qualified_as.bare_account, proposed_adjusted_balance_minor: expected_proposed_balance_2 } ] @@ -174,8 +181,12 @@ mod tests { initial_balance_minor: u128, ) -> WeightedPayable { let mut account = make_weighed_payable(n, initial_balance_minor); - account.qualified_account.payment_threshold_intercept_minor = 3_000_000_000; - account.qualified_account.creditor_thresholds = CreditorThresholds::new(1_000_000_000); + account + .analyzed_account + .qualified_as + .payment_threshold_intercept_minor = 3_000_000_000; + account.analyzed_account.qualified_as.creditor_thresholds = + CreditorThresholds::new(1_000_000_000); account } @@ -244,12 +255,12 @@ mod tests { ); let subject = ServiceFeeOnlyAdjustmentRunner {}; let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { - qualified_account: account, + analyzed_account: AnalyzedPayableAccount::new(account, 5_000_000_000), weight: 4_000_000_000, }; - let criteria_and_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; + let weighted_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; - let result = subject.adjust_accounts(&mut payment_adjuster, criteria_and_accounts); + let result = subject.adjust_accounts(&mut payment_adjuster, weighted_accounts); let returned_accounts = result .into_iter() diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 42389eddd..10f155ffc 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -35,7 +35,9 @@ mod tests { use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; + use crate::accountant::test_utils::{ + make_analyzed_account, make_non_guaranteed_qualified_payable, + }; use std::time::SystemTime; #[test] @@ -50,34 +52,39 @@ mod tests { #[test] fn balance_and_age_criterion_calculator_works() { let now = SystemTime::now(); - let qualified_accounts = [50, 100, 2_222] + let analyzed_accounts = [50, 100, 2_222] .into_iter() .enumerate() .map(|(idx, n)| { - let mut basic_q_payable = make_non_guaranteed_qualified_payable(idx as u64); - basic_q_payable.bare_account.balance_wei = multiple_by_billion(n); - basic_q_payable.payment_threshold_intercept_minor = - (multiple_by_billion(2) / 5) * 3; - basic_q_payable + let mut basic_analyzed_payable = make_analyzed_account(idx as u64); + basic_analyzed_payable.qualified_as.bare_account.balance_wei = + multiple_by_billion(n); + basic_analyzed_payable + .qualified_as + .payment_threshold_intercept_minor = (multiple_by_billion(2) / 5) * 3; + basic_analyzed_payable }) .collect::>(); - let largest_exceeding_balance = find_largest_exceeding_balance(&qualified_accounts); + let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); let payment_adjuster_inner = PaymentAdjusterInnerReal::new(now, None, 123456789, largest_exceeding_balance); let subject = BalanceAndAgeCriterionCalculator::default(); - let computed_criteria = qualified_accounts + let computed_criteria = analyzed_accounts .iter() - .map(|qualified_account| subject.calculate(qualified_account, &payment_adjuster_inner)) + .map(|analyzed_account| { + subject.calculate(&analyzed_account.qualified_as, &payment_adjuster_inner) + }) .collect::>(); - let zipped = qualified_accounts + let zipped = analyzed_accounts .into_iter() .zip(computed_criteria.into_iter()); zipped.into_iter().for_each(|(account, actual_criterion)| { let expected_criterion = { let exceeding_balance_on_this_account = - account.bare_account.balance_wei - account.payment_threshold_intercept_minor; + account.qualified_as.bare_account.balance_wei + - account.qualified_as.payment_threshold_intercept_minor; let diff = largest_exceeding_balance - exceeding_balance_on_this_account; largest_exceeding_balance + diff }; diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index df7a4765e..b028fe810 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -3,7 +3,7 @@ pub mod balance_and_age_calculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 842a4a7b8..3fce31000 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -129,16 +129,12 @@ pub struct DisqualificationSuspectedAccount<'account> { pub disqualification_limit_minor: u128, } -impl<'unconfirmed_accounts> From<(&'unconfirmed_accounts UnconfirmedAdjustment)> +impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> for DisqualificationSuspectedAccount<'unconfirmed_accounts> { fn from(unconfirmed_account: &'unconfirmed_accounts UnconfirmedAdjustment) -> Self { DisqualificationSuspectedAccount { - wallet: &unconfirmed_account - .weighted_account - .qualified_account - .bare_account - .wallet, + wallet: unconfirmed_account.wallet(), weight: unconfirmed_account.weighted_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor, @@ -215,17 +211,13 @@ mod tests { DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, DisqualificationSuspectedAccount, }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - UnconfirmedAdjustment, WeightedPayable, - }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, - DisqualificationGaugeMock, }; - use crate::accountant::test_utils::{make_guaranteed_qualified_payables, make_payable_account}; - use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; + use crate::accountant::test_utils::make_guaranteed_qualified_payables; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; @@ -465,7 +457,8 @@ mod tests { let accounts = vec![account_1, account_2, account_3, account_4]; let qualified_payables = make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); - let largest_exceeding_balance = find_largest_exceeding_balance(&qualified_payables); + let analyzed_accounts = convert_collection(qualified_payables); + let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); let subject = make_initialized_subject( Some(now), Some(cw_service_fee_balance_minor), @@ -473,7 +466,7 @@ mod tests { Some(largest_exceeding_balance), None, ); - let weights_and_accounts = subject.calculate_weights_for_accounts(qualified_payables); + let weights_and_accounts = subject.calculate_weights(analyzed_accounts); let subject = DisqualificationArbiter::default(); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments( diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index df8d8867f..897c30c0e 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -105,14 +105,16 @@ pub mod ordinary_diagnostic_functions { diagnostics!( &account_info .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .wallet, "OUTWEIGHED ACCOUNT FOUND", "Original balance: {}, proposed balance: {}", account_info .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei .separate_with_commas(), @@ -130,7 +132,8 @@ pub mod ordinary_diagnostic_functions { diagnostics!( account_info .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .wallet, "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", @@ -145,7 +148,11 @@ pub mod ordinary_diagnostic_functions { disqualification_limit: u128, ) { diagnostics!( - weighted_account.qualified_account.bare_account.wallet, + weighted_account + .analyzed_account + .qualified_as + .bare_account + .wallet, "MINIMAL ACCEPTABLE BALANCE ASSIGNED", "Used disqualification limit for given account {}", disqualification_limit.separate_with_commas() @@ -158,7 +165,7 @@ pub mod ordinary_diagnostic_functions { disqualification_limit_opt: Option, ) { diagnostics!( - account.qualified_account.bare_account.wallet, + account.analyzed_account.qualified_as.bare_account.wallet, "HANDLING LAST ACCOUNT", "Remaining CW balance {} is {}", cw_service_fee_balance_minor, @@ -199,11 +206,11 @@ pub mod ordinary_diagnostic_functions { } pub fn proposed_adjusted_balance_diagnostics( - account: &QualifiedPayableAccount, + account: &WeightedPayable, proposed_adjusted_balance: u128, ) { diagnostics!( - &account.bare_account.wallet, + account.wallet(), "PROPOSED ADJUSTED BALANCE", "{}", proposed_adjusted_balance.separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index d7d85ab61..97899be3a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -36,7 +36,8 @@ impl From for AdjustedAccountBeforeFinalization { unconfirmed_adjustment.proposed_adjusted_balance_minor; let original_account = unconfirmed_adjustment .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account; AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) @@ -48,9 +49,12 @@ impl From for AdjustedAccountBeforeFinalization { // they requested impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { - let proposed_adjusted_balance_minor = - weighted_account.qualified_account.bare_account.balance_wei; - let original_account = weighted_account.qualified_account.bare_account; + let proposed_adjusted_balance_minor = weighted_account + .analyzed_account + .qualified_as + .bare_account + .balance_wei; + let original_account = weighted_account.analyzed_account.qualified_as.bare_account; AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) } @@ -62,8 +66,10 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; - use crate::accountant::test_utils::make_payable_account; - use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; + use crate::accountant::test_utils::{ + make_non_guaranteed_qualified_payable, make_payable_account, + }; + use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; #[test] fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { @@ -79,12 +85,14 @@ mod tests { } fn prepare_weighted_account(payable_account: PayableAccount) -> WeightedPayable { - let qualified_account = QualifiedPayableAccount::new( - payable_account, - 444_555_666, - CreditorThresholds::new(111_111_111), + let garbage_disqualification_limit = 333_333_333; + let garbage_weight = 777_777_777; + let mut analyzed_account = AnalyzedPayableAccount::new( + make_non_guaranteed_qualified_payable(111), + garbage_disqualification_limit, ); - WeightedPayable::new(qualified_account, 777_777_777) + analyzed_account.qualified_as.bare_account = payable_account; + WeightedPayable::new(analyzed_account, garbage_weight) } #[test] fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index e5d4ba7fb..0b2fac203 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,22 +1,31 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::sub_lib::wallet::Wallet; use web3::types::U256; #[derive(Clone, Debug, PartialEq, Eq)] pub struct WeightedPayable { - pub qualified_account: QualifiedPayableAccount, + pub analyzed_account: AnalyzedPayableAccount, pub weight: u128, } impl WeightedPayable { - pub fn new(qualified_account: QualifiedPayableAccount, weight: u128) -> Self { + pub fn new(analyzed_account: AnalyzedPayableAccount, weight: u128) -> Self { Self { - qualified_account, + analyzed_account, weight, } } + + pub fn wallet(&self) -> &Wallet { + &self.analyzed_account.qualified_as.bare_account.wallet + } + + pub fn balance_minor(&self) -> u128 { + self.analyzed_account.qualified_as.bare_account.balance_wei + } } #[derive(Debug, PartialEq, Eq)] @@ -89,6 +98,19 @@ impl UnconfirmedAdjustment { disqualification_limit_minor, } } + + pub fn wallet(&self) -> &Wallet { + &self + .weighted_account + .analyzed_account + .qualified_as + .bare_account + .wallet + } + + pub fn balance_minor(&self) -> u128 { + todo!() + } } pub struct TransactionCountsWithin16bits { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e7887d0a8..dafdae2f9 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -6,7 +6,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::o exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AccountsEliminatedByTxFeeInfo, AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable}; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use itertools::{Either, Itertools}; pub fn zero_affordable_accounts_found( @@ -32,7 +32,7 @@ pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { }) } -pub fn dump_unaffordable_accounts_by_txn_fee( +pub fn dump_unaffordable_accounts_by_transaction_fee( weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, ) -> (Vec, AccountsEliminatedByTxFeeInfo) { @@ -43,7 +43,11 @@ pub fn dump_unaffordable_accounts_by_txn_fee( let elimination_info = { let count = to_be_dumped.len(); let sum_of_balances = sum_as(&to_be_dumped, |account| { - account.qualified_account.bare_account.balance_wei + account + .analyzed_account + .qualified_as + .bare_account + .balance_wei }); AccountsEliminatedByTxFeeInfo { count, @@ -73,14 +77,15 @@ pub fn compute_mul_coefficient_preventing_fractional_numbers( u128::MAX / cw_service_fee_balance_minor } -pub fn find_largest_exceeding_balance(qualified_accounts: &[QualifiedPayableAccount]) -> u128 { +pub fn find_largest_exceeding_balance(qualified_accounts: &[AnalyzedPayableAccount]) -> u128 { let diffs = qualified_accounts .iter() .map(|account| { account + .qualified_as .bare_account .balance_wei - .checked_sub(account.payment_threshold_intercept_minor) + .checked_sub(account.qualified_as.payment_threshold_intercept_minor) .expect("should be: balance > intercept!") }) .collect::>(); @@ -208,7 +213,7 @@ pub fn drop_no_longer_needed_weights_away_from_accounts( ) -> Vec { weights_and_accounts .into_iter() - .map(|weighted_account| weighted_account.qualified_account.bare_account) + .map(|weighted_account| weighted_account.analyzed_account.qualified_as.bare_account) .collect() } @@ -224,7 +229,7 @@ mod tests { ConsumingWalletExhaustingStatus, }; use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, make_payable_account, + make_analyzed_account, make_non_guaranteed_qualified_payable, make_payable_account, }; use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; @@ -279,12 +284,12 @@ mod tests { #[test] fn find_largest_exceeding_balance_works() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.bare_account.balance_wei = 5_000_000_000; - account_1.payment_threshold_intercept_minor = 2_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(222); - account_2.bare_account.balance_wei = 4_000_000_000; - account_2.payment_threshold_intercept_minor = 800_000_000; + let mut account_1 = make_analyzed_account(111); + account_1.qualified_as.bare_account.balance_wei = 5_000_000_000; + account_1.qualified_as.payment_threshold_intercept_minor = 2_000_000_000; + let mut account_2 = make_analyzed_account(222); + account_2.qualified_as.bare_account.balance_wei = 4_000_000_000; + account_2.qualified_as.payment_threshold_intercept_minor = 800_000_000; let qualified_accounts = &[account_1, account_2]; let result = find_largest_exceeding_balance(qualified_accounts); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 7cadd5948..75e4e3860 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -39,7 +39,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_txn_fee, + drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, sort_in_descendant_order_by_weights, sum_as, zero_affordable_accounts_found, }; @@ -49,7 +49,7 @@ use crate::accountant::payment_adjuster::service_fee_adjuster::{ }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -57,6 +57,7 @@ use itertools::Either; use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; +use std::fmt::Alignment::Left; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; @@ -64,9 +65,9 @@ use web3::types::U256; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[QualifiedPayableAccount], + qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, PaymentAdjusterError>; + ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError>; fn adjust_payments( &mut self, @@ -80,18 +81,19 @@ pub trait PaymentAdjuster { pub struct PaymentAdjusterReal { analyzer: PreparatoryAnalyzer, disqualification_arbiter: DisqualificationArbiter, - inner: Box, service_fee_adjuster: Box, calculators: Vec>, + inner: Box, logger: Logger, } impl PaymentAdjuster for PaymentAdjusterReal { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[QualifiedPayableAccount], + qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, PaymentAdjusterError> { + ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> + { let number_of_counts = qualified_payables.len(); match self @@ -103,25 +105,25 @@ impl PaymentAdjuster for PaymentAdjusterReal { ) { Ok(None) => (), Ok(Some(affordable_transaction_count)) => { - return Ok(Some(Adjustment::TransactionFeeInPriority { - affordable_transaction_count, - })) + return Ok(Either::Right(AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count, + }, + todo!(), + ))) } Err(e) => return Err(e), }; let service_fee_balance_minor = agent.service_fee_balance_minor(); - match self.analyzer.check_need_of_adjustment_by_service_fee( - &self.disqualification_arbiter, - None, - Either::Left(qualified_payables), - service_fee_balance_minor, - &self.logger, - ) { - Ok(false) => Ok(None), - Ok(true) => Ok(Some(Adjustment::ByServiceFee)), - Err(e) => Err(e), - } + self.analyzer + .check_need_of_adjustment_by_service_fee::( + &self.disqualification_arbiter, + None, + qualified_payables, + service_fee_balance_minor, + &self.logger, + ) } fn adjust_payments( @@ -129,13 +131,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { setup: PreparedAdjustment, now: SystemTime, ) -> Result { - let qualified_payables = setup.qualified_payables; + let analyzed_payables = setup.adjustment_analysis.accounts; let response_skeleton_opt = setup.response_skeleton_opt; let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); - let required_adjustment = setup.adjustment; + let required_adjustment = setup.adjustment_analysis.adjustment; let largest_exceeding_balance_recently_qualified = - find_largest_exceeding_balance(&qualified_payables); + find_largest_exceeding_balance(&analyzed_payables); self.initialize_inner( initial_service_fee_balance_minor, @@ -144,9 +146,9 @@ impl PaymentAdjuster for PaymentAdjusterReal { now, ); - let sketched_debug_info_opt = self.sketch_debug_info_opt(&qualified_payables); + let sketched_debug_info_opt = self.sketch_debug_info_opt(&analyzed_payables); - let affordable_accounts = self.run_adjustment(qualified_payables)?; + let affordable_accounts = self.run_adjustment(analyzed_payables)?; self.complete_debug_info_if_enabled(sketched_debug_info_opt, &affordable_accounts); @@ -171,9 +173,9 @@ impl PaymentAdjusterReal { Self { analyzer: PreparatoryAnalyzer::new(), disqualification_arbiter: DisqualificationArbiter::default(), - inner: Box::new(PaymentAdjusterInnerNull {}), service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], + inner: Box::new(PaymentAdjusterInnerNull {}), logger: Logger::new("PaymentAdjuster"), } } @@ -204,10 +206,10 @@ impl PaymentAdjusterReal { fn run_adjustment( &mut self, - qualified_accounts: Vec, + analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let weighted_accounts_sorted = self.calculate_weights_for_accounts(qualified_accounts); - let processed_accounts = self.calculate_criteria_and_propose_adjustments_recursively( + let weighted_accounts_sorted = self.calculate_weights(analyzed_accounts); + let processed_accounts = self.propose_adjustments_recursively( weighted_accounts_sorted, TransactionAndServiceFeeAdjustmentRunner {}, )?; @@ -230,7 +232,7 @@ impl PaymentAdjusterReal { } } - fn calculate_criteria_and_propose_adjustments_recursively( + fn propose_adjustments_recursively( &mut self, unresolved_accounts: Vec, adjustment_runner: AR, @@ -255,42 +257,40 @@ impl PaymentAdjusterReal { PaymentAdjusterError, > { let (weighted_accounts_affordable_by_transaction_fee, elimination_info) = - dump_unaffordable_accounts_by_txn_fee( + dump_unaffordable_accounts_by_transaction_fee( weighted_accounts_in_descending_order, already_known_affordable_transaction_count, ); let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); - let is_service_fee_adjustment_needed = - match self.analyzer.check_need_of_adjustment_by_service_fee( - &self.disqualification_arbiter, - Some(elimination_info), - Either::Right(&weighted_accounts_affordable_by_transaction_fee), - cw_service_fee_balance, - &self.logger, - ) { - Ok(answer) => answer, - Err(e) => { - log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); - return Err(e); - } - }; + let check_result = match self.analyzer.check_need_of_adjustment_by_service_fee( + &self.disqualification_arbiter, + Some(elimination_info), + weighted_accounts_affordable_by_transaction_fee, + cw_service_fee_balance, + &self.logger, + ) { + Ok(answer) => answer, + Err(e) => { + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); + return Err(e); + } + }; - match is_service_fee_adjustment_needed { - true => { + match check_result { + Either::Left(weighted_accounts_needing_adjustment) => { diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); let adjustment_result_before_verification = self - .propose_possible_adjustment_recursively( - weighted_accounts_affordable_by_transaction_fee, - ); + .propose_possible_adjustment_recursively(weighted_accounts_needing_adjustment); Ok(Either::Left(adjustment_result_before_verification)) } - false => { + Either::Right(weighted_accounts) => { let accounts_not_needing_adjustment = + //TODO you should use from impl drop_no_longer_needed_weights_away_from_accounts( - weighted_accounts_affordable_by_transaction_fee, + weighted_accounts, ); Ok(Either::Right(accounts_not_needing_adjustment)) } @@ -348,39 +348,35 @@ impl PaymentAdjusterReal { } }; - let down_stream_decided_accounts = self - .calculate_criteria_and_propose_adjustments_recursively( - remaining_undecided_accounts, - ServiceFeeOnlyAdjustmentRunner {}, - ); + let down_stream_decided_accounts = self.propose_adjustments_recursively( + remaining_undecided_accounts, + ServiceFeeOnlyAdjustmentRunner {}, + ); RecursionResults::new(here_decided_accounts, down_stream_decided_accounts) } - fn calculate_weights_for_accounts( - &self, - accounts: Vec, - ) -> Vec { + fn calculate_weights(&self, accounts: Vec) -> Vec { self.apply_criteria(self.calculators.as_slice(), accounts) } fn apply_criteria( &self, criteria_calculators: &[Box], - qualified_accounts: Vec, + qualified_accounts: Vec, ) -> Vec { let weighted_accounts = qualified_accounts.into_iter().map(|payable| { let weight = criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = - criterion_calculator.calculate(&payable, self.inner.as_ref()); + let new_criterion = criterion_calculator + .calculate(&payable.qualified_as, self.inner.as_ref()); let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( - &payable.bare_account.wallet, + &payable.qualified_as.bare_account.wallet, criterion_calculator.as_ref(), new_criterion, summed_up, @@ -417,15 +413,15 @@ impl PaymentAdjusterReal { fn sketch_debug_info_opt( &self, - qualified_payables: &[QualifiedPayableAccount], + qualified_payables: &[AnalyzedPayableAccount], ) -> Option> { self.logger.debug_enabled().then(|| { qualified_payables .iter() .map(|payable| { ( - payable.bare_account.wallet.clone(), - payable.bare_account.balance_wei, + payable.qualified_as.bare_account.wallet.clone(), + payable.qualified_as.bare_account.balance_wei, ) }) .collect::>() @@ -451,6 +447,21 @@ pub enum Adjustment { TransactionFeeInPriority { affordable_transaction_count: u16 }, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AdjustmentAnalysis { + pub adjustment: Adjustment, + pub accounts: Vec, +} + +impl AdjustmentAnalysis { + pub fn new(adjustment: Adjustment, accounts: Vec) -> Self { + AdjustmentAnalysis { + adjustment, + accounts, + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum PaymentAdjusterError { NotEnoughTransactionFeeBalanceForSingleTx { @@ -529,18 +540,19 @@ mod tests { AdjustmentComputer, ServiceFeeAdjusterReal, }; use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_payables, make_initialized_subject, make_qualified_payable_by_wallet, - multiple_by_billion, CriterionCalculatorMock, DisqualificationGaugeMock, - ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, - PRESERVED_TEST_PAYMENT_THRESHOLDS, + make_analyzed_account_by_wallet, make_extreme_payables, make_initialized_subject, + make_qualified_payable_by_wallet, multiple_by_billion, CriterionCalculatorMock, + DisqualificationGaugeMock, ServiceFeeAdjusterMock, + MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ - Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ + make_analyzed_account, make_guaranteed_analyzed_payables, make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, make_payable_account, }; @@ -555,6 +567,7 @@ mod tests { use libc::RESOLVE_NO_XDEV; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use masq_lib::utils::convert_collection; use rand::rngs::mock; use std::collections::HashMap; use std::iter::zip; @@ -670,8 +683,9 @@ mod tests { .enumerate() .for_each(|(idx, (qualified_payables, agent))| { assert_eq!( - subject.search_for_indispensable_adjustment(&qualified_payables, &*agent), - Ok(None), + subject + .search_for_indispensable_adjustment(qualified_payables.clone(), &*agent), + Ok(Either::Left(qualified_payables)), "failed for tested input number {:?}", idx + 1 ) @@ -697,14 +711,18 @@ mod tests { cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, }), ); + let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); + let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); assert_eq!( result, - Ok(Some(Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 2 - })) + Ok(Either::Right(AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 2 + }, + analyzed_payables + ))) ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( @@ -736,10 +754,17 @@ mod tests { }), None, ); + let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); + let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); - assert_eq!(result, Ok(Some(Adjustment::ByServiceFee))); + assert_eq!( + result, + Ok(Either::Right(AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_payables + ))) + ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,\ 000,001 wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of \ @@ -765,7 +790,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); + let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); assert_eq!( result, @@ -796,7 +821,7 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(&qualified_payables, &*agent); + let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); assert_eq!( result, @@ -853,8 +878,8 @@ mod tests { .calculate_result(1_000_000_003); let subject = make_initialized_subject(None, None, Some(calculator), Some(12345678), None); let make_account = |n: u64| { - let account = make_non_guaranteed_qualified_payable(n); - let wallet = account.bare_account.wallet.clone(); + let account = make_analyzed_account(n); + let wallet = account.qualified_as.bare_account.wallet.clone(); (wallet, account) }; let (wallet_1, payable_1) = make_account(111); @@ -862,7 +887,7 @@ mod tests { let (wallet_3, payable_3) = make_account(333); let criteria_and_accounts = - subject.calculate_weights_for_accounts(vec![payable_1, payable_2, payable_3]); + subject.calculate_weights(vec![payable_1, payable_2, payable_3]); let mut previous_weight = u128::MAX; let accounts_alone = criteria_and_accounts @@ -875,7 +900,11 @@ mod tests { weighted_account.weight ); previous_weight = weighted_account.weight; - weighted_account.qualified_account.bare_account.wallet + weighted_account + .analyzed_account + .qualified_as + .bare_account + .wallet }) .collect::>(); assert_eq!(accounts_alone, vec![wallet_3, wallet_1, wallet_2]) @@ -886,23 +915,20 @@ mod tests { ) { let cw_service_fee_balance_minor = multiple_by_billion(3_600_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let mut account_1 = make_qualified_payable_by_wallet("abc"); + let mut account_1 = make_analyzed_account_by_wallet("abc"); let balance_1 = multiple_by_billion(3_000_000); let disqualification_limit_1 = multiple_by_billion(2_300_000); - account_1.bare_account.balance_wei = balance_1; - let threshold_intercept_minor_1 = account_1.payment_threshold_intercept_minor; - let permanent_debt_allowed_minor_1 = - account_1.creditor_thresholds.permanent_debt_allowed_wei; - let mut account_2 = make_qualified_payable_by_wallet("def"); - let wallet_2 = account_2.bare_account.wallet.clone(); + account_1.qualified_as.bare_account.balance_wei = balance_1; + account_1.disqualification_limit = disqualification_limit_1; + let mut account_2 = make_analyzed_account_by_wallet("def"); + let wallet_2 = account_2.qualified_as.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); let disqualification_limit_2 = multiple_by_billion(800_000); - account_2.bare_account.balance_wei = balance_2; - let threshold_intercept_minor_2 = account_2.payment_threshold_intercept_minor; - let permanent_debt_allowed_minor_2 = - account_2.creditor_thresholds.permanent_debt_allowed_wei; - let largest_exceeding_balance = (balance_1 - account_1.payment_threshold_intercept_minor) - .max(balance_2 - account_2.payment_threshold_intercept_minor); + account_2.qualified_as.bare_account.balance_wei = balance_2; + account_2.disqualification_limit = disqualification_limit_2; + let largest_exceeding_balance = (balance_1 + - account_1.qualified_as.payment_threshold_intercept_minor) + .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); let mut subject = make_initialized_subject( None, Some(cw_service_fee_balance_minor), @@ -923,7 +949,7 @@ mod tests { ]; let mut result = subject - .calculate_criteria_and_propose_adjustments_recursively( + .propose_adjustments_recursively( weighted_payables_in_descending_order.clone(), TransactionAndServiceFeeAdjustmentRunner {}, ) @@ -947,7 +973,8 @@ mod tests { assert_eq!( &first_returned_account.original_account, &weighted_payables_in_descending_order[0] - .qualified_account + .analyzed_account + .qualified_as .bare_account ); assert_eq!( @@ -958,7 +985,8 @@ mod tests { assert_eq!( &second_returned_account.original_account, &weighted_payables_in_descending_order[1] - .qualified_account + .analyzed_account + .qualified_as .bare_account ); assert_eq!( @@ -966,27 +994,6 @@ mod tests { 2_300_000_000_000_000 ); assert!(result.is_empty()); - let determine_limit_params = determine_limit_params_arc.lock().unwrap(); - assert_eq!( - *determine_limit_params, - vec![ - ( - balance_2, - threshold_intercept_minor_2, - permanent_debt_allowed_minor_2 - ), - ( - balance_1, - threshold_intercept_minor_1, - permanent_debt_allowed_minor_1 - ), - ( - balance_1, - threshold_intercept_minor_1, - permanent_debt_allowed_minor_1 - ) - ] - ) } fn prove_that_proposed_adjusted_balance_would_exceed_the_original_value( @@ -1018,12 +1025,8 @@ mod tests { // The results are sorted from the biggest weights down let proposed_adjusted_balance = unconfirmed_adjustments[0].proposed_adjusted_balance_minor; assert_eq!( - unconfirmed_adjustments[0] - .weighted_account - .qualified_account - .bare_account - .wallet, - wallet_of_expected_outweighed + unconfirmed_adjustments[0].wallet(), + &wallet_of_expected_outweighed ); // The weight of this account grew progressively due to the additional criterion added // in to the sum. Consequences would've been that redistribution of the adjusted balances @@ -1082,7 +1085,7 @@ mod tests { let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( disqualification_arbiter, None, - Either::Left(&qualified_payables), + qualified_payables, service_fee_balance_in_minor_units, &subject.logger, ); @@ -1091,7 +1094,16 @@ mod tests { // However, it can only assess the balance (that early - in the real world) and accounts // with the smallest balance is outplayed by the other one gaining some kind of extra // significance - assert_eq!(analysis_result, Ok(true)); + let analyzed_accounts = match analysis_result { + Ok(Either::Right(adjustment_analysis)) => { + assert_eq!(adjustment_analysis.adjustment, Adjustment::ByServiceFee); + adjustment_analysis.accounts + } + x => panic!( + "We expected to be let it for an adjustments with AnalyzedAccounts but got: {:?}", + x + ), + }; let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1099,10 +1111,12 @@ mod tests { Box::new(mock) }; let adjustment_setup = PreparedAdjustment { - qualified_payables, agent, - adjustment: Adjustment::ByServiceFee, response_skeleton_opt: None, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_accounts, + ), }; let result = subject.adjust_payments(adjustment_setup, now); @@ -1148,8 +1162,8 @@ mod tests { pending_payable_opt: None, }; let payables = vec![account_1, account_2.clone(), account_3.clone()]; - let qualified_payables = - make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + let analyzed_accounts = + make_guaranteed_analyzed_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(0) .calculate_result(multiple_by_billion(50_000_000_000)) @@ -1167,9 +1181,11 @@ mod tests { Box::new(mock) }; let adjustment_setup = PreparedAdjustment { - qualified_payables, agent, - adjustment: Adjustment::ByServiceFee, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_accounts, + ), response_skeleton_opt: None, }; @@ -1199,7 +1215,7 @@ mod tests { now, ) }; - let qualified_payables = make_guaranteed_qualified_payables( + let analyzed_payables = make_guaranteed_analyzed_payables( extreme_payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now, @@ -1214,9 +1230,11 @@ mod tests { Box::new(mock) }; let adjustment_setup = PreparedAdjustment { - qualified_payables, agent, - adjustment: Adjustment::ByServiceFee, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_payables, + ), response_skeleton_opt: None, }; @@ -1299,6 +1317,7 @@ mod tests { qualified_account_2.clone(), qualified_account_3.clone(), ]; + let analyzed_payables = convert_collection(qualified_payables); let mut subject = PaymentAdjusterReal::new(); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(4_500_000_000)) @@ -1313,9 +1332,11 @@ mod tests { .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(cw_service_fee_balance_minor); let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::ByServiceFee, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_payables, + ), response_skeleton_opt: None, }; @@ -1381,6 +1402,7 @@ mod tests { let balance_3 = multiple_by_billion(222_222_222); let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 40_000_000); let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; + let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(400_000_000)) // This account will be cut off because it has the lowest weight and only two accounts @@ -1396,11 +1418,13 @@ mod tests { .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(10_u128.pow(22)); let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 2, - }, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 2, + }, + analyzed_payables, + ), response_skeleton_opt: None, }; @@ -1452,6 +1476,7 @@ mod tests { let disqualification_limit_3 = disqualification_arbiter.calculate_disqualification_edge(&account_3); let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; + let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(400_000_000)) .calculate_result(multiple_by_billion(200_000_000)) @@ -1468,11 +1493,13 @@ mod tests { context_id: 321, }); // Just hardening, not so important let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 2, - }, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 2, + }, + analyzed_payables, + ), response_skeleton_opt, }; @@ -1513,6 +1540,7 @@ mod tests { let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); let wallet_3 = account_3.bare_account.wallet.clone(); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; + let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(900_000_000)) .calculate_result(multiple_by_billion(1_100_000_000)) @@ -1530,9 +1558,11 @@ mod tests { context_id: 234, }); let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::ByServiceFee, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::ByServiceFee, + analyzed_payables, + ), response_skeleton_opt, }; @@ -1581,6 +1611,7 @@ mod tests { let account_3 = make_plucked_qualified_account("ghi", balance_3, 90_000_000_000, 20_000_000_000); let qualified_payables = vec![account_1.clone(), account_2, account_3]; + let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(900_000_000_000)) .calculate_result(multiple_by_billion(500_000_000_000)) @@ -1593,11 +1624,13 @@ mod tests { .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(service_fee_balance_in_minor); let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 2, - }, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 2, + }, + analyzed_payables, + ), response_skeleton_opt: None, }; @@ -1653,14 +1686,17 @@ mod tests { // This is exactly the amount which will provoke an error let service_fee_balance_in_minor_units = disqualification_limit_2 - 1; let qualified_payables = vec![account_1, account_2, account_3]; + let analyzed_payables = convert_collection(qualified_payables); let agent = BlockchainAgentMock::default() .service_fee_balance_minor_result(service_fee_balance_in_minor_units); let adjustment_setup = PreparedAdjustment { - qualified_payables, agent: Box::new(agent), - adjustment: Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 2, - }, + adjustment_analysis: AdjustmentAnalysis::new( + Adjustment::TransactionFeeInPriority { + affordable_transaction_count: 2, + }, + analyzed_payables, + ), response_skeleton_opt: None, }; @@ -2002,8 +2038,9 @@ mod tests { now: SystemTime, cw_service_fee_balance_minor: u128, ) -> Vec { + let analyzed_payables = convert_collection(qualified_payables); let largest_exceeding_balance_recently_qualified = - find_largest_exceeding_balance(&qualified_payables); + find_largest_exceeding_balance(&analyzed_payables); let mut subject = make_initialized_subject( Some(now), Some(cw_service_fee_balance_minor), @@ -2024,7 +2061,7 @@ mod tests { }); subject.service_fee_adjuster = Box::new(service_fee_adjuster_mock); - let result = subject.run_adjustment(qualified_payables.to_vec()); + let result = subject.run_adjustment(analyzed_payables); less_important_constant_assertions_and_weighted_accounts_extraction( result, @@ -2104,12 +2141,9 @@ mod tests { fn make_comparison_hashmap( weighted_accounts: Vec, ) -> HashMap { - let feeding_iterator = weighted_accounts.into_iter().map(|account| { - ( - account.qualified_account.bare_account.wallet.clone(), - account, - ) - }); + let feeding_iterator = weighted_accounts + .into_iter() + .map(|account| (account.wallet().clone(), account)); HashMap::from_iter(feeding_iterator) } diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index c392accd2..29fad89fb 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -5,16 +5,16 @@ use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ - Adjustment, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; -use itertools::Itertools; +use itertools::{Either, Itertools}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use rand; use rand::rngs::ThreadRng; @@ -62,18 +62,22 @@ fn loading_test_with_randomized_params() { // doesn't interact with the potential error 'AllAccountsEliminated' whose occurrence // rate is interesting compared to how many times the initial check lets the adjustment // procedure go on - let initial_check_result = subject.search_for_indispensable_adjustment( - &scenario.qualified_payables, - &*scenario.agent, - ); + let qualified_payables = scenario + .adjustment_analysis + .accounts + .iter() + .map(|account| account.qualified_as.clone()) + .collect(); + let initial_check_result = + subject.search_for_indispensable_adjustment(qualified_payables, &*scenario.agent); let allowed_scenario_opt = match initial_check_result { - Ok(adjustment_opt) => { - match adjustment_opt { - None => panic!( - "Wrong test setup. This test is designed to generate scenarios \ - with balances always insufficient in some way!" + Ok(check_factual_output) => { + match check_factual_output { + Either::Left(_) => panic!( + "Wrong test setup. This test is designed to generate scenarios with \ + balances always insufficient in some way!" ), - Some(_) => (), + Either::Right(_) => (), }; Some(scenario) } @@ -104,8 +108,8 @@ fn loading_test_with_randomized_params() { .into_iter() .map(|prepared_adjustment| { let account_infos = - preserve_account_infos(&prepared_adjustment.qualified_payables, now); - let required_adjustment = prepared_adjustment.adjustment.clone(); + preserve_account_infos(&prepared_adjustment.adjustment_analysis.accounts, now); + let required_adjustment = prepared_adjustment.adjustment_analysis.adjustment.clone(); let cw_service_fee_balance_minor = prepared_adjustment.agent.service_fee_balance_minor(); @@ -152,13 +156,13 @@ fn try_making_single_valid_scenario( if payables_len != qualified_payables.len() { return None; } + let analyzed_accounts: Vec = todo!(); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, qualified_payables.len()); Some(PreparedAdjustment::new( - qualified_payables, Box::new(agent), None, - adjustment, + AdjustmentAnalysis::new(adjustment, analyzed_accounts), )) } @@ -296,16 +300,16 @@ struct FailedAdjustment { } fn preserve_account_infos( - accounts: &[QualifiedPayableAccount], + accounts: &[AnalyzedPayableAccount], now: SystemTime, ) -> Vec { accounts .iter() .map(|account| AccountInfo { - wallet: account.bare_account.wallet.clone(), - initially_requested_service_fee_minor: account.bare_account.balance_wei, + wallet: account.qualified_as.bare_account.wallet.clone(), + initially_requested_service_fee_minor: account.qualified_as.bare_account.balance_wei, debt_age_s: now - .duration_since(account.bare_account.last_paid_timestamp) + .duration_since(account.qualified_as.bare_account.last_paid_timestamp) .unwrap() .as_secs(), }) diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 5b1e9d593..1522b699f 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -8,11 +8,11 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AccountsEliminatedByTxFeeInfo, TransactionCountsWithin16bits, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::PaymentAdjusterError; +use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; -use itertools::Either; +use itertools::{Either, Product}; use masq_lib::logger::Logger; pub struct PreparatoryAnalyzer {} @@ -81,77 +81,97 @@ impl PreparatoryAnalyzer { cw_transaction_fee_balance_minor / U256::from(fee_requirement_per_tx_minor) } - pub fn check_need_of_adjustment_by_service_fee( + pub fn check_need_of_adjustment_by_service_fee< + MidProduct, + IncomingAccount, + AdjustmentNeededValue, + >( &self, disqualification_arbiter: &DisqualificationArbiter, accounts_eliminated_by_tx_fee_info_opt: Option, - payables: Either<&[QualifiedPayableAccount], &[WeightedPayable]>, + payables: Vec, cw_service_fee_balance_minor: u128, logger: &Logger, - ) -> Result { - let qualified_payables = Self::comb_qualified_payables(payables); - - let required_service_fee_sum: u128 = - sum_as(&qualified_payables, |qa| qa.bare_account.balance_wei); - - if cw_service_fee_balance_minor >= required_service_fee_sum { - Ok(false) + ) -> Result, AdjustmentNeededValue>, PaymentAdjusterError> + where + IncomingAccount: DisqualificationAnalysableAccount, + Vec: + ReturnedFromServiceFeeCheck, + { + let required_service_fee_total = + Self::compute_total_of_service_fee_required::(&payables); + + if cw_service_fee_balance_minor >= required_service_fee_total { + Ok(Either::Left(payables)) } else { - self.analyse_smallest_adjustment_possibility( - disqualification_arbiter, - accounts_eliminated_by_tx_fee_info_opt, - &qualified_payables, - cw_service_fee_balance_minor, - )?; + let result = self + .analyse_smallest_adjustment_possibility::( + disqualification_arbiter, + accounts_eliminated_by_tx_fee_info_opt, + required_service_fee_total, + payables, + cw_service_fee_balance_minor, + )?; log_adjustment_by_service_fee_is_required( logger, - required_service_fee_sum, + required_service_fee_total, cw_service_fee_balance_minor, ); - Ok(true) + + Ok(Either::Right(result.failing_check_output())) } } - fn comb_qualified_payables<'payables>( - payables: Either<&'payables [QualifiedPayableAccount], &'payables [WeightedPayable]>, - ) -> Vec<&'payables QualifiedPayableAccount> { - match payables { - Either::Left(accounts) => accounts.iter().collect(), - Either::Right(weighted_accounts) => weighted_accounts - .iter() - .map(|weighted_account| &weighted_account.qualified_account) - .collect(), - } + fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 + where + Account: DisqualificationAnalysableAccount, + { + sum_as(payables, |account| account.balance_minor()) + } + + fn find_smallest_weight_and_prepare_accounts_to_proceed( + accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, + ) -> (u128, Vec) + where + Account: DisqualificationAnalysableAccount, + { + todo!() } // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with // the assumption that the debt with the lowest disqualification limit in the set fits in the // available balance. If it doesn't, we won't want to bother the payment adjuster by its work, // so we'll abort and no payments will come out. - fn analyse_smallest_adjustment_possibility( + fn analyse_smallest_adjustment_possibility( &self, disqualification_arbiter: &DisqualificationArbiter, accounts_eliminated_by_tx_fee_info_opt: Option, - qualified_payables: &[&QualifiedPayableAccount], + required_service_fee_total: u128, + accounts: Vec, cw_service_fee_balance_minor: u128, - ) -> Result<(), PaymentAdjusterError> { - let lowest_disqualification_limit = - Self::find_lowest_dsq_limit(qualified_payables, disqualification_arbiter); + ) -> Result, PaymentAdjusterError> + where + Account: DisqualificationAnalysableAccount, + { + let (lowest_disqualification_limit, prepared_accounts) = + Self::find_smallest_weight_and_prepare_accounts_to_proceed( + accounts, + disqualification_arbiter, + ); if lowest_disqualification_limit <= cw_service_fee_balance_minor { - Ok(()) + Ok(prepared_accounts) } else { - let total_amount_demanded_minor = - sum_as(qualified_payables, |qp| qp.bare_account.balance_wei); let (number_of_accounts, total_amount_demanded_minor) = if let Some(info) = accounts_eliminated_by_tx_fee_info_opt { ( - qualified_payables.len() + info.count, - total_amount_demanded_minor + info.sum_of_balances, + prepared_accounts.len() + info.count, + required_service_fee_total + info.sum_of_balances, ) } else { - (qualified_payables.len(), total_amount_demanded_minor) + (prepared_accounts.len(), required_service_fee_total) }; Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { @@ -174,14 +194,72 @@ impl PreparatoryAnalyzer { } } +pub trait DisqualificationAnalysableAccount { + // fn process_findings(insufficiency_found: bool)-> + fn analyse_limit(self, disqualification_arbiter: &DisqualificationArbiter) -> (Product, u128); + fn balance_minor(&self) -> u128; +} + +impl DisqualificationAnalysableAccount for QualifiedPayableAccount { + fn analyse_limit( + self, + disqualification_arbiter: &DisqualificationArbiter, + ) -> (AnalyzedPayableAccount, u128) { + todo!() + } + + fn balance_minor(&self) -> u128 { + self.bare_account.balance_wei + } +} + +impl DisqualificationAnalysableAccount for WeightedPayable { + fn analyse_limit( + self, + disqualification_arbiter: &DisqualificationArbiter, + ) -> (WeightedPayable, u128) { + todo!() + } + + fn balance_minor(&self) -> u128 { + self.analyzed_account.qualified_as.bare_account.balance_wei + } +} + +pub trait ReturnedFromServiceFeeCheck { + type AdjustmentNeededReturnValue; + + fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue; +} + +impl ReturnedFromServiceFeeCheck for Vec { + type AdjustmentNeededReturnValue = AdjustmentAnalysis; + + fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue { + todo!() + } +} + +impl ReturnedFromServiceFeeCheck for Vec { + type AdjustmentNeededReturnValue = Vec; + + fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue { + todo!() + } +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; + use crate::accountant::payment_adjuster::preparatory_analyser::{ + DisqualificationAnalysableAccount, PreparatoryAnalyzer, + }; use crate::accountant::payment_adjuster::test_utils::DisqualificationGaugeMock; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; use crate::accountant::QualifiedPayableAccount; + use masq_lib::utils::convert_collection; use std::sync::{Arc, Mutex}; #[test] @@ -217,21 +295,22 @@ mod tests { let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); let disqualification_gauge = disqualification_gauge.determine_limit_params(&determine_limit_params_arc); - let accounts_in_expected_format = original_accounts - .iter() - .collect::>(); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; - + let required_service_fee_total = sum_as(original_accounts.as_slice(), |account| { + account.balance_minor() + }); let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, None, - &accounts_in_expected_format, + required_service_fee_total, + original_accounts.clone().to_vec(), cw_service_fee_balance, ); - assert_eq!(result, Ok(())); + let expected_analyzed_accounts = convert_collection(original_accounts.to_vec()); + assert_eq!(result, Ok(expected_analyzed_accounts)); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); let account_1 = &original_accounts[0]; let account_2 = &original_accounts[1]; @@ -300,9 +379,7 @@ mod tests { account_3.bare_account.balance_wei = 1_000_111_111; let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; - let accounts_in_expected_format = original_accounts - .iter() - .collect::>(); + let required_fee_total = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; let disqualification_gauge = DisqualificationGaugeMock::default() .determine_limit_result(1_500_000_000) .determine_limit_result(1_000_000_101) @@ -314,7 +391,8 @@ mod tests { let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, None, - &accounts_in_expected_format, + required_fee_total, + original_accounts, cw_service_fee_balance, ); @@ -323,7 +401,7 @@ mod tests { Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, - total_amount_demanded_minor: 2_000_000_000 + 1_000_050_000 + 1_000_111_111, + total_amount_demanded_minor: required_fee_total, cw_service_fee_balance_minor: cw_service_fee_balance } ) diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 217499e3b..5870d0015 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -77,7 +77,9 @@ impl ServiceFeeAdjusterReal { .into_iter() .map(|weighted_account| { let disqualification_limit = disqualification_arbiter - .calculate_disqualification_edge(&weighted_account.qualified_account); + .calculate_disqualification_edge( + &weighted_account.analyzed_account.qualified_as, + ); minimal_acceptable_balance_assigned_diagnostics( &weighted_account, disqualification_limit, @@ -149,14 +151,7 @@ impl ServiceFeeAdjusterReal { let remaining = unconfirmed_adjustments .into_iter() - .filter(|account_info| { - account_info - .weighted_account - .qualified_account - .bare_account - .wallet - != disqualified_account_wallet - }) + .filter(|account_info| account_info.wallet() != &disqualified_account_wallet) .collect(); let remaining_reverted = convert_collection(remaining); @@ -246,13 +241,12 @@ impl AdjustmentComputer { let proposed_adjusted_balance = compute_proposed_adjusted_balance(weighted_account.weight); - proposed_adjusted_balance_diagnostics( - &weighted_account.qualified_account, - proposed_adjusted_balance, - ); + proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); let disqualification_limit = disqualification_arbiter - .calculate_disqualification_edge(&weighted_account.qualified_account); + .calculate_disqualification_edge( + &weighted_account.analyzed_account.qualified_as, + ); UnconfirmedAdjustment::new( weighted_account, @@ -299,7 +293,8 @@ mod tests { let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); account_1 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; @@ -308,7 +303,8 @@ mod tests { let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei = multiple_by_billion(5_000_000_000); account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; @@ -317,7 +313,8 @@ mod tests { let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei = multiple_by_billion(3_000_000_000); account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; @@ -326,7 +323,8 @@ mod tests { let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei = multiple_by_billion(1_500_000_000); account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; @@ -335,7 +333,8 @@ mod tests { let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; @@ -354,15 +353,27 @@ mod tests { assert_eq!(low_gainers, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ AdjustedAccountBeforeFinalization { - original_account: account_1.weighted_account.qualified_account.bare_account, + original_account: account_1 + .weighted_account + .analyzed_account + .qualified_as + .bare_account, proposed_adjusted_balance_minor: multiple_by_billion(1_800_000_000), }, AdjustedAccountBeforeFinalization { - original_account: account_2.weighted_account.qualified_account.bare_account, + original_account: account_2 + .weighted_account + .analyzed_account + .qualified_as + .bare_account, proposed_adjusted_balance_minor: multiple_by_billion(4_200_000_000) - 1, }, AdjustedAccountBeforeFinalization { - original_account: account_4.weighted_account.qualified_account.bare_account, + original_account: account_4 + .weighted_account + .analyzed_account + .qualified_as + .bare_account, proposed_adjusted_balance_minor: multiple_by_billion(500_000_000), }, ]; @@ -400,13 +411,15 @@ mod tests { let unconfirmed_account_1 = make_non_guaranteed_unconfirmed_adjustment(111); let payable_account_1 = unconfirmed_account_1 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .clone(); let unconfirmed_account_2 = make_non_guaranteed_unconfirmed_adjustment(222); let payable_account_2 = unconfirmed_account_2 .weighted_account - .qualified_account + .analyzed_account + .qualified_as .bare_account .clone(); let accounts = vec![unconfirmed_account_1, unconfirmed_account_2]; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 50a09ff23..2365086c2 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -13,8 +13,8 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; -use crate::accountant::QualifiedPayableAccount; +use crate::accountant::test_utils::{make_analyzed_account, make_non_guaranteed_qualified_payable}; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use itertools::Either; @@ -92,13 +92,13 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO }; pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { - let qualified_payable = make_non_guaranteed_qualified_payable(n); + let analyzed_account = make_analyzed_account(n); let proposed_adjusted_balance_minor = - (qualified_payable.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; + (analyzed_account.qualified_as.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; let disqualification_limit = (3 * proposed_adjusted_balance_minor) / 4; let weight = (n as u128).pow(3); UnconfirmedAdjustment::new( - WeightedPayable::new(qualified_payable, weight), + WeightedPayable::new(analyzed_account, weight), proposed_adjusted_balance_minor, disqualification_limit, ) @@ -144,6 +144,7 @@ impl DisqualificationGauge for DisqualificationGaugeMock { threshold_intercept_wei: u128, permanent_debt_allowed_wei: u128, ) -> u128 { + todo!("make sure this params are used somewhere"); self.determine_limit_params.lock().unwrap().push(( account_balance_wei, threshold_intercept_wei, @@ -219,3 +220,20 @@ pub fn make_qualified_payable_by_wallet(wallet_address_segment: &str) -> Qualifi account.bare_account.wallet = wallet; account } + +pub fn make_analyzed_account_by_wallet(wallet_address_segment: &str) -> AnalyzedPayableAccount { + let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); + let wallet = make_wallet(wallet_address_segment); + let mut account = make_analyzed_account(num); + account.qualified_as.bare_account.wallet = wallet; + account +} + +// Should stay test only! +impl From for AnalyzedPayableAccount { + fn from(qualified_account: QualifiedPayableAccount) -> Self { + let disqualification_limit = + DisqualificationArbiter::default().calculate_disqualification_edge(&qualified_account); + AnalyzedPayableAccount::new(qualified_account, disqualification_limit) + } +} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 68db68e08..829ef4990 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -6,11 +6,11 @@ pub mod blockchain_agent; pub mod msgs; pub mod test_utils; -use crate::accountant::payment_adjuster::Adjustment; +use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::Scanner; -use crate::accountant::{QualifiedPayableAccount, ResponseSkeleton}; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount, ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use actix::Message; use itertools::Either; @@ -39,24 +39,21 @@ pub trait SolvencySensitivePaymentInstructor { } pub struct PreparedAdjustment { - pub qualified_payables: Vec, pub agent: Box, pub response_skeleton_opt: Option, - pub adjustment: Adjustment, + pub adjustment_analysis: AdjustmentAnalysis, } impl PreparedAdjustment { pub fn new( - qualified_payables: Vec, agent: Box, response_skeleton_opt: Option, - adjustment: Adjustment, + adjustment_analysis: AdjustmentAnalysis, ) -> Self { Self { - qualified_payables, agent, response_skeleton_opt, - adjustment, + adjustment_analysis, } } } @@ -68,10 +65,9 @@ mod tests { impl Clone for PreparedAdjustment { fn clone(&self) -> Self { Self { - qualified_payables: self.qualified_payables.clone(), agent: self.agent.dup(), response_skeleton_opt: self.response_skeleton_opt, - adjustment: self.adjustment.clone(), + adjustment_analysis: self.adjustment_analysis.clone(), } } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 34a85f65f..47d70175c 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -261,21 +261,22 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { match self .payment_adjuster .borrow() - .search_for_indispensable_adjustment(&unprotected, &*msg.agent) + .search_for_indispensable_adjustment(unprotected, &*msg.agent) { - Ok(adjustment_opt) => { - let either = match adjustment_opt { - None => Either::Left(OutboundPaymentsInstructions::new( - Either::Left(unprotected), - msg.agent, - msg.response_skeleton_opt, - )), - Some(adjustment) => { + Ok(processed) => { + let either = match processed { + Either::Left(verified_payables) => { + Either::Left(OutboundPaymentsInstructions::new( + Either::Left(verified_payables), + msg.agent, + msg.response_skeleton_opt, + )) + } + Either::Right(adjustment_analysis) => { let prepared_adjustment = PreparedAdjustment::new( - unprotected, msg.agent, msg.response_skeleton_opt, - adjustment, + adjustment_analysis, ); Either::Right(prepared_adjustment) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index a3eb415e2..4ee4953e4 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -13,7 +13,9 @@ use crate::accountant::db_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; -use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjuster, PaymentAdjusterError}; +use crate::accountant::payment_adjuster::{ + Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, +}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ BlockchainAgentWithContextMessage, QualifiedPayablesMessage, @@ -29,8 +31,8 @@ use crate::accountant::scanners::{ ReceivableScanner, ScanSchedulers, Scanner, }; use crate::accountant::{ - gwei_to_wei, Accountant, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, - SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, + gwei_to_wei, Accountant, AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount, + ResponseSkeleton, SentPayables, DEFAULT_PENDING_TOO_LONG_SEC, }; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::data_structures::BlockchainTransaction; @@ -52,6 +54,7 @@ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::messages::ScanType; use masq_lib::ui_gateway::NodeToUiMessage; +use masq_lib::utils::convert_collection; use rusqlite::{Connection, Row}; use std::any::type_name; use std::cell::RefCell; @@ -1421,8 +1424,9 @@ impl PayableThresholdsGaugeMock { pub struct PaymentAdjusterMock { search_for_indispensable_adjustment_params: Arc, ArbitraryIdStamp)>>>, - search_for_indispensable_adjustment_results: - RefCell, PaymentAdjusterError>>>, + search_for_indispensable_adjustment_results: RefCell< + Vec, AdjustmentAnalysis>, PaymentAdjusterError>>, + >, adjust_payments_params: Arc>>, adjust_payments_results: RefCell>>, @@ -1431,13 +1435,14 @@ pub struct PaymentAdjusterMock { impl PaymentAdjuster for PaymentAdjusterMock { fn search_for_indispensable_adjustment( &self, - qualified_payables: &[QualifiedPayableAccount], + qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, PaymentAdjusterError> { + ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> + { self.search_for_indispensable_adjustment_params .lock() .unwrap() - .push((qualified_payables.to_vec(), agent.arbitrary_id_stamp())); + .push((qualified_payables, agent.arbitrary_id_stamp())); self.search_for_indispensable_adjustment_results .borrow_mut() .remove(0) @@ -1467,7 +1472,10 @@ impl PaymentAdjusterMock { pub fn search_for_indispensable_adjustment_result( self, - result: Result, PaymentAdjusterError>, + result: Result< + Either, AdjustmentAnalysis>, + PaymentAdjusterError, + >, ) -> Self { self.search_for_indispensable_adjustment_results .borrow_mut() @@ -1696,6 +1704,10 @@ pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount ) } +pub fn make_analyzed_account(n: u64) -> AnalyzedPayableAccount { + AnalyzedPayableAccount::new(make_non_guaranteed_qualified_payable(n), 123456789) +} + pub fn make_guaranteed_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, @@ -1704,6 +1716,18 @@ pub fn make_guaranteed_qualified_payables( try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) } +pub fn make_guaranteed_analyzed_payables( + payables: Vec, + payment_thresholds: &PaymentThresholds, + now: SystemTime, +) -> Vec { + convert_collection(make_guaranteed_qualified_payables( + payables, + payment_thresholds, + now, + )) +} + pub fn try_making_guaranteed_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, From c7ab101fc6b15f9d5fdeb9589add07191d7ba5f1 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 16 Apr 2024 23:20:11 +0200 Subject: [PATCH 167/250] GH-711-c: some progress in mounting things together properly; mostly in the preparatory_analyzer --- multinode_integration_tests/ci/all.sh | 2 +- .../src/masq_cores_server.rs | 2 +- .../tests/verify_bill_payment.rs | 2 +- .../db_access_objects/receivable_dao.rs | 2 +- node/src/accountant/mod.rs | 12 +- .../payment_adjuster/adjustment_runners.rs | 12 +- .../disqualification_arbiter.rs | 13 +- .../logging_and_diagnostics/diagnostics.rs | 30 +- .../account_stages_conversions.rs | 16 +- .../miscellaneous/data_structures.rs | 99 +++++-- .../miscellaneous/helper_functions.rs | 42 +-- node/src/accountant/payment_adjuster/mod.rs | 122 ++++---- .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../payment_adjuster/preparatory_analyser.rs | 276 +++++++++++------- .../payment_adjuster/service_fee_adjuster.rs | 69 ++--- .../accountant/payment_adjuster/test_utils.rs | 22 +- node/src/accountant/scanners/mod.rs | 2 +- .../src/accountant/scanners/scanners_utils.rs | 4 +- .../blockchain_interface_web3/mod.rs | 4 +- node/src/bootstrapper.rs | 6 +- node/src/daemon/launch_verifier.rs | 2 +- node/src/database/db_initializer.rs | 2 +- node/src/db_config/db_encryption_layer.rs | 2 +- node/src/neighborhood/gossip_acceptor.rs | 6 +- node/src/neighborhood/mod.rs | 2 +- node/src/sub_lib/accountant.rs | 2 +- node/src/sub_lib/neighborhood.rs | 2 +- node/src/sub_lib/ttl_hashmap.rs | 2 +- ...r_diagnostics_of_age_criterion_formula.txt | 79 ----- ...agnostics_of_balance_criterion_formula.txt | 35 --- node/src/test_utils/mod.rs | 6 +- .../src/test_utils/neighborhood_test_utils.rs | 2 +- .../node_exits_from_crash_message_test.rs | 2 +- 33 files changed, 433 insertions(+), 450 deletions(-) delete mode 100644 node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt delete mode 100644 node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index b8b6212e0..6ce38689c 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -7,7 +7,7 @@ export PATH="$PATH:$HOME/.cargo/bin" export RUST_BACKTRACE=full -# Docker-beside-Docker: Running these non_unit_tests requires building and starting Docker containers that refer to an +# Docker-beside-Docker: Running these tests requires building and starting Docker containers that refer to an # existing executable on the host system. If you're not running in a Docker container, the executable you want # will be in your own filesystem, and these scripts can find everything they need without assistance. But if you # _are_ running in a Docker container, the containers you start will be your siblings, not your children, and the diff --git a/multinode_integration_tests/src/masq_cores_server.rs b/multinode_integration_tests/src/masq_cores_server.rs index 9585db0e1..c98fcd661 100644 --- a/multinode_integration_tests/src/masq_cores_server.rs +++ b/multinode_integration_tests/src/masq_cores_server.rs @@ -30,7 +30,7 @@ use std::thread; use std::thread::JoinHandle; use std::time::Duration; -// TODO: Cover this with non_unit_tests and put it in the production tree. +// TODO: Cover this with tests and put it in the production tree. pub struct DiscriminatorCluster { discriminators: Vec, } diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 0238f4d2b..52c472694 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -566,7 +566,7 @@ struct TestInputsOutputsConfig { ui_ports_opt: Option, // Gets ganache default 100 ETH if None. // Uses an arg where the private key of the wallet and the amount in - // wei must be specified (in these non_unit_tests the key is hardcoded and + // wei must be specified (in these tests the key is hardcoded and // corresponds to the path m/44'/60'/0'/0/0/1 that belongs to // the consuming Node). // Specify number of wei this account should possess at its initialisation diff --git a/node/src/accountant/db_access_objects/receivable_dao.rs b/node/src/accountant/db_access_objects/receivable_dao.rs index 4187a2fb9..dd24fb91a 100644 --- a/node/src/accountant/db_access_objects/receivable_dao.rs +++ b/node/src/accountant/db_access_objects/receivable_dao.rs @@ -79,7 +79,7 @@ pub trait ReceivableDao: Send { fn total(&self) -> i128; - // Test-only-like method but because of share with multi-node non_unit_tests #[cfg(test)] is disallowed + // Test-only-like method but because of share with multi-node tests #[cfg(test)] is disallowed fn account_status(&self, wallet: &Wallet) -> Option; as_any_in_trait!(); diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 6253415ac..4c83d0664 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -153,27 +153,27 @@ impl QualifiedPayableAccount { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AnalyzedPayableAccount { pub qualified_as: QualifiedPayableAccount, - pub disqualification_limit: u128, + pub disqualification_limit_minor: u128, } impl AnalyzedPayableAccount { - pub fn new(qualified_as: QualifiedPayableAccount, disqualification_limit: u128) -> Self { + pub fn new(qualified_as: QualifiedPayableAccount, disqualification_limit_minor: u128) -> Self { AnalyzedPayableAccount { qualified_as, - disqualification_limit, + disqualification_limit_minor, } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct CreditorThresholds { - pub permanent_debt_allowed_wei: u128, + pub permanent_debt_allowed_minor: u128, } impl CreditorThresholds { pub fn new(permanent_debt_allowed_wei: u128) -> Self { Self { - permanent_debt_allowed_wei, + permanent_debt_allowed_minor: permanent_debt_allowed_wei, } } } @@ -4927,7 +4927,7 @@ pub mod exportable_test_parts { let assertion_msg = AssertionsMessage { assertions: Box::new(|accountant: &mut Accountant| { // Will crash a test if our user-defined SQLite fns have been unreachable; - // We cannot rely on failures in the DAO non_unit_tests, because Account's database connection + // We cannot rely on failures in the DAO tests, because Account's database connection // has to be set up specially first (we teach it about the extra functions) as we're // creating the actor diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 1f8d2aac0..cea75cbf1 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -69,11 +69,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { weighted_accounts: Vec, ) -> Self::ReturnType { let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { - weighted_account - .analyzed_account - .qualified_as - .bare_account - .balance_wei + weighted_account.balance_minor() }); let unallocated_cw_balance = payment_adjuster @@ -82,11 +78,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { if check_sum <= unallocated_cw_balance { // Fast return after a direct conversion into the expected type - let disqualification_arbiter = &payment_adjuster.disqualification_arbiter; - return ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( - weighted_accounts, - disqualification_arbiter, - ); + todo!() } payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 3fce31000..9ce870525 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -36,7 +36,7 @@ impl DisqualificationArbiter { let intercept = qualified_payable.payment_threshold_intercept_minor; let permanent_debt_allowed = qualified_payable .creditor_thresholds - .permanent_debt_allowed_wei; + .permanent_debt_allowed_minor; self.disqualification_gauge .determine_limit(balance, intercept, permanent_debt_allowed) @@ -81,7 +81,7 @@ impl DisqualificationArbiter { unconfirmed_adjustments .iter() .flat_map(|adjustment_info| { - let disqualification_limit = adjustment_info.disqualification_limit_minor; + let disqualification_limit = adjustment_info.disqualification_limit_minor(); let proposed_adjusted_balance = adjustment_info.proposed_adjusted_balance_minor; if proposed_adjusted_balance < disqualification_limit { @@ -137,7 +137,7 @@ impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> wallet: unconfirmed_account.wallet(), weight: unconfirmed_account.weighted_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, - disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor, + disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor(), } } } @@ -375,7 +375,7 @@ mod tests { fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit() { let mut account = make_non_guaranteed_unconfirmed_adjustment(444); account.proposed_adjusted_balance_minor = 1_000_000_000; - account.disqualification_limit_minor = 1_000_000_000; + account.weighted_account.analyzed_account.disqualification_limit_minor = 1_000_000_000; let accounts = vec![account]; let result = @@ -459,19 +459,18 @@ mod tests { make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); let analyzed_accounts = convert_collection(qualified_payables); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); - let subject = make_initialized_subject( + let payment_adjuster = make_initialized_subject( Some(now), Some(cw_service_fee_balance_minor), None, Some(largest_exceeding_balance), None, ); - let weights_and_accounts = subject.calculate_weights(analyzed_accounts); + let weights_and_accounts = payment_adjuster.calculate_weights(analyzed_accounts); let subject = DisqualificationArbiter::default(); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments( weights_and_accounts, - &subject, cw_service_fee_balance_minor, ); diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 897c30c0e..40e5f9561 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -103,21 +103,10 @@ pub mod ordinary_diagnostic_functions { pub fn outweighed_accounts_diagnostics(account_info: &UnconfirmedAdjustment) { diagnostics!( - &account_info - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .wallet, + &account_info.wallet(), "OUTWEIGHED ACCOUNT FOUND", "Original balance: {}, proposed balance: {}", - account_info - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .balance_wei - .separate_with_commas(), + account_info.balance_minor().separate_with_commas(), account_info .proposed_adjusted_balance_minor .separate_with_commas() @@ -130,12 +119,7 @@ pub mod ordinary_diagnostic_functions { disqualification_edge: u128, ) { diagnostics!( - account_info - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .wallet, + account_info.wallet(), "ACCOUNT NOMINATED FOR DISQUALIFICATION FOR INSIGNIFICANCE AFTER ADJUSTMENT", "Proposed: {}, disqualification limit: {}", proposed_adjusted_balance.separate_with_commas(), @@ -148,11 +132,7 @@ pub mod ordinary_diagnostic_functions { disqualification_limit: u128, ) { diagnostics!( - weighted_account - .analyzed_account - .qualified_as - .bare_account - .wallet, + weighted_account.wallet(), "MINIMAL ACCEPTABLE BALANCE ASSIGNED", "Used disqualification limit for given account {}", disqualification_limit.separate_with_commas() @@ -165,7 +145,7 @@ pub mod ordinary_diagnostic_functions { disqualification_limit_opt: Option, ) { diagnostics!( - account.analyzed_account.qualified_as.bare_account.wallet, + account.wallet(), "HANDLING LAST ACCOUNT", "Remaining CW balance {} is {}", cw_service_fee_balance_minor, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 97899be3a..1242d6dd5 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -13,6 +13,14 @@ impl From for PayableAccount { } } +// After transaction fee adjustment while no need to go off with the other fee, and so we want to +// drop the weights etc. +impl From for PayableAccount { + fn from(weighted_account: WeightedPayable) -> Self { + todo!() + } +} + impl From for PayableAccount { fn from(non_finalized_adjustment: AdjustedAccountBeforeFinalization) -> Self { let mut account = non_finalized_adjustment.original_account; @@ -49,11 +57,7 @@ impl From for AdjustedAccountBeforeFinalization { // they requested impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { - let proposed_adjusted_balance_minor = weighted_account - .analyzed_account - .qualified_as - .bare_account - .balance_wei; + let proposed_adjusted_balance_minor = weighted_account.balance_minor(); let original_account = weighted_account.analyzed_account.qualified_as.bare_account; AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) @@ -113,7 +117,7 @@ mod tests { original_payable_account.balance_wei = 200_000_000; let weighted_account = prepare_weighted_account(original_payable_account.clone()); let unconfirmed_adjustment = - UnconfirmedAdjustment::new(weighted_account, 111_222_333, 100_000_000); + UnconfirmedAdjustment::new(weighted_account, 111_222_333); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 0b2fac203..f42d1297d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,6 +1,8 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; +use crate::accountant::payment_adjuster::preparatory_analyser::BalanceProvidingAccount; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; use web3::types::U256; @@ -82,34 +84,30 @@ impl AdjustedAccountBeforeFinalization { #[derive(Debug, PartialEq, Eq, Clone)] pub struct UnconfirmedAdjustment { pub weighted_account: WeightedPayable, - pub proposed_adjusted_balance_minor: u128, - pub disqualification_limit_minor: u128, + pub proposed_adjusted_balance_minor: u128 } impl UnconfirmedAdjustment { pub fn new( weighted_account: WeightedPayable, - proposed_adjusted_balance_minor: u128, - disqualification_limit_minor: u128, + proposed_adjusted_balance_minor: u128 ) -> Self { Self { weighted_account, - proposed_adjusted_balance_minor, - disqualification_limit_minor, + proposed_adjusted_balance_minor } } pub fn wallet(&self) -> &Wallet { - &self - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .wallet + &self.weighted_account.wallet() } pub fn balance_minor(&self) -> u128 { - todo!() + self.weighted_account.balance_minor() + } + + pub fn disqualification_limit_minor(&self)->u128{ + self.weighted_account.analyzed_account.disqualification_limit_minor } } @@ -127,16 +125,47 @@ impl TransactionCountsWithin16bits { } } -pub struct AccountsEliminatedByTxFeeInfo { - pub count: usize, - pub sum_of_balances: u128, +#[derive(Debug)] +pub enum ServiceFeeCheckErrorContext { + TransactionFeeLimitationInspectionResult { + limitation_opt: Option, + }, + TransactionFeeAccountsDumpPerformed { + original_tx_count: usize, + original_sum_of_service_fee_balances: u128, + }, +} + +impl ServiceFeeCheckErrorContext { + pub fn new_dump_context(analyzed_accounts: &[WeightedPayable]) -> Self { + let original_tx_count = analyzed_accounts.len(); + let original_sum_of_service_fee_balances = + sum_as(analyzed_accounts, |account| account.balance_minor()); + Self::TransactionFeeAccountsDumpPerformed { + original_tx_count, + original_sum_of_service_fee_balances, + } + } + + pub fn new_detection_context(limitation_opt: Option) -> Self { + Self::TransactionFeeLimitationInspectionResult { limitation_opt } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct TransactionFeeLimitation { + pub affordable_tx_count: u16, + pub actual_balance: U256, + pub required_balance: U256, } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsWithin16bits, + AdjustedAccountBeforeFinalization, RecursionResults, ServiceFeeCheckErrorContext, + TransactionCountsWithin16bits, TransactionFeeLimitation, }; + use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::make_payable_account; use ethereum_types::U256; @@ -179,7 +208,7 @@ mod tests { let corrections_from_u16_max = [-3_i8, -1, 0, 1, 10]; let result = corrections_from_u16_max .into_iter() - .map(|correction| plus_minus_correction_of_u16_max(correction)) + .map(plus_minus_correction_for_u16_max) .map(U256::from) .map(|max_possible_tx_count| { let detected_tx_counts = @@ -199,7 +228,7 @@ mod tests { let corrections_from_u16_max = [-9_i8, -1, 0, 1, 5]; let result = corrections_from_u16_max .into_iter() - .map(|correction| plus_minus_correction_of_u16_max(correction)) + .map(plus_minus_correction_for_u16_max) .map(|required_tx_count_usize| { let detected_tx_counts = TransactionCountsWithin16bits::new(U256::from(123), required_tx_count_usize); @@ -213,11 +242,41 @@ mod tests { ) } - fn plus_minus_correction_of_u16_max(correction: i8) -> usize { + fn plus_minus_correction_for_u16_max(correction: i8) -> usize { if correction < 0 { (u16::MAX - correction.abs() as u16) as usize } else { u16::MAX as usize + correction as usize } } + + #[test] + fn construction_of_error_context_for_accounts_dump_works() { + let mut account_1 = make_weighed_account(123); + account_1 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = 1234567; + let mut account_2 = make_weighed_account(345); + account_2 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = 999888777; + let weighted_accounts = vec![account_1, account_2]; + + let dump_performed = ServiceFeeCheckErrorContext::new_dump_context(&weighted_accounts); + + match dump_performed { + ServiceFeeCheckErrorContext::TransactionFeeAccountsDumpPerformed { + original_tx_count, + original_sum_of_service_fee_balances, + } => { + assert_eq!(original_tx_count, 2); + assert_eq!(original_sum_of_service_fee_balances, 1234567 + 999888777) + } + x => panic!("We expected version for accounts dump but got: {:?}", x), + } + } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index dafdae2f9..f7f803a50 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -5,7 +5,7 @@ use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AccountsEliminatedByTxFeeInfo, AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable}; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use itertools::{Either, Itertools}; @@ -35,40 +35,21 @@ pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { pub fn dump_unaffordable_accounts_by_transaction_fee( weighted_accounts_in_descending_order: Vec, affordable_transaction_count: u16, -) -> (Vec, AccountsEliminatedByTxFeeInfo) { - let to_be_dumped = weighted_accounts_in_descending_order - .iter() - .skip(affordable_transaction_count as usize) - .collect::>(); - let elimination_info = { - let count = to_be_dumped.len(); - let sum_of_balances = sum_as(&to_be_dumped, |account| { - account - .analyzed_account - .qualified_as - .bare_account - .balance_wei - }); - AccountsEliminatedByTxFeeInfo { - count, - sum_of_balances, - } - }; - +) -> Vec { diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "Keeping {} out of {} accounts. Dumping these accounts: {:?}", affordable_transaction_count, weighted_accounts_in_descending_order.len(), - to_be_dumped + weighted_accounts_in_descending_order + .iter() + .skip(affordable_transaction_count as usize) ); - let kept_accounts = weighted_accounts_in_descending_order + weighted_accounts_in_descending_order .into_iter() .take(affordable_transaction_count as usize) - .collect(); - - (kept_accounts, elimination_info) + .collect() } pub fn compute_mul_coefficient_preventing_fractional_numbers( @@ -208,15 +189,6 @@ pub fn sort_in_descendant_order_by_weights( .collect() } -pub fn drop_no_longer_needed_weights_away_from_accounts( - weights_and_accounts: Vec, -) -> Vec { - weights_and_accounts - .into_iter() - .map(|weighted_account| weighted_account.analyzed_account.qualified_as.bare_account) - .collect() -} - #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 75e4e3860..aab15f16c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -34,12 +34,9 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ LowGainingAccountEliminated, SomeAccountsProcessed, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, - UnconfirmedAdjustment, WeightedPayable, -}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, ServiceFeeCheckErrorContext, TransactionFeeLimitation, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - drop_no_longer_needed_weights_away_from_accounts, dump_unaffordable_accounts_by_transaction_fee, + dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, sort_in_descendant_order_by_weights, sum_as, zero_affordable_accounts_found, }; @@ -57,10 +54,10 @@ use itertools::Either; use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; -use std::fmt::Alignment::Left; use std::time::SystemTime; use thousands::Separable; use web3::types::U256; +use masq_lib::utils::convert_collection; pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( @@ -96,34 +93,37 @@ impl PaymentAdjuster for PaymentAdjusterReal { { let number_of_counts = qualified_payables.len(); - match self + let transaction_fee_limitation_opt = match self .analyzer .determine_transaction_count_limit_by_transaction_fee( agent, number_of_counts, &self.logger, ) { - Ok(None) => (), - Ok(Some(affordable_transaction_count)) => { - return Ok(Either::Right(AdjustmentAnalysis::new( - Adjustment::TransactionFeeInPriority { - affordable_transaction_count, - }, - todo!(), - ))) - } + Ok(detected_limitation_opt) => detected_limitation_opt, Err(e) => return Err(e), }; + let service_fee_check_error_context = + ServiceFeeCheckErrorContext::new_detection_context(transaction_fee_limitation_opt); let service_fee_balance_minor = agent.service_fee_balance_minor(); - self.analyzer + let service_fee_check_outcome = match self.analyzer .check_need_of_adjustment_by_service_fee::( &self.disqualification_arbiter, - None, + service_fee_check_error_context, qualified_payables, service_fee_balance_minor, &self.logger, - ) + ){ + Ok(check_outcome) => check_outcome, + Err(e) => todo!() + }; + + match (transaction_fee_limitation_opt, service_fee_check_outcome) { + (None, Either::Left(unlimited_payables)) => todo!(), + (None, Either::Right(adjustment_analysis)) => todo!(), + (Some(transaction_fee_limitation), _) => todo!(), + } } fn adjust_payments( @@ -256,7 +256,10 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let (weighted_accounts_affordable_by_transaction_fee, elimination_info) = + let service_fee_error_context = + ServiceFeeCheckErrorContext::new_dump_context(&weighted_accounts_in_descending_order); + + let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( weighted_accounts_in_descending_order, already_known_affordable_transaction_count, @@ -264,9 +267,9 @@ impl PaymentAdjusterReal { let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); - let check_result = match self.analyzer.check_need_of_adjustment_by_service_fee( + let check_outcome = match self.analyzer.check_need_of_adjustment_by_service_fee( &self.disqualification_arbiter, - Some(elimination_info), + service_fee_error_context, weighted_accounts_affordable_by_transaction_fee, cw_service_fee_balance, &self.logger, @@ -278,7 +281,7 @@ impl PaymentAdjusterReal { } }; - match check_result { + match check_outcome { Either::Left(weighted_accounts_needing_adjustment) => { diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); @@ -289,9 +292,7 @@ impl PaymentAdjusterReal { Either::Right(weighted_accounts) => { let accounts_not_needing_adjustment = //TODO you should use from impl - drop_no_longer_needed_weights_away_from_accounts( - weighted_accounts, - ); + convert_collection(weighted_accounts); Ok(Either::Right(accounts_not_needing_adjustment)) } } @@ -388,6 +389,8 @@ impl PaymentAdjusterReal { WeightedPayable::new(payable, weight) }); + //TODO take this and move it before the tx fee cut-off; then go and tide up + //the awkward variables referring to the descending order for no good reason sort_in_descendant_order_by_weights(weighted_accounts) } @@ -473,6 +476,7 @@ pub enum PaymentAdjusterError { number_of_accounts: usize, total_amount_demanded_minor: u128, cw_service_fee_balance_minor: u128, + appendix_opt: Option, }, AllAccountsEliminated, } @@ -497,6 +501,7 @@ impl Display for PaymentAdjusterError { number_of_accounts, total_amount_demanded_minor, cw_service_fee_balance_minor, + appendix_opt, } => write!( f, "Found a smaller service fee balance than it does for a single payment. \ @@ -531,7 +536,8 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DecidedAccounts, - UnconfirmedAdjustment, WeightedPayable, + ServiceFeeCheckErrorContext, TransactionFeeLimitation, UnconfirmedAdjustment, + WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, weights_total, @@ -541,9 +547,9 @@ mod tests { }; use crate::accountant::payment_adjuster::test_utils::{ make_analyzed_account_by_wallet, make_extreme_payables, make_initialized_subject, - make_qualified_payable_by_wallet, multiple_by_billion, CriterionCalculatorMock, - DisqualificationGaugeMock, ServiceFeeAdjusterMock, - MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, + multiple_by_billion, CriterionCalculatorMock, DisqualificationGaugeMock, + ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, + PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -798,7 +804,8 @@ mod tests { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, total_amount_demanded_minor: 920_000_000_000, - cw_service_fee_balance_minor + cw_service_fee_balance_minor, + appendix_opt: None, } ) ); @@ -844,10 +851,28 @@ mod tests { number_of_accounts: 5, total_amount_demanded_minor: 6_000_000_000, cw_service_fee_balance_minor: 333_000_000, + appendix_opt: None, }, "Found a smaller service fee balance than it does for a single payment. \ - Number of canceled payments: 5. Total amount demanded: 6,000,000,000 wei. \ - Consuming wallet balance: 333,000,000 wei", + Number of canceled payments: 5. Total amount required: 6,000,000,000 wei, + while in wallet: 333,000,000 wei", + ), + ( + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: 5, + total_amount_demanded_minor: 7_000_000_000, + cw_service_fee_balance_minor: 100_000_000, + appendix_opt: Some(TransactionFeeLimitation { + affordable_tx_count: 3, + actual_balance: U256::from(3_000_000_000_u64), + required_balance: U256::from(5_000_000_000_u64), + }), + }, + "Both transaction fee and service fee balances are not high enough. Number of \ + payments considered: 5. Current transaction fee balance allows maximally 3 payments + for 5,000,000,000 wei required against the actual 3,000,000,000 wei. However, the + service fee balance does not allow even a single payment. Total amount required: + 7,000,000,000 wei, while in wallet: 100,000,000 wei", ), ( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { @@ -856,9 +881,8 @@ mod tests { cw_transaction_fee_balance_minor: U256::from(90_000), }, "Found a smaller transaction fee balance than it does for a single \ - payment. Number of canceled payments: 4. Transaction fee by single \ - account: 70,000,000,000,000 wei. Consuming wallet balance: 90,000 \ - wei", + payment. Number of canceled payments: 4. Transaction fee required \ + 140,000,000,000,000 wei, while in wallet: 90,000 wei", ), ( PaymentAdjusterError::AllAccountsEliminated, @@ -919,13 +943,13 @@ mod tests { let balance_1 = multiple_by_billion(3_000_000); let disqualification_limit_1 = multiple_by_billion(2_300_000); account_1.qualified_as.bare_account.balance_wei = balance_1; - account_1.disqualification_limit = disqualification_limit_1; + account_1.disqualification_limit_minor = disqualification_limit_1; let mut account_2 = make_analyzed_account_by_wallet("def"); let wallet_2 = account_2.qualified_as.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(1_000_000); let disqualification_limit_2 = multiple_by_billion(800_000); account_2.qualified_as.bare_account.balance_wei = balance_2; - account_2.disqualification_limit = disqualification_limit_2; + account_2.disqualification_limit_minor = disqualification_limit_2; let largest_exceeding_balance = (balance_1 - account_1.qualified_as.payment_threshold_intercept_minor) .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); @@ -1011,15 +1035,9 @@ mod tests { cw_service_fee_balance_minor, garbage_largest_exceeding_balance_recently_qualified, )); - let disqualification_gauge_mock = DisqualificationGaugeMock::default() - .determine_limit_result(0) - .determine_limit_result(0); - let garbage_disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge_mock)); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments( weighted_accounts, - &garbage_disqualification_arbiter, cw_service_fee_balance_minor, ); // The results are sorted from the biggest weights down @@ -1082,9 +1100,13 @@ mod tests { let agent_id_stamp = ArbitraryIdStamp::new(); let service_fee_balance_in_minor_units = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; + let service_fee_error_context = + ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { + limitation_opt: None, + }; let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( disqualification_arbiter, - None, + service_fee_error_context, qualified_payables, service_fee_balance_in_minor_units, &subject.logger, @@ -1278,7 +1300,7 @@ mod tests { // This function should take just such essential args as balances and those that play rather // a secondary role, yet an important one in the verification processes for proposed adjusted // balances. Refrain from employing more of the weights-affecting parameters as they would - // only burden us with their consideration in these non_unit_tests. + // only burden us with their consideration in these tests. fn make_plucked_qualified_account( wallet_addr_fragment: &str, balance_minor: u128, @@ -1711,7 +1733,8 @@ mod tests { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, total_amount_demanded_minor: balance_1 + balance_2 + balance_3, - cw_service_fee_balance_minor: service_fee_balance_in_minor_units + cw_service_fee_balance_minor: service_fee_balance_in_minor_units, + appendix_opt: None, } ); TestLogHandler::new().exists_log_containing(&format!( @@ -1828,7 +1851,7 @@ mod tests { bare_account: payable, payment_threshold_intercept_minor: (balance / 10) * 7, creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: (balance / 10) * 7, + permanent_debt_allowed_minor: (balance / 10) * 7, }, } }) @@ -1856,7 +1879,7 @@ mod tests { Box::new(blockchain_agent) } - // The following non_unit_tests together prove the use of correct calculators in the production code + // The following tests together prove the use of correct calculators in the production code #[test] fn each_of_defaulted_calculators_returns_different_value() { @@ -1935,6 +1958,7 @@ mod tests { assertion_value: AssertionValue, } + //TODO implement this in struct AssertionValue { wallet_to_match_result_with: Wallet, expected_computed_weight: u128, diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 29fad89fb..f4c48c1fe 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -321,7 +321,7 @@ fn render_results_to_file_and_attempt_basic_assertions( number_of_requested_scenarios: usize, overall_output_collector: TestOverallOutputCollector, ) { - let file_dir = ensure_node_home_directory_exists("payment_adjuster", "non_unit_tests"); + let file_dir = ensure_node_home_directory_exists("payment_adjuster", "tests"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); introduction(&mut file); let test_overall_output_collector = diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 1522b699f..1dcb19751 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -5,7 +5,8 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AccountsEliminatedByTxFeeInfo, TransactionCountsWithin16bits, WeightedPayable, + ServiceFeeCheckErrorContext, TransactionCountsWithin16bits, TransactionFeeLimitation, + WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; @@ -14,6 +15,7 @@ use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; use itertools::{Either, Product}; use masq_lib::logger::Logger; +use std::cmp::Ordering; pub struct PreparatoryAnalyzer {} @@ -27,7 +29,7 @@ impl PreparatoryAnalyzer { agent: &dyn BlockchainAgent, number_of_qualified_accounts: usize, logger: &Logger, - ) -> Result, PaymentAdjusterError> { + ) -> Result, PaymentAdjusterError> { let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction_minor(); @@ -58,7 +60,8 @@ impl PreparatoryAnalyzer { cw_transaction_fee_balance_minor, max_tx_count_we_can_afford_u16, ); - Ok(Some(max_tx_count_we_can_afford_u16)) + let limitation = todo!(); + Ok(Some(limitation)) } } @@ -88,27 +91,35 @@ impl PreparatoryAnalyzer { >( &self, disqualification_arbiter: &DisqualificationArbiter, - accounts_eliminated_by_tx_fee_info_opt: Option, + error_context: ServiceFeeCheckErrorContext, payables: Vec, cw_service_fee_balance_minor: u128, logger: &Logger, ) -> Result, AdjustmentNeededValue>, PaymentAdjusterError> where IncomingAccount: DisqualificationAnalysableAccount, - Vec: - ReturnedFromServiceFeeCheck, + MidProduct: BalanceProvidingAccount, + Vec: ProcessableReturnTypeFromServiceFeeCheck< + AdjustmentNeededReturnValue = AdjustmentNeededValue, + >, { let required_service_fee_total = Self::compute_total_of_service_fee_required::(&payables); if cw_service_fee_balance_minor >= required_service_fee_total { - Ok(Either::Left(payables)) + if matches!( + error_context, + ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { .. } + ) { + todo!() + } else { + Ok(Either::Left(payables)) + } } else { let result = self .analyse_smallest_adjustment_possibility::( disqualification_arbiter, - accounts_eliminated_by_tx_fee_info_opt, - required_service_fee_total, + error_context, payables, cw_service_fee_balance_minor, )?; @@ -119,13 +130,14 @@ impl PreparatoryAnalyzer { cw_service_fee_balance_minor, ); - Ok(Either::Right(result.failing_check_output())) + Ok(Either::Right(result.prepare_return())) } } fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 where Account: DisqualificationAnalysableAccount, + Product: BalanceProvidingAccount, { sum_as(payables, |account| account.balance_minor()) } @@ -136,8 +148,22 @@ impl PreparatoryAnalyzer { ) -> (u128, Vec) where Account: DisqualificationAnalysableAccount, + Product: BalanceProvidingAccount, { - todo!() + accounts.into_iter().fold( + (u128::MAX, vec![]), + |(min_dsq_limit, mut analyzed_accounts), current| { + let (current_dsq_limit, analyzed_account) = + current.analyse_limit(disqualification_arbiter); + let next_min_dsq_limit = match min_dsq_limit.cmp(¤t_dsq_limit) { + Ordering::Less => min_dsq_limit, + Ordering::Equal => min_dsq_limit, + Ordering::Greater => current_dsq_limit, + }; + analyzed_accounts.push(analyzed_account); + (next_min_dsq_limit, analyzed_accounts) + }, + ) } // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with @@ -147,13 +173,13 @@ impl PreparatoryAnalyzer { fn analyse_smallest_adjustment_possibility( &self, disqualification_arbiter: &DisqualificationArbiter, - accounts_eliminated_by_tx_fee_info_opt: Option, - required_service_fee_total: u128, + error_context: ServiceFeeCheckErrorContext, accounts: Vec, cw_service_fee_balance_minor: u128, ) -> Result, PaymentAdjusterError> where Account: DisqualificationAnalysableAccount, + Product: BalanceProvidingAccount, //TODO is this necessary? { let (lowest_disqualification_limit, prepared_accounts) = Self::find_smallest_weight_and_prepare_accounts_to_proceed( @@ -164,39 +190,48 @@ impl PreparatoryAnalyzer { if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(prepared_accounts) } else { - let (number_of_accounts, total_amount_demanded_minor) = - if let Some(info) = accounts_eliminated_by_tx_fee_info_opt { - ( - prepared_accounts.len() + info.count, - required_service_fee_total + info.sum_of_balances, - ) - } else { - (prepared_accounts.len(), required_service_fee_total) - }; + let (number_of_accounts, total_amount_demanded_minor, transaction_fee_appendix_opt): ( + usize, + u128, + Option, + ) = match error_context { + ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { + limitation_opt: limitation, + } => todo!(), + ServiceFeeCheckErrorContext::TransactionFeeAccountsDumpPerformed { + original_tx_count, + original_sum_of_service_fee_balances, + } => todo!(), + }; + + // if let Some(info) = summary_for_potential_error_opt { + // (info.count, info.sum_of_balances) + // } else { + // let required_service_fee_total = + // sum_as(&prepared_accounts, |account| account.balance_minor()); + // (prepared_accounts.len(), required_service_fee_total) + // }; Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts, total_amount_demanded_minor, cw_service_fee_balance_minor, + appendix_opt: transaction_fee_appendix_opt, }, ) } } - - fn find_lowest_dsq_limit( - qualified_payables: &[&QualifiedPayableAccount], - disqualification_arbiter: &DisqualificationArbiter, - ) -> u128 { - qualified_payables - .iter() - .map(|account| disqualification_arbiter.calculate_disqualification_edge(account)) - .fold(u128::MAX, |lowest_so_far, limit| lowest_so_far.min(limit)) - } } -pub trait DisqualificationAnalysableAccount { +pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount +where + Product: BalanceProvidingAccount, +{ // fn process_findings(insufficiency_found: bool)-> - fn analyse_limit(self, disqualification_arbiter: &DisqualificationArbiter) -> (Product, u128); + fn analyse_limit(self, disqualification_arbiter: &DisqualificationArbiter) -> (u128, Product); +} + +pub trait BalanceProvidingAccount { fn balance_minor(&self) -> u128; } @@ -204,46 +239,58 @@ impl DisqualificationAnalysableAccount for QualifiedPaya fn analyse_limit( self, disqualification_arbiter: &DisqualificationArbiter, - ) -> (AnalyzedPayableAccount, u128) { - todo!() + ) -> (u128, AnalyzedPayableAccount) { + let dsq_limit = disqualification_arbiter.calculate_disqualification_edge(&self); + let analyzed_account = AnalyzedPayableAccount::new(self, dsq_limit); + (dsq_limit, analyzed_account) } +} +impl BalanceProvidingAccount for QualifiedPayableAccount { fn balance_minor(&self) -> u128 { self.bare_account.balance_wei } } +impl BalanceProvidingAccount for AnalyzedPayableAccount { + fn balance_minor(&self) -> u128 { + todo!() + } +} + impl DisqualificationAnalysableAccount for WeightedPayable { fn analyse_limit( self, - disqualification_arbiter: &DisqualificationArbiter, - ) -> (WeightedPayable, u128) { - todo!() + _disqualification_arbiter: &DisqualificationArbiter, + ) -> (u128, WeightedPayable) { + (self.analyzed_account.disqualification_limit_minor, self) } +} +impl BalanceProvidingAccount for WeightedPayable { fn balance_minor(&self) -> u128 { self.analyzed_account.qualified_as.bare_account.balance_wei } } -pub trait ReturnedFromServiceFeeCheck { +pub trait ProcessableReturnTypeFromServiceFeeCheck { type AdjustmentNeededReturnValue; - fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue; + fn prepare_return(self) -> Self::AdjustmentNeededReturnValue; } -impl ReturnedFromServiceFeeCheck for Vec { +impl ProcessableReturnTypeFromServiceFeeCheck for Vec { type AdjustmentNeededReturnValue = AdjustmentAnalysis; - fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue { + fn prepare_return(self) -> Self::AdjustmentNeededReturnValue { todo!() } } -impl ReturnedFromServiceFeeCheck for Vec { +impl ProcessableReturnTypeFromServiceFeeCheck for Vec { type AdjustmentNeededReturnValue = Vec; - fn failing_check_output(self) -> Self::AdjustmentNeededReturnValue { + fn prepare_return(self) -> Self::AdjustmentNeededReturnValue { todo!() } } @@ -251,43 +298,18 @@ impl ReturnedFromServiceFeeCheck for Vec { #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::ServiceFeeCheckErrorContext; use crate::accountant::payment_adjuster::preparatory_analyser::{ - DisqualificationAnalysableAccount, PreparatoryAnalyzer, + PreparatoryAnalyzer, }; - use crate::accountant::payment_adjuster::test_utils::DisqualificationGaugeMock; + use crate::accountant::payment_adjuster::test_utils::{multiple_by_billion, DisqualificationGaugeMock, make_weighed_account}; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use crate::accountant::QualifiedPayableAccount; + use crate::accountant::{QualifiedPayableAccount}; use masq_lib::utils::convert_collection; use std::sync::{Arc, Mutex}; - #[test] - fn find_lowest_dsq_limit_begins_at_u128_max() { - let disqualification_arbiter = DisqualificationArbiter::default(); - let result = PreparatoryAnalyzer::find_lowest_dsq_limit(&[], &disqualification_arbiter); - - assert_eq!(result, u128::MAX) - } - - #[test] - fn find_lowest_dsq_limit_when_multiple_accounts_with_the_same_limit() { - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(200_000_000) - .determine_limit_result(200_000_000); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let account_1 = make_non_guaranteed_qualified_payable(111); - let account_2 = make_non_guaranteed_qualified_payable(222); - let accounts = vec![&account_1, &account_2]; - - let result = - PreparatoryAnalyzer::find_lowest_dsq_limit(&accounts, &disqualification_arbiter); - - assert_eq!(result, 200_000_000) - } - - fn test_body_for_adjustment_possibility_nearly_rejected( + fn test_adjustment_possibility_nearly_rejected( disqualification_gauge: DisqualificationGaugeMock, original_accounts: [QualifiedPayableAccount; 2], cw_service_fee_balance: u128, @@ -298,13 +320,11 @@ mod tests { let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; - let required_service_fee_total = sum_as(original_accounts.as_slice(), |account| { - account.balance_minor() - }); + let service_fee_error_context = ServiceFeeCheckErrorContext::new_dump_context(&vec![]); + let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, - None, - required_service_fee_total, + service_fee_error_context, original_accounts.clone().to_vec(), cw_service_fee_balance, ); @@ -314,20 +334,21 @@ mod tests { let determine_limit_params = determine_limit_params_arc.lock().unwrap(); let account_1 = &original_accounts[0]; let account_2 = &original_accounts[1]; + let expected_params = vec![ + ( + account_1.bare_account.balance_wei, + account_1.payment_threshold_intercept_minor, + account_1.creditor_thresholds.permanent_debt_allowed_minor + ), + ( + account_2.bare_account.balance_wei, + account_2.payment_threshold_intercept_minor, + account_2.creditor_thresholds.permanent_debt_allowed_minor + ) + ]; assert_eq!( *determine_limit_params, - vec![ - ( - account_1.bare_account.balance_wei, - account_1.payment_threshold_intercept_minor, - account_1.creditor_thresholds.permanent_debt_allowed_wei - ), - ( - account_2.bare_account.balance_wei, - account_2.payment_threshold_intercept_minor, - account_2.creditor_thresholds.permanent_debt_allowed_wei - ) - ] + expected_params ) } @@ -343,7 +364,7 @@ mod tests { .determine_limit_result(1_500_000_000); let original_accounts = [account_1, account_2]; - test_body_for_adjustment_possibility_nearly_rejected( + test_adjustment_possibility_nearly_rejected( disqualification_gauge, original_accounts, cw_service_fee_balance, @@ -362,7 +383,7 @@ mod tests { .determine_limit_result(750_000_000); let original_accounts = [account_1, account_2]; - test_body_for_adjustment_possibility_nearly_rejected( + test_adjustment_possibility_nearly_rejected( disqualification_gauge, original_accounts, cw_service_fee_balance, @@ -370,7 +391,7 @@ mod tests { } #[test] - fn adjustment_possibility_err_from_insufficient_balance_for_even_the_least_demanding_account() { + fn insufficient_balance_for_even_the_least_demanding_account_causes_error() { let mut account_1 = make_non_guaranteed_qualified_payable(111); account_1.bare_account.balance_wei = 2_000_000_000; let mut account_2 = make_non_guaranteed_qualified_payable(222); @@ -386,12 +407,15 @@ mod tests { .determine_limit_result(1_000_000_222); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let service_fee_check_error_context = + ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { + limitation_opt: None, + }; let subject = PreparatoryAnalyzer {}; let result = subject.analyse_smallest_adjustment_possibility( &disqualification_arbiter, - None, - required_fee_total, + service_fee_check_error_context, original_accounts, cw_service_fee_balance, ); @@ -402,9 +426,65 @@ mod tests { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, total_amount_demanded_minor: required_fee_total, - cw_service_fee_balance_minor: cw_service_fee_balance + cw_service_fee_balance_minor: cw_service_fee_balance, + appendix_opt: None, } ) ) } + + #[test] + fn accounts_analyzing_works_even_for_weighted_payable() { + let disqualification_limit_1 = multiple_by_billion(300_000) + 2; + let mut weighted_account_1 = make_weighed_account(123); + weighted_account_1.analyzed_account.disqualification_limit_minor = disqualification_limit_1; + let disqualification_limit_2 = multiple_by_billion(300_000) + 2; + let mut weighted_account_2 = make_weighed_account(456); + weighted_account_2.analyzed_account.disqualification_limit_minor = disqualification_limit_2; + let disqualification_limit_3 = multiple_by_billion(300_000) + 2; + let mut weighted_account_3 = make_weighed_account(789); + weighted_account_3.analyzed_account.disqualification_limit_minor = disqualification_limit_3; + let disqualification_limit_4 = disqualification_limit_3; + let mut weighted_account_4 = make_weighed_account(789); + weighted_account_4.analyzed_account.disqualification_limit_minor = disqualification_limit_4; + let accounts = vec![ + weighted_account_1.clone(), + weighted_account_2.clone(), + weighted_account_3.clone(), + weighted_account_4.clone(), + ]; + let disqualification_gauge = DisqualificationGaugeMock::default(); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + + let (minimal_disqualification_limit, analyzed_accounts) = + PreparatoryAnalyzer::find_smallest_weight_and_prepare_accounts_to_proceed( + accounts, + &disqualification_arbiter, + ); + + assert_eq!(minimal_disqualification_limit, disqualification_limit_2); + let expected_analyzed_accounts = vec![ + weighted_account_1, + weighted_account_2, + weighted_account_3, + weighted_account_4, + ]; + assert_eq!(analyzed_accounts, expected_analyzed_accounts) + } + + #[test] + fn fold_for_find_smallest_weight_and_prepare_accounts_to_proceed_starts_with_u128_max() { + let disqualification_arbiter = DisqualificationArbiter::default(); + let accounts: Vec = vec![]; + + let (minimal_disqualification_limit, analyzed_accounts) = + PreparatoryAnalyzer::find_smallest_weight_and_prepare_accounts_to_proceed( + accounts, + &disqualification_arbiter, + ); + + assert_eq!(minimal_disqualification_limit, u128::MAX); + assert_eq!(analyzed_accounts, vec![]) + } } diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 5870d0015..79d985d75 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -42,12 +42,12 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { ) -> AdjustmentIterationResult { let unconfirmed_adjustments = self.adjustment_computer.compute_unconfirmed_adjustments( weighted_accounts, - disqualification_arbiter, cw_service_fee_balance_minor, ); match Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments) { Either::Left(without_gainers) => { + //TODO arbiter, what about it here? Self::disqualify_single_account(disqualification_arbiter, without_gainers, logger) } Either::Right(with_gainers) => with_gainers, @@ -71,28 +71,28 @@ impl ServiceFeeAdjusterReal { OutputAccounts: From, { // In some cases taking advantage of that Rust std library implements also From for T - let weighted_accounts: Vec = convert_collection(accounts); - - let unconfirmed_accounts = weighted_accounts - .into_iter() - .map(|weighted_account| { - let disqualification_limit = disqualification_arbiter - .calculate_disqualification_edge( - &weighted_account.analyzed_account.qualified_as, - ); - minimal_acceptable_balance_assigned_diagnostics( - &weighted_account, - disqualification_limit, - ); - UnconfirmedAdjustment::new( - weighted_account, - disqualification_limit, - disqualification_limit, - ) - }) - .collect(); - - convert_collection(unconfirmed_accounts) + todo!() + // let weighted_accounts: Vec = convert_collection(accounts); + // + // let unconfirmed_accounts = weighted_accounts + // .into_iter() + // .map(|weighted_account| { + // let disqualification_limit = disqualification_arbiter + // .calculate_disqualification_edge( + // &weighted_account.analyzed_account.qualified_as, + // ); + // minimal_acceptable_balance_assigned_diagnostics( + // &weighted_account, + // disqualification_limit, + // ); + // UnconfirmedAdjustment::new( + // weighted_account, + // disqualification_limit, + // ) + // }) + // .collect(); + // + // convert_collection(unconfirmed_accounts) } fn new() -> Self { @@ -172,7 +172,7 @@ impl ServiceFeeAdjusterReal { let (sufficient_gainers, low_gainers) = unconfirmed_adjustments.into_iter().fold( init, |(mut sufficient_gainers, mut low_gainers), current| { - let disqualification_limit = current.disqualification_limit_minor; + let disqualification_limit = current.disqualification_limit_minor(); if current.proposed_adjusted_balance_minor >= disqualification_limit //TODO is the operator tested?? { @@ -217,7 +217,6 @@ impl AdjustmentComputer { pub fn compute_unconfirmed_adjustments( &self, weighted_accounts: Vec, - disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { let weights_total = weights_total(&weighted_accounts); @@ -243,15 +242,9 @@ impl AdjustmentComputer { proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); - let disqualification_limit = disqualification_arbiter - .calculate_disqualification_edge( - &weighted_account.analyzed_account.qualified_as, - ); - UnconfirmedAdjustment::new( weighted_account, proposed_adjusted_balance, - disqualification_limit, ) }) .collect() @@ -298,7 +291,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; - account_1.disqualification_limit_minor = multiple_by_billion(1_800_000_000); + account_1.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(1_800_000_000); let proposed_adjusted_balance_2 = multiple_by_billion(4_200_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 @@ -308,7 +301,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(5_000_000_000); account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; - account_2.disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; + account_2.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; let proposed_adjusted_balance_3 = multiple_by_billion(2_000_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 @@ -318,7 +311,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(3_000_000_000); account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; - account_3.disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; + account_3.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; let proposed_adjusted_balance_4 = multiple_by_billion(500_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 @@ -328,7 +321,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(1_500_000_000); account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; - account_4.disqualification_limit_minor = multiple_by_billion(500_000_000); + account_4.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(500_000_000); let proposed_adjusted_balance_5 = multiple_by_billion(1_000_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 @@ -338,7 +331,7 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; - account_5.disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; + account_5.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -400,8 +393,8 @@ mod tests { ); let expected_result = vec![ - UnconfirmedAdjustment::new(weighted_account_1, 123456789, 123456789), - UnconfirmedAdjustment::new(weighted_account_2, 987654321, 987654321), + UnconfirmedAdjustment::new(weighted_account_1, 123456789), + UnconfirmedAdjustment::new(weighted_account_2, 987654321), ]; assert_eq!(result, expected_result) } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 2365086c2..bd41bb762 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -92,15 +92,15 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO }; pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { - let analyzed_account = make_analyzed_account(n); + let qualified_account = make_non_guaranteed_qualified_payable(n); let proposed_adjusted_balance_minor = - (analyzed_account.qualified_as.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; - let disqualification_limit = (3 * proposed_adjusted_balance_minor) / 4; + (qualified_account.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; + let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; + let analyzed_account = AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); let weight = (n as u128).pow(3); UnconfirmedAdjustment::new( WeightedPayable::new(analyzed_account, weight), proposed_adjusted_balance_minor, - disqualification_limit, ) } @@ -144,7 +144,6 @@ impl DisqualificationGauge for DisqualificationGaugeMock { threshold_intercept_wei: u128, permanent_debt_allowed_wei: u128, ) -> u128 { - todo!("make sure this params are used somewhere"); self.determine_limit_params.lock().unwrap().push(( account_balance_wei, threshold_intercept_wei, @@ -212,15 +211,6 @@ impl ServiceFeeAdjusterMock { pub fn multiple_by_billion(num: u128) -> u128 { num * 10_u128.pow(9) } - -pub fn make_qualified_payable_by_wallet(wallet_address_segment: &str) -> QualifiedPayableAccount { - let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); - let wallet = make_wallet(wallet_address_segment); - let mut account = make_non_guaranteed_qualified_payable(num); - account.bare_account.wallet = wallet; - account -} - pub fn make_analyzed_account_by_wallet(wallet_address_segment: &str) -> AnalyzedPayableAccount { let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); let wallet = make_wallet(wallet_address_segment); @@ -229,6 +219,10 @@ pub fn make_analyzed_account_by_wallet(wallet_address_segment: &str) -> Analyzed account } +pub fn make_weighed_account(n: u64) -> WeightedPayable { + WeightedPayable::new(make_analyzed_account(n), 123456789) +} + // Should stay test only! impl From for AnalyzedPayableAccount { fn from(qualified_account: QualifiedPayableAccount) -> Self { diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 47d70175c..ddf438f00 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -964,7 +964,7 @@ impl ReceivableScanner { pub enum BeginScanError { NothingToProcess, ScanAlreadyRunning(SystemTime), - CalledFromNullScanner, // Exclusive for non_unit_tests + CalledFromNullScanner, // Exclusive for tests } impl BeginScanError { diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 9de25057e..43ed0146d 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -659,7 +659,7 @@ mod tests { }, payment_threshold_intercept_minor: 10_000_000_001_152_000_u128, creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: 333_333, + permanent_debt_allowed_minor: 333_333, }, }, QualifiedPayableAccount { @@ -671,7 +671,7 @@ mod tests { }, payment_threshold_intercept_minor: 999_978_993_055_555_580, creditor_thresholds: CreditorThresholds { - permanent_debt_allowed_wei: 10_000_000, + permanent_debt_allowed_minor: 10_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 ea6cb7331..79e4bef85 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -1885,7 +1885,7 @@ mod tests { } //an adapted test from old times when we had our own signing method - //I don't have data for the new chains so I omit them in this kind of non_unit_tests + //I don't have data for the new chains so I omit them in this kind of tests #[test] fn signs_various_transactions_for_eth_mainnet() { let signatures = &[ @@ -1919,7 +1919,7 @@ mod tests { } //an adapted test from old times when we had our own signing method - //I don't have data for the new chains so I omit them in this kind of non_unit_tests + //I don't have data for the new chains so I omit them in this kind of tests #[test] fn signs_various_transactions_for_ropsten() { let signatures = &[ diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 90bdd4ba0..5d40c2379 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -97,7 +97,7 @@ pub struct CryptDEPair { pub main: &'static dyn CryptDE, // This has the public key with which this Node instructs exit Nodes to encrypt responses. // In production, it is unrelated to the main public key to prevent the exit Node from - // identifying the originating Node. In non_unit_tests using --fake-public-key, the alias public key + // identifying the originating Node. In tests using --fake-public-key, the alias public key // is the main public key reversed. pub alias: &'static dyn CryptDE, } @@ -493,7 +493,7 @@ impl ConfiguredByPrivilege for Bootstrapper { multi_config: &MultiConfig, _: &mut StdStreams, ) -> Result<(), ConfiguratorError> { - // NOTE: The following line of code is not covered by unit non_unit_tests + // NOTE: The following line of code is not covered by unit tests fdlimit::raise_fd_limit(); let unprivileged_config = NodeConfiguratorStandardUnprivileged::new(&self.config).configure(multi_config)?; @@ -512,7 +512,7 @@ impl ConfiguredByPrivilege for Bootstrapper { ); self.config.node_descriptor = node_descriptor; // Before you remove local-descriptor reporting for non-Standard neighborhood modes, make - // sure you modify the multinode non_unit_tests so that they can tell A) when a Node has started up, + // sure you modify the multinode tests so that they can tell A) when a Node has started up, // and B) what its public key is. match &self.config.neighborhood_config.mode { NeighborhoodMode::Standard(node_addr, _, _) diff --git a/node/src/daemon/launch_verifier.rs b/node/src/daemon/launch_verifier.rs index 4960eab11..f0a4125c9 100644 --- a/node/src/daemon/launch_verifier.rs +++ b/node/src/daemon/launch_verifier.rs @@ -15,7 +15,7 @@ use websocket::client::ParseError; use websocket::sync::Client; use websocket::{ClientBuilder, OwnedMessage, WebSocketResult}; -// Note: if the INTERVALs are half the DELAYs or greater, the non_unit_tests below will need to change, +// Note: if the INTERVALs are half the DELAYs or greater, the tests below will need to change, // because they depend on being able to fail twice and still succeed. const DELAY_FOR_RESPONSE_MS: u64 = 10000; const RESPONSE_CHECK_INTERVAL_MS: u64 = 250; diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index dc9308256..2fad52508 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -703,7 +703,7 @@ pub mod test_utils { intentionally_blank!() /*all existing test calls only initialize() in the mocked version, but we need to call initialize_to_version() for the real object - in order to carry out some important non_unit_tests too*/ + in order to carry out some important tests too*/ } } diff --git a/node/src/db_config/db_encryption_layer.rs b/node/src/db_config/db_encryption_layer.rs index 714384afa..9c8ff4caf 100644 --- a/node/src/db_config/db_encryption_layer.rs +++ b/node/src/db_config/db_encryption_layer.rs @@ -81,5 +81,5 @@ impl DbEncryptionLayer { Bip39::encrypt_bytes(&decrypted_value, new_password).expect("Encryption failed") } - // These methods were extracted from SecureConfigLayer and are covered by the non_unit_tests there. + // These methods were extracted from SecureConfigLayer and are covered by the tests there. } diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 24881d3b1..2548a7593 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -48,7 +48,7 @@ trait NamedType { fn type_name(&self) -> &'static str; } -trait GossipHandler: NamedType + Send /* Send because lazily-written non_unit_tests require it */ { +trait GossipHandler: NamedType + Send /* Send because lazily-written tests require it */ { fn qualifies( &self, database: &NeighborhoodDatabase, @@ -1221,7 +1221,7 @@ impl RejectHandler { } } -pub trait GossipAcceptor: Send /* Send because lazily-written non_unit_tests require it */ { +pub trait GossipAcceptor: Send /* Send because lazily-written tests require it */ { fn handle( &self, database: &mut NeighborhoodDatabase, @@ -1358,7 +1358,7 @@ mod tests { Standard, OriginateOnly, // GossipAcceptor doesn't care about ConsumeOnly; that's routing, not Gossip - // ZeroHop is decentralized and should never appear in GossipAcceptor non_unit_tests + // ZeroHop is decentralized and should never appear in GossipAcceptor tests } fn make_default_neighborhood_metadata() -> NeighborhoodMetadata { diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 389aed942..4a0ba99ba 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -5470,7 +5470,7 @@ mod tests { } /* - For the next two non_unit_tests, the database looks like this: + For the next two tests, the database looks like this: +---A---+ | | diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 3cee16b97..8bb201d43 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -59,7 +59,7 @@ impl Default for PaymentThresholds { } } -//this code is used in non_unit_tests in Accountant +//this code is used in tests in Accountant impl PaymentThresholds { pub fn maturity_and_grace(&self, now: i64) -> i64 { now - checked_conversion::(self.maturity_threshold_sec) diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 20cec8081..64c30c2fe 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -175,7 +175,7 @@ impl Default for NodeDescriptor { } } -//the public key's role as a separate arg is to enable the produced descriptor to be constant and reliable in non_unit_tests +//the public key's role as a separate arg is to enable the produced descriptor to be constant and reliable in tests impl From<(&PublicKey, &NodeAddr, Chain, &dyn CryptDE)> for NodeDescriptor { fn from(tuple: (&PublicKey, &NodeAddr, Chain, &dyn CryptDE)) -> Self { let (public_key, node_addr, blockchain, cryptde) = tuple; diff --git a/node/src/sub_lib/ttl_hashmap.rs b/node/src/sub_lib/ttl_hashmap.rs index 9420815f7..faa79b68a 100644 --- a/node/src/sub_lib/ttl_hashmap.rs +++ b/node/src/sub_lib/ttl_hashmap.rs @@ -141,7 +141,7 @@ mod tests { #[test] fn ttl_hashmap_get_preserves_otherwise_expired_entry() { - // Note: You may think that these delays are far too long for unit non_unit_tests, and that you can + // Note: You may think that these delays are far too long for unit tests, and that you can // reduce them proportionally and get faster test execution. Before you do, though, be sure the // reduced test runs reliably on the Mac. We were seeing granularities of 200ms (that is, if you // order a 5ms sleep, you get 200ms) in early March 2019. diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt deleted file mode 100644 index 019d50300..000000000 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_age_criterion_formula.txt +++ /dev/null @@ -1,79 +0,0 @@ -# -# The data follows the standard JSON format and will be further processed into a row of integers to feed the respective -# criterion formula to be rendered. There are three different categories of values that can be supplied via an object -# with the following fields: "literals", "decimal_exponents" and "marked_values". -# -# Literals are as string declared integers in an array whose notation can be made better readable by use of underscores. -# -# Decimal exponents are plain integers in an array and represent powers of a ten. -# -# Marked values come as objects with two fields: "value" and "label", both expecting string values. The "value" field -# also accepts underscores in the integers supplied. -# -# If the same value has been defined by any of the other categories, marked values will replace them with no conflict. -# -{ - # anomalies: places where the trend changes from ascending in the big picture to a short descending one. Such places - # are always minor and limited so the major trend continues after an interruption. It's always a question how big - # issue this is, usually none at all, because the bounces are shallow. - "literals": [ - "1", - "5", - "9", - "25", - "44", - "50", - # - # Local anomaly* somewhere near here - # - "75", - "180", - "600", - "900", - "33_333", - "86_400", - "255_000", - "6_700_000", - "55_333_000", - "200_300_400", - "500_000_000", - "4_000_000_000", - "7_000_000_000", - "7_900_000_000", - # - # A bit more significant anomaly* in a long range of values, however, if taken seriously, we speak of - # section on the time scale representing approximately 24 years. This makes the issue irrelevant. - # - "8_000_000_000", - "9_000_000_000", - "11_000_000_000", - "15_000_000_000", - "78_000_000_000", - "444_333_444_444" - ], - "decimal_exponents": [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 - ], - "marked_values": [ - { - "value": "60", - "label": "MINUTE" - }, - { - "value": "3_600", - "label": "HOUR" - }, - { - "value": "86_400", - "label": "DAY" - }, - { - "value": "604_800", - "label": "WEEK" - }, - { - "value": "2_628_000", - "label": "MONTH" - } - ] -} \ No newline at end of file diff --git a/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt b/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt deleted file mode 100644 index 994870e85..000000000 --- a/node/src/test_utils/input_data/input_data_for_diagnostics_of_balance_criterion_formula.txt +++ /dev/null @@ -1,35 +0,0 @@ -# -# The data follows the standard JSON format and will be further processed into a row of integers to feed the respective -# criterion formula to be rendered. There are three different categories of values that can be supplied via an object -# with the following fields: "literals", "decimal_exponents" and "marked_values". -# -# Literals are as string declared integers in an array whose notation can be made better readable by use of underscores. -# -# Decimal exponents are plain integers in an array and represent powers of a ten. -# -# Marked values come as objects with two fields: "value" and "label", both expecting string values. The "value" field -# also accepts underscores in the integers supplied. -# -# If the same value has been defined by any of the other categories, marked values will replace them with no conflict. -# -{ - "literals": [ - "123_456", - "7_777_777", - "1_888_999_999_888", - "543_210_000_000_000_000_000" - ], - "decimal_exponents": [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 - ], - "marked_values": [ - { - "value": "1_000_000_000", - "label": "GWEI" - }, - { - "value": "1_000_000_000_000_000_000", - "label": "MASQ" - } - ] -} \ No newline at end of file diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 2db0a08dd..a7fe4ee0d 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -1054,7 +1054,7 @@ pub mod unshared_test_utils { // In some implementations of mocks that have methods demanding args, the best we can do in order to // capture and examine these args in assertions is to receive the ArbitraryIdStamp of the given // argument. - // If such strategy is once decided for, transfers of this id will have to happen in all the non_unit_tests + // If such strategy is once decided for, transfers of this id will have to happen in all the tests // relying on this mock, while also calling the intended method. So even in cases where we certainly // are not really interested in checking that id, if we ignored that, the call of this method would // blow up because the field that stores it is likely optional, with the value defaulted to None. @@ -1062,7 +1062,7 @@ pub mod unshared_test_utils { // As prevention of confusion from putting a requirement on devs to set the id stamp even though // they're not planning to use it, we have a null type of that stamp to be there at most cases. // As a result, we don't risk a direct punishment (for the None value being the problem) but also - // we'll set the assertion on fire if it doesn't match the expected id in non_unit_tests where we suddenly + // we'll set the assertion on fire if it doesn't match the expected id in tests where we suddenly // do care None => ArbitraryIdStamp::null(), } @@ -1122,7 +1122,7 @@ pub mod unshared_test_utils { // Objects of that trait have some native field about them that can be set to // different values so that we can distinguish different instances in an assertion. - // There are no non_unit_tests involving objects of that trait where instances are passed + // There are no tests involving objects of that trait where instances are passed // as parameters to a mock and need to be asserted on as part of a ..._params_arc // collection. diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index bb0d779df..25dfeeadb 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -208,7 +208,7 @@ impl PartialEq for NodeRecord { } impl NeighborhoodDatabase { - // These methods are intended for use only in non_unit_tests. Do not use them in production code. + // These methods are intended for use only in tests. Do not use them in production code. pub fn add_arbitrary_half_neighbor( &mut self, node_key: &PublicKey, diff --git a/node/tests/node_exits_from_crash_message_test.rs b/node/tests/node_exits_from_crash_message_test.rs index 15bcaffaa..8f0c4b1b6 100644 --- a/node/tests/node_exits_from_crash_message_test.rs +++ b/node/tests/node_exits_from_crash_message_test.rs @@ -14,7 +14,7 @@ use websocket::{ClientBuilder, OwnedMessage}; //blockchain_bridge, //dispatcher, //configurator, -//don't add more non_unit_tests unless you know what you're doing +//don't add more tests unless you know what you're doing #[test] fn node_exits_from_blockchain_bridge_panic_integration() { From d8756c4e6e3001aa37844207dc5fd14c67464273 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 19 Apr 2024 00:35:13 +0200 Subject: [PATCH 168/250] GH-711-c: tests prepared for fixes, seems logical and now again compiling --- .../disqualification_arbiter.rs | 10 +- .../account_stages_conversions.rs | 3 +- .../miscellaneous/data_structures.rs | 91 ++--- node/src/accountant/payment_adjuster/mod.rs | 72 ++-- .../payment_adjuster/preparatory_analyser.rs | 310 ++++++++---------- .../payment_adjuster/service_fee_adjuster.rs | 37 ++- .../accountant/payment_adjuster/test_utils.rs | 3 +- 7 files changed, 244 insertions(+), 282 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 9ce870525..efd2807ad 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -375,7 +375,10 @@ mod tests { fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit() { let mut account = make_non_guaranteed_unconfirmed_adjustment(444); account.proposed_adjusted_balance_minor = 1_000_000_000; - account.weighted_account.analyzed_account.disqualification_limit_minor = 1_000_000_000; + account + .weighted_account + .analyzed_account + .disqualification_limit_minor = 1_000_000_000; let accounts = vec![account]; let result = @@ -469,10 +472,7 @@ mod tests { let weights_and_accounts = payment_adjuster.calculate_weights(analyzed_accounts); let subject = DisqualificationArbiter::default(); let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments( - weights_and_accounts, - cw_service_fee_balance_minor, - ); + .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); let result = subject .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 1242d6dd5..e7e0f351e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -116,8 +116,7 @@ mod tests { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; let weighted_account = prepare_weighted_account(original_payable_account.clone()); - let unconfirmed_adjustment = - UnconfirmedAdjustment::new(weighted_account, 111_222_333); + let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index f42d1297d..afaf4a20b 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -84,17 +84,14 @@ impl AdjustedAccountBeforeFinalization { #[derive(Debug, PartialEq, Eq, Clone)] pub struct UnconfirmedAdjustment { pub weighted_account: WeightedPayable, - pub proposed_adjusted_balance_minor: u128 + pub proposed_adjusted_balance_minor: u128, } impl UnconfirmedAdjustment { - pub fn new( - weighted_account: WeightedPayable, - proposed_adjusted_balance_minor: u128 - ) -> Self { + pub fn new(weighted_account: WeightedPayable, proposed_adjusted_balance_minor: u128) -> Self { Self { weighted_account, - proposed_adjusted_balance_minor + proposed_adjusted_balance_minor, } } @@ -106,19 +103,21 @@ impl UnconfirmedAdjustment { self.weighted_account.balance_minor() } - pub fn disqualification_limit_minor(&self)->u128{ - self.weighted_account.analyzed_account.disqualification_limit_minor + pub fn disqualification_limit_minor(&self) -> u128 { + self.weighted_account + .analyzed_account + .disqualification_limit_minor } } -pub struct TransactionCountsWithin16bits { +pub struct TransactionCountsBy16bits { pub affordable: u16, pub required: u16, } -impl TransactionCountsWithin16bits { +impl TransactionCountsBy16bits { pub fn new(max_possible_tx_count: U256, number_of_accounts: usize) -> Self { - TransactionCountsWithin16bits { + TransactionCountsBy16bits { affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), } @@ -126,44 +125,46 @@ impl TransactionCountsWithin16bits { } #[derive(Debug)] -pub enum ServiceFeeCheckErrorContext { - TransactionFeeLimitationInspectionResult { +pub enum TransactionFeePastActionsContext { + TransactionFeeCheckDone { limitation_opt: Option, }, - TransactionFeeAccountsDumpPerformed { - original_tx_count: usize, - original_sum_of_service_fee_balances: u128, + TransactionFeeAccountsDumped { + past_txs_count: usize, + past_sum_of_service_fee_balances: u128, }, } -impl ServiceFeeCheckErrorContext { - pub fn new_dump_context(analyzed_accounts: &[WeightedPayable]) -> Self { - let original_tx_count = analyzed_accounts.len(); - let original_sum_of_service_fee_balances = - sum_as(analyzed_accounts, |account| account.balance_minor()); - Self::TransactionFeeAccountsDumpPerformed { - original_tx_count, - original_sum_of_service_fee_balances, +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct TransactionFeeLimitation { + pub count_limit: u16, + pub available_balance: u128, + pub sum_of_transaction_fee_balances: u128, +} + +impl TransactionFeePastActionsContext { + pub fn accounts_dumped_context(whole_set_of_analyzed_accounts: &[WeightedPayable]) -> Self { + let past_txs_count = whole_set_of_analyzed_accounts.len(); + let past_sum_of_service_fee_balances: u128 = + sum_as(whole_set_of_analyzed_accounts, |account| { + account.balance_minor() + }); + Self::TransactionFeeAccountsDumped { + past_txs_count, + past_sum_of_service_fee_balances, } } - pub fn new_detection_context(limitation_opt: Option) -> Self { - Self::TransactionFeeLimitationInspectionResult { limitation_opt } + pub fn check_done_context(limitation_opt: Option) -> Self { + Self::TransactionFeeCheckDone { limitation_opt } } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct TransactionFeeLimitation { - pub affordable_tx_count: u16, - pub actual_balance: U256, - pub required_balance: U256, -} - #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, RecursionResults, ServiceFeeCheckErrorContext, - TransactionCountsWithin16bits, TransactionFeeLimitation, + AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsBy16bits, + TransactionFeePastActionsContext, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::make_payable_account; @@ -211,8 +212,7 @@ mod tests { .map(plus_minus_correction_for_u16_max) .map(U256::from) .map(|max_possible_tx_count| { - let detected_tx_counts = - TransactionCountsWithin16bits::new(max_possible_tx_count, 123); + let detected_tx_counts = TransactionCountsBy16bits::new(max_possible_tx_count, 123); detected_tx_counts.affordable }) .collect::>(); @@ -231,7 +231,7 @@ mod tests { .map(plus_minus_correction_for_u16_max) .map(|required_tx_count_usize| { let detected_tx_counts = - TransactionCountsWithin16bits::new(U256::from(123), required_tx_count_usize); + TransactionCountsBy16bits::new(U256::from(123), required_tx_count_usize); detected_tx_counts.required }) .collect::>(); @@ -251,7 +251,7 @@ mod tests { } #[test] - fn construction_of_error_context_for_accounts_dump_works() { + fn construction_of_error_context_with_accounts_dumped_works() { let mut account_1 = make_weighed_account(123); account_1 .analyzed_account @@ -266,15 +266,16 @@ mod tests { .balance_wei = 999888777; let weighted_accounts = vec![account_1, account_2]; - let dump_performed = ServiceFeeCheckErrorContext::new_dump_context(&weighted_accounts); + let dump_performed = + TransactionFeePastActionsContext::accounts_dumped_context(&weighted_accounts); match dump_performed { - ServiceFeeCheckErrorContext::TransactionFeeAccountsDumpPerformed { - original_tx_count, - original_sum_of_service_fee_balances, + TransactionFeePastActionsContext::TransactionFeeAccountsDumped { + past_txs_count: txs_count, + past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, } => { - assert_eq!(original_tx_count, 2); - assert_eq!(original_sum_of_service_fee_balances, 1234567 + 999888777) + assert_eq!(txs_count, 2); + assert_eq!(sum_of_transaction_fee_balances, 1234567 + 999888777) } x => panic!("We expected version for accounts dump but got: {:?}", x), } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index aab15f16c..d95b09e1d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -34,7 +34,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ LowGainingAccountEliminated, SomeAccountsProcessed, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, ServiceFeeCheckErrorContext, TransactionFeeLimitation, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, TransactionFeeLimitation, TransactionFeePastActionsContext, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, @@ -104,13 +104,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { Err(e) => return Err(e), }; - let service_fee_check_error_context = - ServiceFeeCheckErrorContext::new_detection_context(transaction_fee_limitation_opt); + let transaction_fee_past_actions_context = + TransactionFeePastActionsContext::check_done_context(transaction_fee_limitation_opt); let service_fee_balance_minor = agent.service_fee_balance_minor(); let service_fee_check_outcome = match self.analyzer - .check_need_of_adjustment_by_service_fee::( + .check_need_of_adjustment_by_service_fee::( &self.disqualification_arbiter, - service_fee_check_error_context, + transaction_fee_past_actions_context, qualified_payables, service_fee_balance_minor, &self.logger, @@ -120,9 +120,18 @@ impl PaymentAdjuster for PaymentAdjusterReal { }; match (transaction_fee_limitation_opt, service_fee_check_outcome) { - (None, Either::Left(unlimited_payables)) => todo!(), - (None, Either::Right(adjustment_analysis)) => todo!(), - (Some(transaction_fee_limitation), _) => todo!(), + (None, Either::Left(non_analyzed_payables)) => Ok(Either::Left(non_analyzed_payables)), + (Some(limitation), Either::Left(non_analyzed_payables)) => { + // use this here + // let prepared_accounts= + // Self::prepare_accounts_with_disqualification_limits( + // payables, + // disqualification_arbiter, + // ); + todo!() + } + (None, Either::Right(checked_payables)) => todo!(), + (Some(limitation), Either::Right(checked_payables)) => todo!(), } } @@ -256,8 +265,9 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let service_fee_error_context = - ServiceFeeCheckErrorContext::new_dump_context(&weighted_accounts_in_descending_order); + let service_fee_error_context = TransactionFeePastActionsContext::accounts_dumped_context( + &weighted_accounts_in_descending_order, + ); let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( @@ -274,7 +284,7 @@ impl PaymentAdjusterReal { cw_service_fee_balance, &self.logger, ) { - Ok(answer) => answer, + Ok(outcome) => outcome, Err(e) => { log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); return Err(e); @@ -282,19 +292,19 @@ impl PaymentAdjusterReal { }; match check_outcome { - Either::Left(weighted_accounts_needing_adjustment) => { + Either::Left(weighted_accounts) => { + let accounts_not_needing_adjustment = convert_collection(weighted_accounts); + Ok(Either::Right(accounts_not_needing_adjustment)) + } + + Either::Right(weighted_accounts_needing_adjustment) => { diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); let adjustment_result_before_verification = self .propose_possible_adjustment_recursively(weighted_accounts_needing_adjustment); + Ok(Either::Left(adjustment_result_before_verification)) } - Either::Right(weighted_accounts) => { - let accounts_not_needing_adjustment = - //TODO you should use from impl - convert_collection(weighted_accounts); - Ok(Either::Right(accounts_not_needing_adjustment)) - } } } @@ -535,8 +545,7 @@ mod tests { LowGainingAccountEliminated, SomeAccountsProcessed, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentIterationResult, DecidedAccounts, - ServiceFeeCheckErrorContext, TransactionFeeLimitation, UnconfirmedAdjustment, + AdjustmentIterationResult, TransactionFeeLimitation, TransactionFeePastActionsContext, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ @@ -863,9 +872,9 @@ mod tests { total_amount_demanded_minor: 7_000_000_000, cw_service_fee_balance_minor: 100_000_000, appendix_opt: Some(TransactionFeeLimitation { - affordable_tx_count: 3, - actual_balance: U256::from(3_000_000_000_u64), - required_balance: U256::from(5_000_000_000_u64), + count_limit: 3, + available_balance: 3_000_000_000, + sum_of_transaction_fee_balances: 5_000_000_000, }), }, "Both transaction fee and service fee balances are not high enough. Number of \ @@ -1036,10 +1045,7 @@ mod tests { garbage_largest_exceeding_balance_recently_qualified, )); let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments( - weighted_accounts, - cw_service_fee_balance_minor, - ); + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); // The results are sorted from the biggest weights down let proposed_adjusted_balance = unconfirmed_adjustments[0].proposed_adjusted_balance_minor; assert_eq!( @@ -1100,10 +1106,9 @@ mod tests { let agent_id_stamp = ArbitraryIdStamp::new(); let service_fee_balance_in_minor_units = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; - let service_fee_error_context = - ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { - limitation_opt: None, - }; + let service_fee_error_context = TransactionFeePastActionsContext::TransactionFeeCheckDone { + limitation_opt: None, + }; let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( disqualification_arbiter, service_fee_error_context, @@ -1117,10 +1122,7 @@ mod tests { // with the smallest balance is outplayed by the other one gaining some kind of extra // significance let analyzed_accounts = match analysis_result { - Ok(Either::Right(adjustment_analysis)) => { - assert_eq!(adjustment_analysis.adjustment, Adjustment::ByServiceFee); - adjustment_analysis.accounts - } + Ok(Either::Right(analyzed_accounts)) => analyzed_accounts, x => panic!( "We expected to be let it for an adjustments with AnalyzedAccounts but got: {:?}", x diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs index 1dcb19751..868657b3a 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser.rs @@ -5,7 +5,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - ServiceFeeCheckErrorContext, TransactionCountsWithin16bits, TransactionFeeLimitation, + TransactionCountsBy16bits, TransactionFeeLimitation, TransactionFeePastActionsContext, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; @@ -13,6 +13,7 @@ use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, Paymen use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; +use futures::collect; use itertools::{Either, Product}; use masq_lib::logger::Logger; use std::cmp::Ordering; @@ -60,69 +61,53 @@ impl PreparatoryAnalyzer { cw_transaction_fee_balance_minor, max_tx_count_we_can_afford_u16, ); - let limitation = todo!(); - Ok(Some(limitation)) + let transaction_fee_limitation_opt = todo!(); + Ok(Some(transaction_fee_limitation_opt)) } } fn transaction_counts_verification( cw_transaction_fee_balance_minor: U256, - fee_requirement_per_tx_minor: u128, + txn_fee_required_per_txn_minor: u128, number_of_qualified_accounts: usize, - ) -> TransactionCountsWithin16bits { - let max_possible_tx_count_u256 = Self::max_possible_tx_count( - cw_transaction_fee_balance_minor, - fee_requirement_per_tx_minor, - ); - TransactionCountsWithin16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) - } + ) -> TransactionCountsBy16bits { + let max_possible_tx_count_u256 = + cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); - fn max_possible_tx_count( - cw_transaction_fee_balance_minor: U256, - fee_requirement_per_tx_minor: u128, - ) -> U256 { - cw_transaction_fee_balance_minor / U256::from(fee_requirement_per_tx_minor) + TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) } - pub fn check_need_of_adjustment_by_service_fee< - MidProduct, - IncomingAccount, - AdjustmentNeededValue, - >( + pub fn check_need_of_adjustment_by_service_fee( &self, disqualification_arbiter: &DisqualificationArbiter, - error_context: ServiceFeeCheckErrorContext, + transaction_fee_past_actions_context: TransactionFeePastActionsContext, payables: Vec, cw_service_fee_balance_minor: u128, logger: &Logger, - ) -> Result, AdjustmentNeededValue>, PaymentAdjusterError> + ) -> Result, Vec>, PaymentAdjusterError> where - IncomingAccount: DisqualificationAnalysableAccount, - MidProduct: BalanceProvidingAccount, - Vec: ProcessableReturnTypeFromServiceFeeCheck< - AdjustmentNeededReturnValue = AdjustmentNeededValue, - >, + IncomingAccount: DisqualificationAnalysableAccount, + AnalyzedAccount: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, { let required_service_fee_total = - Self::compute_total_of_service_fee_required::(&payables); + Self::compute_total_of_service_fee_required::(&payables); if cw_service_fee_balance_minor >= required_service_fee_total { - if matches!( - error_context, - ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { .. } - ) { - todo!() - } else { - Ok(Either::Left(payables)) - } + Ok(Either::Left(payables)) } else { - let result = self - .analyse_smallest_adjustment_possibility::( - disqualification_arbiter, - error_context, - payables, - cw_service_fee_balance_minor, - )?; + let prepared_accounts = Self::prepare_accounts_with_disqualification_limits( + payables, + disqualification_arbiter, + ); + + let lowest_disqualification_limit = + Self::find_lowest_disqualification_limit(&prepared_accounts); + + Self::analyse_lowest_adjustment_possibility( + transaction_fee_past_actions_context, + lowest_disqualification_limit, + cw_service_fee_balance_minor, + )?; log_adjustment_by_service_fee_is_required( logger, @@ -130,77 +115,61 @@ impl PreparatoryAnalyzer { cw_service_fee_balance_minor, ); - Ok(Either::Right(result.prepare_return())) + Ok(Either::Right(prepared_accounts)) } } - fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 + fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 where - Account: DisqualificationAnalysableAccount, - Product: BalanceProvidingAccount, + Account: BalanceProvidingAccount, { sum_as(payables, |account| account.balance_minor()) } - fn find_smallest_weight_and_prepare_accounts_to_proceed( + fn prepare_accounts_with_disqualification_limits( accounts: Vec, disqualification_arbiter: &DisqualificationArbiter, - ) -> (u128, Vec) + ) -> Vec where Account: DisqualificationAnalysableAccount, - Product: BalanceProvidingAccount, + Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, { - accounts.into_iter().fold( - (u128::MAX, vec![]), - |(min_dsq_limit, mut analyzed_accounts), current| { - let (current_dsq_limit, analyzed_account) = - current.analyse_limit(disqualification_arbiter); - let next_min_dsq_limit = match min_dsq_limit.cmp(¤t_dsq_limit) { - Ordering::Less => min_dsq_limit, - Ordering::Equal => min_dsq_limit, - Ordering::Greater => current_dsq_limit, - }; - analyzed_accounts.push(analyzed_account); - (next_min_dsq_limit, analyzed_accounts) - }, - ) + accounts + .into_iter() + .map(|account| account.prepare_analyzable_account(disqualification_arbiter)) + .collect() } - // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with - // the assumption that the debt with the lowest disqualification limit in the set fits in the - // available balance. If it doesn't, we won't want to bother the payment adjuster by its work, - // so we'll abort and no payments will come out. - fn analyse_smallest_adjustment_possibility( - &self, - disqualification_arbiter: &DisqualificationArbiter, - error_context: ServiceFeeCheckErrorContext, - accounts: Vec, - cw_service_fee_balance_minor: u128, - ) -> Result, PaymentAdjusterError> + fn find_lowest_disqualification_limit(accounts: &[Account]) -> u128 where - Account: DisqualificationAnalysableAccount, - Product: BalanceProvidingAccount, //TODO is this necessary? + Account: DisqualificationLimitProvidingAccount, { - let (lowest_disqualification_limit, prepared_accounts) = - Self::find_smallest_weight_and_prepare_accounts_to_proceed( - accounts, - disqualification_arbiter, - ); + todo!() + } + // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with + // the assumption that the debt with the lowest disqualification limit in the set fits in the + // available balance. If it doesn't, we're not going to bother the payment adjuster by that work, + // so it'll abort and no payments will come out. + fn analyse_lowest_adjustment_possibility( + transaction_fee_past_actions_context: TransactionFeePastActionsContext, + lowest_disqualification_limit: u128, + cw_service_fee_balance_minor: u128, + ) -> Result<(), PaymentAdjusterError> { if lowest_disqualification_limit <= cw_service_fee_balance_minor { - Ok(prepared_accounts) + Ok(()) } else { let (number_of_accounts, total_amount_demanded_minor, transaction_fee_appendix_opt): ( usize, u128, Option, - ) = match error_context { - ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { - limitation_opt: limitation, - } => todo!(), - ServiceFeeCheckErrorContext::TransactionFeeAccountsDumpPerformed { - original_tx_count, - original_sum_of_service_fee_balances, + ) = match transaction_fee_past_actions_context { + TransactionFeePastActionsContext::TransactionFeeCheckDone { limitation_opt } => { + todo!() + } + TransactionFeePastActionsContext::TransactionFeeAccountsDumped { + past_txs_count: txs_count, + past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, } => todo!(), }; @@ -225,24 +194,30 @@ impl PreparatoryAnalyzer { pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount where - Product: BalanceProvidingAccount, + Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, { // fn process_findings(insufficiency_found: bool)-> - fn analyse_limit(self, disqualification_arbiter: &DisqualificationArbiter) -> (u128, Product); + fn prepare_analyzable_account( + self, + disqualification_arbiter: &DisqualificationArbiter, + ) -> Product; } pub trait BalanceProvidingAccount { fn balance_minor(&self) -> u128; } +pub trait DisqualificationLimitProvidingAccount { + fn disqualification_limit(&self) -> u128; +} + impl DisqualificationAnalysableAccount for QualifiedPayableAccount { - fn analyse_limit( + fn prepare_analyzable_account( self, disqualification_arbiter: &DisqualificationArbiter, - ) -> (u128, AnalyzedPayableAccount) { + ) -> AnalyzedPayableAccount { let dsq_limit = disqualification_arbiter.calculate_disqualification_edge(&self); - let analyzed_account = AnalyzedPayableAccount::new(self, dsq_limit); - (dsq_limit, analyzed_account) + AnalyzedPayableAccount::new(self, dsq_limit) } } @@ -258,12 +233,18 @@ impl BalanceProvidingAccount for AnalyzedPayableAccount { } } +impl DisqualificationLimitProvidingAccount for WeightedPayable { + fn disqualification_limit(&self) -> u128 { + todo!() + } +} + impl DisqualificationAnalysableAccount for WeightedPayable { - fn analyse_limit( + fn prepare_analyzable_account( self, _disqualification_arbiter: &DisqualificationArbiter, - ) -> (u128, WeightedPayable) { - (self.analyzed_account.disqualification_limit_minor, self) + ) -> WeightedPayable { + self } } @@ -273,24 +254,8 @@ impl BalanceProvidingAccount for WeightedPayable { } } -pub trait ProcessableReturnTypeFromServiceFeeCheck { - type AdjustmentNeededReturnValue; - - fn prepare_return(self) -> Self::AdjustmentNeededReturnValue; -} - -impl ProcessableReturnTypeFromServiceFeeCheck for Vec { - type AdjustmentNeededReturnValue = AdjustmentAnalysis; - - fn prepare_return(self) -> Self::AdjustmentNeededReturnValue { - todo!() - } -} - -impl ProcessableReturnTypeFromServiceFeeCheck for Vec { - type AdjustmentNeededReturnValue = Vec; - - fn prepare_return(self) -> Self::AdjustmentNeededReturnValue { +impl DisqualificationLimitProvidingAccount for AnalyzedPayableAccount { + fn disqualification_limit(&self) -> u128 { todo!() } } @@ -298,58 +263,67 @@ impl ProcessableReturnTypeFromServiceFeeCheck for Vec { #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::ServiceFeeCheckErrorContext; - use crate::accountant::payment_adjuster::preparatory_analyser::{ - PreparatoryAnalyzer, + use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionFeePastActionsContext; + use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; + use crate::accountant::payment_adjuster::test_utils::{ + make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, }; - use crate::accountant::payment_adjuster::test_utils::{multiple_by_billion, DisqualificationGaugeMock, make_weighed_account}; use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use crate::accountant::{QualifiedPayableAccount}; + use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; + use itertools::Either; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::utils::convert_collection; use std::sync::{Arc, Mutex}; fn test_adjustment_possibility_nearly_rejected( + test_name: &str, disqualification_gauge: DisqualificationGaugeMock, original_accounts: [QualifiedPayableAccount; 2], cw_service_fee_balance: u128, ) { + init_test_logging(); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); let disqualification_gauge = disqualification_gauge.determine_limit_params(&determine_limit_params_arc); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; - let service_fee_error_context = ServiceFeeCheckErrorContext::new_dump_context(&vec![]); + let service_fee_error_context = + TransactionFeePastActionsContext::accounts_dumped_context(&vec![]); - let result = subject.analyse_smallest_adjustment_possibility( + let result = subject.check_need_of_adjustment_by_service_fee( &disqualification_arbiter, service_fee_error_context, original_accounts.clone().to_vec(), cw_service_fee_balance, + &Logger::new(test_name), ); let expected_analyzed_accounts = convert_collection(original_accounts.to_vec()); - assert_eq!(result, Ok(expected_analyzed_accounts)); + assert_eq!(result, Ok(Either::Right(expected_analyzed_accounts))); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); let account_1 = &original_accounts[0]; let account_2 = &original_accounts[1]; - let expected_params = vec![ + let expected_params = vec![ ( account_1.bare_account.balance_wei, account_1.payment_threshold_intercept_minor, - account_1.creditor_thresholds.permanent_debt_allowed_minor + account_1.creditor_thresholds.permanent_debt_allowed_minor, ), ( account_2.bare_account.balance_wei, account_2.payment_threshold_intercept_minor, - account_2.creditor_thresholds.permanent_debt_allowed_minor - ) + account_2.creditor_thresholds.permanent_debt_allowed_minor, + ), ]; - assert_eq!( - *determine_limit_params, - expected_params - ) + assert_eq!(*determine_limit_params, expected_params); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Total of \ + blah wei in MASQ was ordered while the consuming wallet held only bluh wei of \ + the MASQ token. Adjustment in their count or the amounts is required." + )); } #[test] @@ -365,6 +339,7 @@ mod tests { let original_accounts = [account_1, account_2]; test_adjustment_possibility_nearly_rejected( + "adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger", disqualification_gauge, original_accounts, cw_service_fee_balance, @@ -384,6 +359,7 @@ mod tests { let original_accounts = [account_1, account_2]; test_adjustment_possibility_nearly_rejected( + "adjustment_possibility_nearly_rejected_when_cw_balance_equal", disqualification_gauge, original_accounts, cw_service_fee_balance, @@ -407,17 +383,18 @@ mod tests { .determine_limit_result(1_000_000_222); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let service_fee_check_error_context = - ServiceFeeCheckErrorContext::TransactionFeeLimitationInspectionResult { + let transaction_fee_past_actions_context = + TransactionFeePastActionsContext::TransactionFeeCheckDone { limitation_opt: None, }; let subject = PreparatoryAnalyzer {}; - let result = subject.analyse_smallest_adjustment_possibility( + let result = subject.check_need_of_adjustment_by_service_fee( &disqualification_arbiter, - service_fee_check_error_context, + transaction_fee_past_actions_context, original_accounts, cw_service_fee_balance, + &Logger::new("test"), ); assert_eq!( @@ -435,56 +412,27 @@ mod tests { #[test] fn accounts_analyzing_works_even_for_weighted_payable() { - let disqualification_limit_1 = multiple_by_billion(300_000) + 2; - let mut weighted_account_1 = make_weighed_account(123); - weighted_account_1.analyzed_account.disqualification_limit_minor = disqualification_limit_1; - let disqualification_limit_2 = multiple_by_billion(300_000) + 2; - let mut weighted_account_2 = make_weighed_account(456); - weighted_account_2.analyzed_account.disqualification_limit_minor = disqualification_limit_2; - let disqualification_limit_3 = multiple_by_billion(300_000) + 2; - let mut weighted_account_3 = make_weighed_account(789); - weighted_account_3.analyzed_account.disqualification_limit_minor = disqualification_limit_3; - let disqualification_limit_4 = disqualification_limit_3; - let mut weighted_account_4 = make_weighed_account(789); - weighted_account_4.analyzed_account.disqualification_limit_minor = disqualification_limit_4; - let accounts = vec![ - weighted_account_1.clone(), - weighted_account_2.clone(), - weighted_account_3.clone(), - weighted_account_4.clone(), - ]; - let disqualification_gauge = DisqualificationGaugeMock::default(); + let weighted_account_1 = make_weighed_account(123); + let weighted_account_2 = make_weighed_account(456); + let accounts = vec![weighted_account_1, weighted_account_2]; let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); + DisqualificationArbiter::new(Box::new(DisqualificationGaugeMock::default())); - let (minimal_disqualification_limit, analyzed_accounts) = - PreparatoryAnalyzer::find_smallest_weight_and_prepare_accounts_to_proceed( - accounts, - &disqualification_arbiter, - ); + let analyzed_accounts = PreparatoryAnalyzer::prepare_accounts_with_disqualification_limits( + accounts.clone(), + &disqualification_arbiter, + ); - assert_eq!(minimal_disqualification_limit, disqualification_limit_2); - let expected_analyzed_accounts = vec![ - weighted_account_1, - weighted_account_2, - weighted_account_3, - weighted_account_4, - ]; - assert_eq!(analyzed_accounts, expected_analyzed_accounts) + assert_eq!(analyzed_accounts, accounts) } #[test] - fn fold_for_find_smallest_weight_and_prepare_accounts_to_proceed_starts_with_u128_max() { - let disqualification_arbiter = DisqualificationArbiter::default(); - let accounts: Vec = vec![]; - - let (minimal_disqualification_limit, analyzed_accounts) = - PreparatoryAnalyzer::find_smallest_weight_and_prepare_accounts_to_proceed( - accounts, - &disqualification_arbiter, - ); + fn fold_for_find_lowest_weight_and_prepare_accounts_to_proceed_starts_with_u128_max() { + let accounts: Vec = vec![]; + + let minimal_disqualification_limit = + PreparatoryAnalyzer::find_lowest_disqualification_limit(&accounts); assert_eq!(minimal_disqualification_limit, u128::MAX); - assert_eq!(analyzed_accounts, vec![]) } } diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 79d985d75..f72f6065d 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -40,10 +40,9 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let unconfirmed_adjustments = self.adjustment_computer.compute_unconfirmed_adjustments( - weighted_accounts, - cw_service_fee_balance_minor, - ); + let unconfirmed_adjustments = self + .adjustment_computer + .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); match Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments) { Either::Left(without_gainers) => { @@ -242,10 +241,7 @@ impl AdjustmentComputer { proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); - UnconfirmedAdjustment::new( - weighted_account, - proposed_adjusted_balance, - ) + UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) }) .collect() } @@ -291,7 +287,10 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; - account_1.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(1_800_000_000); + account_1 + .weighted_account + .analyzed_account + .disqualification_limit_minor = multiple_by_billion(1_800_000_000); let proposed_adjusted_balance_2 = multiple_by_billion(4_200_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 @@ -301,7 +300,10 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(5_000_000_000); account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; - account_2.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; + account_2 + .weighted_account + .analyzed_account + .disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; let proposed_adjusted_balance_3 = multiple_by_billion(2_000_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 @@ -311,7 +313,10 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(3_000_000_000); account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; - account_3.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; + account_3 + .weighted_account + .analyzed_account + .disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; let proposed_adjusted_balance_4 = multiple_by_billion(500_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 @@ -321,7 +326,10 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(1_500_000_000); account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; - account_4.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(500_000_000); + account_4 + .weighted_account + .analyzed_account + .disqualification_limit_minor = multiple_by_billion(500_000_000); let proposed_adjusted_balance_5 = multiple_by_billion(1_000_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 @@ -331,7 +339,10 @@ mod tests { .bare_account .balance_wei = multiple_by_billion(2_000_000_000); account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; - account_5.weighted_account.analyzed_account.disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; + account_5 + .weighted_account + .analyzed_account + .disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index bd41bb762..55e62e9d5 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -96,7 +96,8 @@ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustme let proposed_adjusted_balance_minor = (qualified_account.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; - let analyzed_account = AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); + let analyzed_account = + AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); let weight = (n as u128).pow(3); UnconfirmedAdjustment::new( WeightedPayable::new(analyzed_account, weight), From a8495b80f6634be9cfad1a1b1e99b290e7801186 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 20 Apr 2024 21:27:23 +0200 Subject: [PATCH 169/250] GH-711-c: I had to rework the preparatory analyser, oh Gosh, it's a tricky place --- .../logging_and_diagnostics/log_functions.rs | 4 +- .../account_stages_conversions.rs | 33 +- .../miscellaneous/data_structures.rs | 120 +++- .../miscellaneous/helper_functions.rs | 24 +- node/src/accountant/payment_adjuster/mod.rs | 208 ++++--- .../payment_adjuster/preparatory_analyser.rs | 438 -------------- .../accounts_abstraction.rs | 71 +++ .../preparatory_analyser/mod.rs | 542 ++++++++++++++++++ .../payment_adjuster/service_fee_adjuster.rs | 95 --- 9 files changed, 856 insertions(+), 679 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/preparatory_analyser.rs create mode 100644 node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs create mode 100644 node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index 7ffc391b5..ec0a17b6f 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -142,8 +142,8 @@ pub fn log_adjustment_by_service_fee_is_required( ) { warning!( logger, - "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of \ - the MASQ token. Adjustment in their count or the amounts is required.", + "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of MASQ \ + token. Adjustment of the count or amounts is required.", payables_sum.separate_with_commas(), cw_service_fee_balance.separate_with_commas() ); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index e7e0f351e..e6bf6d70c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -4,6 +4,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; +use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; use crate::accountant::QualifiedPayableAccount; // If passing along without PA just to BlockchainBridge @@ -13,11 +14,11 @@ impl From for PayableAccount { } } -// After transaction fee adjustment while no need to go off with the other fee, and so we want to -// drop the weights etc. +// After transaction fee adjustment while no need to go off with the other fee, and so we keep +// the original balance, drop the weights etc. impl From for PayableAccount { fn from(weighted_account: WeightedPayable) -> Self { - todo!() + weighted_account.analyzed_account.qualified_as.bare_account } } @@ -57,10 +58,9 @@ impl From for AdjustedAccountBeforeFinalization { // they requested impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { - let proposed_adjusted_balance_minor = weighted_account.balance_minor(); + let limited_adjusted_balance = weighted_account.disqualification_limit(); let original_account = weighted_account.analyzed_account.qualified_as.bare_account; - - AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) + AdjustedAccountBeforeFinalization::new(original_account, limited_adjusted_balance) } } @@ -98,12 +98,25 @@ mod tests { analyzed_account.qualified_as.bare_account = payable_account; WeightedPayable::new(analyzed_account, garbage_weight) } + #[test] - fn conversion_between_weighted_payable_and_non_finalized_account_is_implemented() { - let mut original_payable_account = make_payable_account(123); - original_payable_account.balance_wei = 200_000_000; + fn conversation_between_weighted_payable_and_standard_payable_account() { + let original_payable_account = make_payable_account(345); let weighted_account = prepare_weighted_account(original_payable_account.clone()); + let result = PayableAccount::from(weighted_account); + + assert_eq!(result, original_payable_account) + } + + #[test] + fn conversion_between_weighted_payable_and_non_finalized_account() { + let original_payable_account = make_payable_account(123); + let mut weighted_account = prepare_weighted_account(original_payable_account.clone()); + weighted_account + .analyzed_account + .disqualification_limit_minor = 200_000_000; + let result = AdjustedAccountBeforeFinalization::from(weighted_account); let expected_result = @@ -112,7 +125,7 @@ mod tests { } #[test] - fn conversion_between_unconfirmed_adjustment_and_non_finalized_account_is_implemented() { + fn conversion_between_unconfirmed_adjustment_and_non_finalized_account() { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; let weighted_account = prepare_weighted_account(original_payable_account.clone()); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index afaf4a20b..bd3cf288c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -2,9 +2,11 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::preparatory_analyser::BalanceProvidingAccount; +use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; +use itertools::Either; +use masq_lib::utils::ExpectValue; use web3::types::U256; #[derive(Clone, Debug, PartialEq, Eq)] @@ -124,8 +126,80 @@ impl TransactionCountsBy16bits { } } -#[derive(Debug)] -pub enum TransactionFeePastActionsContext { +#[derive(Default, Clone)] +pub struct AdjustmentPossibilityErrorBuilder { + context_opt: Option, + analyzed_accounts_count: usize, + service_fee_total_required_minor: u128, + cw_service_fee_balance_minor: u128, +} + +impl AdjustmentPossibilityErrorBuilder { + pub fn accounts_dumped_context( + mut self, + whole_set_of_analyzed_accounts: &[WeightedPayable], + ) -> Self { + let past_txs_count = whole_set_of_analyzed_accounts.len(); + let past_sum_of_service_fee_balances: u128 = + sum_as(whole_set_of_analyzed_accounts, |account| { + account.balance_minor() + }); + self.context_opt.replace( + TransactionFeePastCheckContext::TransactionFeeAccountsDumped { + past_txs_count, + past_sum_of_service_fee_balances, + }, + ); + self + } + + pub fn check_done_context(mut self, limitation_opt: Option) -> Self { + self.context_opt + .replace(TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt }); + self + } + + pub fn all_time_supplied_parameters( + mut self, + analyzed_accounts_count: usize, + service_fee_total_required_minor: u128, + cw_service_fee_balance_minor: u128, + ) -> Self { + self.analyzed_accounts_count = analyzed_accounts_count; + self.service_fee_total_required_minor = service_fee_total_required_minor; + self.cw_service_fee_balance_minor = cw_service_fee_balance_minor; + self + } + + fn derive_params(self) -> (usize, u128, Option) { + match self.context_opt.expectv("Tx fee past check context") { + TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt } => ( + self.analyzed_accounts_count, + self.service_fee_total_required_minor, + limitation_opt, + ), + TransactionFeePastCheckContext::TransactionFeeAccountsDumped { + past_txs_count, + past_sum_of_service_fee_balances, + } => (past_txs_count, past_sum_of_service_fee_balances, None), + } + } + + pub fn build(self) -> PaymentAdjusterError { + let cw_service_fee_balance_minor = self.cw_service_fee_balance_minor; + let (number_of_accounts, total_service_fee_required_minor, transaction_fee_appendix_opt) = + self.derive_params(); + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts, + total_service_fee_required_minor, + cw_service_fee_balance_minor, + transaction_fee_appendix_opt, + } + } +} + +#[derive(Debug, Clone)] +pub enum TransactionFeePastCheckContext { TransactionFeeCheckDone { limitation_opt: Option, }, @@ -138,33 +212,29 @@ pub enum TransactionFeePastActionsContext { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct TransactionFeeLimitation { pub count_limit: u16, - pub available_balance: u128, - pub sum_of_transaction_fee_balances: u128, + pub cw_transaction_fee_balance_minor: u128, + pub per_transaction_required_fee_minor: u128, } -impl TransactionFeePastActionsContext { - pub fn accounts_dumped_context(whole_set_of_analyzed_accounts: &[WeightedPayable]) -> Self { - let past_txs_count = whole_set_of_analyzed_accounts.len(); - let past_sum_of_service_fee_balances: u128 = - sum_as(whole_set_of_analyzed_accounts, |account| { - account.balance_minor() - }); - Self::TransactionFeeAccountsDumped { - past_txs_count, - past_sum_of_service_fee_balances, +impl TransactionFeeLimitation { + pub fn new( + count_limit: u16, + cw_transaction_fee_balance_minor: u128, + per_transaction_required_fee_minor: u128, + ) -> Self { + Self { + count_limit, + cw_transaction_fee_balance_minor, + per_transaction_required_fee_minor, } } - - pub fn check_done_context(limitation_opt: Option) -> Self { - Self::TransactionFeeCheckDone { limitation_opt } - } } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsBy16bits, - TransactionFeePastActionsContext, + AdjustedAccountBeforeFinalization, AdjustmentPossibilityErrorBuilder, RecursionResults, + TransactionCountsBy16bits, TransactionFeePastCheckContext, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::make_payable_account; @@ -266,11 +336,11 @@ mod tests { .balance_wei = 999888777; let weighted_accounts = vec![account_1, account_2]; - let dump_performed = - TransactionFeePastActionsContext::accounts_dumped_context(&weighted_accounts); + let builder = AdjustmentPossibilityErrorBuilder::default() + .accounts_dumped_context(&weighted_accounts); - match dump_performed { - TransactionFeePastActionsContext::TransactionFeeAccountsDumped { + match builder.context_opt.unwrap() { + TransactionFeePastCheckContext::TransactionFeeAccountsDumped { past_txs_count: txs_count, past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, } => { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index f7f803a50..297c3a0f6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -79,6 +79,12 @@ fn find_largest_u128(slice: &[u128]) -> u128 { .fold(0, |largest_so_far, num| largest_so_far.max(*num)) } +pub fn find_smallest_u128(slice: &[u128]) -> u128 { + slice + .iter() + .fold(u128::MAX, |smallest_so_far, num| smallest_so_far.min(*num)) +} + pub fn exhaust_cw_balance_entirely( approved_accounts: Vec, original_cw_service_fee_balance_minor: u128, @@ -197,8 +203,8 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_balance_entirely, - find_largest_exceeding_balance, find_largest_u128, zero_affordable_accounts_found, - ConsumingWalletExhaustingStatus, + find_largest_exceeding_balance, find_largest_u128, find_smallest_u128, + zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; use crate::accountant::test_utils::{ make_analyzed_account, make_non_guaranteed_qualified_payable, make_payable_account, @@ -254,6 +260,20 @@ mod tests { assert_eq!(result, 456565) } + #[test] + fn find_smallest_u128_begins_with_u128_max() { + let result = find_smallest_u128(&[]); + + assert_eq!(result, u128::MAX) + } + + #[test] + fn find_smallest_u128_works() { + let result = find_smallest_u128(&[45, 1112, 456565, 3, 7, 456565, 456564]); + + assert_eq!(result, 3) + } + #[test] fn find_largest_exceeding_balance_works() { let mut account_1 = make_analyzed_account(111); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d95b09e1d..63600fab5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -29,12 +29,12 @@ use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ - accounts_before_and_after_debug, log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, + accounts_before_and_after_debug, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ LowGainingAccountEliminated, SomeAccountsProcessed, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, TransactionFeeLimitation, TransactionFeePastActionsContext, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentPossibilityErrorBuilder, RecursionResults, TransactionFeeLimitation, TransactionFeePastCheckContext, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, @@ -59,12 +59,15 @@ use thousands::Separable; use web3::types::U256; use masq_lib::utils::convert_collection; +pub type AdjustmentAnalysisResult = + Result, AdjustmentAnalysis>, PaymentAdjusterError>; + pub trait PaymentAdjuster { fn search_for_indispensable_adjustment( &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError>; + ) -> AdjustmentAnalysisResult; fn adjust_payments( &mut self, @@ -89,50 +92,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> - { - let number_of_counts = qualified_payables.len(); - - let transaction_fee_limitation_opt = match self - .analyzer - .determine_transaction_count_limit_by_transaction_fee( - agent, - number_of_counts, - &self.logger, - ) { - Ok(detected_limitation_opt) => detected_limitation_opt, - Err(e) => return Err(e), - }; - - let transaction_fee_past_actions_context = - TransactionFeePastActionsContext::check_done_context(transaction_fee_limitation_opt); - let service_fee_balance_minor = agent.service_fee_balance_minor(); - let service_fee_check_outcome = match self.analyzer - .check_need_of_adjustment_by_service_fee::( - &self.disqualification_arbiter, - transaction_fee_past_actions_context, - qualified_payables, - service_fee_balance_minor, - &self.logger, - ){ - Ok(check_outcome) => check_outcome, - Err(e) => todo!() - }; - - match (transaction_fee_limitation_opt, service_fee_check_outcome) { - (None, Either::Left(non_analyzed_payables)) => Ok(Either::Left(non_analyzed_payables)), - (Some(limitation), Either::Left(non_analyzed_payables)) => { - // use this here - // let prepared_accounts= - // Self::prepare_accounts_with_disqualification_limits( - // payables, - // disqualification_arbiter, - // ); - todo!() - } - (None, Either::Right(checked_payables)) => todo!(), - (Some(limitation), Either::Right(checked_payables)) => todo!(), - } + ) -> AdjustmentAnalysisResult { + self.analyzer.analyze_accounts( + agent, + &self.disqualification_arbiter, + qualified_payables, + &self.logger, + ) } fn adjust_payments( @@ -189,6 +155,29 @@ impl PaymentAdjusterReal { } } + //TODO delete me + // fn evaluate_checks(&self, results: PreparatoryAnalysisResults) -> AdjustmentAnalysisResult { + // match ( + // results.transaction_fee_limitation_opt, + // results.accounts_after_service_fee_check, + // ) { + // (None, Either::Left(payables_skipping_service_fee_analysis)) => { + // Ok(Either::Left(payables_skipping_service_fee_analysis)) + // } + // (None, Either::Right(analyzed_payables)) => Ok(Either::Right(AdjustmentAnalysis::new( + // Adjustment::ByServiceFee, + // analyzed_payables, + // ))), + // (Some(limitation), Either::Left(payables_skipping_service_fee_analysis)) => { + // Ok(Either::Right(self.work_skipped_accounts_into_analysis( + // limitation, + // payables_skipping_service_fee_analysis, + // ))) + // } + // (Some(limitation), Either::Right(analyzed_accounts)) => Ok(Either::Right(todo!())), + // } + // } + fn initialize_inner( &mut self, cw_service_fee_balance: u128, @@ -265,9 +254,8 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let service_fee_error_context = TransactionFeePastActionsContext::accounts_dumped_context( - &weighted_accounts_in_descending_order, - ); + let error_builder = AdjustmentPossibilityErrorBuilder::default() + .accounts_dumped_context(&weighted_accounts_in_descending_order); let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( @@ -275,37 +263,46 @@ impl PaymentAdjusterReal { already_known_affordable_transaction_count, ); - let cw_service_fee_balance = self.inner.original_cw_service_fee_balance_minor(); + let cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); - let check_outcome = match self.analyzer.check_need_of_adjustment_by_service_fee( - &self.disqualification_arbiter, - service_fee_error_context, - weighted_accounts_affordable_by_transaction_fee, - cw_service_fee_balance, + if self.analyzer.recheck_if_service_fee_adjustment_is_needed( + &weighted_accounts_affordable_by_transaction_fee, + cw_service_fee_balance_minor, + error_builder, &self.logger, - ) { - Ok(outcome) => outcome, - Err(e) => { - log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); - return Err(e); - } - }; - - match check_outcome { - Either::Left(weighted_accounts) => { - let accounts_not_needing_adjustment = convert_collection(weighted_accounts); - Ok(Either::Right(accounts_not_needing_adjustment)) - } - - Either::Right(weighted_accounts_needing_adjustment) => { - diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); - - let adjustment_result_before_verification = self - .propose_possible_adjustment_recursively(weighted_accounts_needing_adjustment); - - Ok(Either::Left(adjustment_result_before_verification)) - } + )? { + todo!() + } else { + todo!() } + + // match self.analyzer.check_adjustment_possibility( + // transaction_fee_past_context, + // weighted_accounts_affordable_by_transaction_fee, + // cw_service_fee_balance_minor, + // ) { + // Ok(outcome) => outcome, + // Err(e) => { + // log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); + // return Err(e); + // } + // }; + // todo!("terrible"); + // match check_outcome { + // Either::Left(weighted_accounts) => { + // let accounts_not_needing_adjustment = convert_collection(weighted_accounts); + // Ok(Either::Right(accounts_not_needing_adjustment)) + // } + // + // Either::Right(weighted_accounts_needing_adjustment) => { + // diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); + // + // let adjustment_result_before_verification = self + // .propose_possible_adjustment_recursively(weighted_accounts_needing_adjustment); + // + // Ok(Either::Left(adjustment_result_before_verification)) + // } + // } } fn propose_possible_adjustment_recursively( @@ -484,9 +481,9 @@ pub enum PaymentAdjusterError { }, NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: usize, - total_amount_demanded_minor: u128, + total_service_fee_required_minor: u128, cw_service_fee_balance_minor: u128, - appendix_opt: Option, + transaction_fee_appendix_opt: Option, }, AllAccountsEliminated, } @@ -509,9 +506,9 @@ impl Display for PaymentAdjusterError { ), PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts, - total_amount_demanded_minor, + total_service_fee_required_minor: total_amount_demanded_minor, cw_service_fee_balance_minor, - appendix_opt, + transaction_fee_appendix_opt: appendix_opt, } => write!( f, "Found a smaller service fee balance than it does for a single payment. \ @@ -545,7 +542,7 @@ mod tests { LowGainingAccountEliminated, SomeAccountsProcessed, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, TransactionFeeLimitation, TransactionFeePastActionsContext, + AdjustmentIterationResult, TransactionFeeLimitation, TransactionFeePastCheckContext, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ @@ -812,9 +809,9 @@ mod tests { Err( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, - total_amount_demanded_minor: 920_000_000_000, + total_service_fee_required_minor: 920_000_000_000, cw_service_fee_balance_minor, - appendix_opt: None, + transaction_fee_appendix_opt: None, } ) ); @@ -858,9 +855,9 @@ mod tests { ( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 5, - total_amount_demanded_minor: 6_000_000_000, + total_service_fee_required_minor: 6_000_000_000, cw_service_fee_balance_minor: 333_000_000, - appendix_opt: None, + transaction_fee_appendix_opt: None, }, "Found a smaller service fee balance than it does for a single payment. \ Number of canceled payments: 5. Total amount required: 6,000,000,000 wei, @@ -869,12 +866,12 @@ mod tests { ( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 5, - total_amount_demanded_minor: 7_000_000_000, + total_service_fee_required_minor: 7_000_000_000, cw_service_fee_balance_minor: 100_000_000, - appendix_opt: Some(TransactionFeeLimitation { + transaction_fee_appendix_opt: Some(TransactionFeeLimitation { count_limit: 3, - available_balance: 3_000_000_000, - sum_of_transaction_fee_balances: 5_000_000_000, + cw_transaction_fee_balance_minor: 3_000_000_000, + per_transaction_required_fee_minor: 5_000_000_000, }), }, "Both transaction fee and service fee balances are not high enough. Number of \ @@ -1104,16 +1101,16 @@ mod tests { subject.calculators.push(Box::new(calculator_mock)); subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let service_fee_balance_in_minor_units = balance_2; + let cw_service_fee_balance_minor = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; - let service_fee_error_context = TransactionFeePastActionsContext::TransactionFeeCheckDone { - limitation_opt: None, - }; - let analysis_result = subject.analyzer.check_need_of_adjustment_by_service_fee( + let agent_for_analysis = BlockchainAgentMock::default() + .service_fee_balance_minor_result(cw_service_fee_balance_minor) + .transaction_fee_balance_minor_result(U256::MAX) + .estimated_transaction_fee_per_transaction_minor_result(12356); + let analysis_result = subject.analyzer.analyze_accounts( + &agent_for_analysis, disqualification_arbiter, - service_fee_error_context, qualified_payables, - service_fee_balance_in_minor_units, &subject.logger, ); // If concluded at the entry into the PaymentAdjuster that it has no point going off @@ -1121,8 +1118,8 @@ mod tests { // However, it can only assess the balance (that early - in the real world) and accounts // with the smallest balance is outplayed by the other one gaining some kind of extra // significance - let analyzed_accounts = match analysis_result { - Ok(Either::Right(analyzed_accounts)) => analyzed_accounts, + let adjustment_analysis = match analysis_result { + Ok(Either::Right(analysis)) => analysis, x => panic!( "We expected to be let it for an adjustments with AnalyzedAccounts but got: {:?}", x @@ -1131,16 +1128,13 @@ mod tests { let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(cw_service_fee_balance_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { agent, response_skeleton_opt: None, - adjustment_analysis: AdjustmentAnalysis::new( - Adjustment::ByServiceFee, - analyzed_accounts, - ), + adjustment_analysis, }; let result = subject.adjust_payments(adjustment_setup, now); @@ -1734,9 +1728,9 @@ mod tests { err, PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 3, - total_amount_demanded_minor: balance_1 + balance_2 + balance_3, + total_service_fee_required_minor: balance_1 + balance_2 + balance_3, cw_service_fee_balance_minor: service_fee_balance_in_minor_units, - appendix_opt: None, + transaction_fee_appendix_opt: None, } ); TestLogHandler::new().exists_log_containing(&format!( diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser.rs b/node/src/accountant/payment_adjuster/preparatory_analyser.rs deleted file mode 100644 index 868657b3a..000000000 --- a/node/src/accountant/payment_adjuster/preparatory_analyser.rs +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; -use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ - log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, -}; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - TransactionCountsBy16bits, TransactionFeeLimitation, TransactionFeePastActionsContext, - WeightedPayable, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; -use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; -use ethereum_types::U256; -use futures::collect; -use itertools::{Either, Product}; -use masq_lib::logger::Logger; -use std::cmp::Ordering; - -pub struct PreparatoryAnalyzer {} - -impl PreparatoryAnalyzer { - pub fn new() -> Self { - Self {} - } - - pub fn determine_transaction_count_limit_by_transaction_fee( - &self, - agent: &dyn BlockchainAgent, - number_of_qualified_accounts: usize, - logger: &Logger, - ) -> Result, PaymentAdjusterError> { - let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); - let per_transaction_requirement_minor = - agent.estimated_transaction_fee_per_transaction_minor(); - - let verified_tx_counts = Self::transaction_counts_verification( - cw_transaction_fee_balance_minor, - per_transaction_requirement_minor, - number_of_qualified_accounts, - ); - - let max_tx_count_we_can_afford_u16 = verified_tx_counts.affordable; - let required_tx_count_u16 = verified_tx_counts.required; - - if max_tx_count_we_can_afford_u16 == 0 { - Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: number_of_qualified_accounts, - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor, - }, - ) - } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { - Ok(None) - } else { - log_insufficient_transaction_fee_balance( - logger, - required_tx_count_u16, - cw_transaction_fee_balance_minor, - max_tx_count_we_can_afford_u16, - ); - let transaction_fee_limitation_opt = todo!(); - Ok(Some(transaction_fee_limitation_opt)) - } - } - - fn transaction_counts_verification( - cw_transaction_fee_balance_minor: U256, - txn_fee_required_per_txn_minor: u128, - number_of_qualified_accounts: usize, - ) -> TransactionCountsBy16bits { - let max_possible_tx_count_u256 = - cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); - - TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) - } - - pub fn check_need_of_adjustment_by_service_fee( - &self, - disqualification_arbiter: &DisqualificationArbiter, - transaction_fee_past_actions_context: TransactionFeePastActionsContext, - payables: Vec, - cw_service_fee_balance_minor: u128, - logger: &Logger, - ) -> Result, Vec>, PaymentAdjusterError> - where - IncomingAccount: DisqualificationAnalysableAccount, - AnalyzedAccount: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, - { - let required_service_fee_total = - Self::compute_total_of_service_fee_required::(&payables); - - if cw_service_fee_balance_minor >= required_service_fee_total { - Ok(Either::Left(payables)) - } else { - let prepared_accounts = Self::prepare_accounts_with_disqualification_limits( - payables, - disqualification_arbiter, - ); - - let lowest_disqualification_limit = - Self::find_lowest_disqualification_limit(&prepared_accounts); - - Self::analyse_lowest_adjustment_possibility( - transaction_fee_past_actions_context, - lowest_disqualification_limit, - cw_service_fee_balance_minor, - )?; - - log_adjustment_by_service_fee_is_required( - logger, - required_service_fee_total, - cw_service_fee_balance_minor, - ); - - Ok(Either::Right(prepared_accounts)) - } - } - - fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 - where - Account: BalanceProvidingAccount, - { - sum_as(payables, |account| account.balance_minor()) - } - - fn prepare_accounts_with_disqualification_limits( - accounts: Vec, - disqualification_arbiter: &DisqualificationArbiter, - ) -> Vec - where - Account: DisqualificationAnalysableAccount, - Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, - { - accounts - .into_iter() - .map(|account| account.prepare_analyzable_account(disqualification_arbiter)) - .collect() - } - - fn find_lowest_disqualification_limit(accounts: &[Account]) -> u128 - where - Account: DisqualificationLimitProvidingAccount, - { - todo!() - } - - // We cannot do much in this area but stepping in if the cw balance is zero or nearly zero with - // the assumption that the debt with the lowest disqualification limit in the set fits in the - // available balance. If it doesn't, we're not going to bother the payment adjuster by that work, - // so it'll abort and no payments will come out. - fn analyse_lowest_adjustment_possibility( - transaction_fee_past_actions_context: TransactionFeePastActionsContext, - lowest_disqualification_limit: u128, - cw_service_fee_balance_minor: u128, - ) -> Result<(), PaymentAdjusterError> { - if lowest_disqualification_limit <= cw_service_fee_balance_minor { - Ok(()) - } else { - let (number_of_accounts, total_amount_demanded_minor, transaction_fee_appendix_opt): ( - usize, - u128, - Option, - ) = match transaction_fee_past_actions_context { - TransactionFeePastActionsContext::TransactionFeeCheckDone { limitation_opt } => { - todo!() - } - TransactionFeePastActionsContext::TransactionFeeAccountsDumped { - past_txs_count: txs_count, - past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, - } => todo!(), - }; - - // if let Some(info) = summary_for_potential_error_opt { - // (info.count, info.sum_of_balances) - // } else { - // let required_service_fee_total = - // sum_as(&prepared_accounts, |account| account.balance_minor()); - // (prepared_accounts.len(), required_service_fee_total) - // }; - Err( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts, - total_amount_demanded_minor, - cw_service_fee_balance_minor, - appendix_opt: transaction_fee_appendix_opt, - }, - ) - } - } -} - -pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount -where - Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, -{ - // fn process_findings(insufficiency_found: bool)-> - fn prepare_analyzable_account( - self, - disqualification_arbiter: &DisqualificationArbiter, - ) -> Product; -} - -pub trait BalanceProvidingAccount { - fn balance_minor(&self) -> u128; -} - -pub trait DisqualificationLimitProvidingAccount { - fn disqualification_limit(&self) -> u128; -} - -impl DisqualificationAnalysableAccount for QualifiedPayableAccount { - fn prepare_analyzable_account( - self, - disqualification_arbiter: &DisqualificationArbiter, - ) -> AnalyzedPayableAccount { - let dsq_limit = disqualification_arbiter.calculate_disqualification_edge(&self); - AnalyzedPayableAccount::new(self, dsq_limit) - } -} - -impl BalanceProvidingAccount for QualifiedPayableAccount { - fn balance_minor(&self) -> u128 { - self.bare_account.balance_wei - } -} - -impl BalanceProvidingAccount for AnalyzedPayableAccount { - fn balance_minor(&self) -> u128 { - todo!() - } -} - -impl DisqualificationLimitProvidingAccount for WeightedPayable { - fn disqualification_limit(&self) -> u128 { - todo!() - } -} - -impl DisqualificationAnalysableAccount for WeightedPayable { - fn prepare_analyzable_account( - self, - _disqualification_arbiter: &DisqualificationArbiter, - ) -> WeightedPayable { - self - } -} - -impl BalanceProvidingAccount for WeightedPayable { - fn balance_minor(&self) -> u128 { - self.analyzed_account.qualified_as.bare_account.balance_wei - } -} - -impl DisqualificationLimitProvidingAccount for AnalyzedPayableAccount { - fn disqualification_limit(&self) -> u128 { - todo!() - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::TransactionFeePastActionsContext; - use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; - use crate::accountant::payment_adjuster::test_utils::{ - make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, - }; - use crate::accountant::payment_adjuster::PaymentAdjusterError; - use crate::accountant::test_utils::make_non_guaranteed_qualified_payable; - use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; - use itertools::Either; - use masq_lib::logger::Logger; - use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use masq_lib::utils::convert_collection; - use std::sync::{Arc, Mutex}; - - fn test_adjustment_possibility_nearly_rejected( - test_name: &str, - disqualification_gauge: DisqualificationGaugeMock, - original_accounts: [QualifiedPayableAccount; 2], - cw_service_fee_balance: u128, - ) { - init_test_logging(); - let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let disqualification_gauge = - disqualification_gauge.determine_limit_params(&determine_limit_params_arc); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let subject = PreparatoryAnalyzer {}; - let service_fee_error_context = - TransactionFeePastActionsContext::accounts_dumped_context(&vec![]); - - let result = subject.check_need_of_adjustment_by_service_fee( - &disqualification_arbiter, - service_fee_error_context, - original_accounts.clone().to_vec(), - cw_service_fee_balance, - &Logger::new(test_name), - ); - - let expected_analyzed_accounts = convert_collection(original_accounts.to_vec()); - assert_eq!(result, Ok(Either::Right(expected_analyzed_accounts))); - let determine_limit_params = determine_limit_params_arc.lock().unwrap(); - let account_1 = &original_accounts[0]; - let account_2 = &original_accounts[1]; - let expected_params = vec![ - ( - account_1.bare_account.balance_wei, - account_1.payment_threshold_intercept_minor, - account_1.creditor_thresholds.permanent_debt_allowed_minor, - ), - ( - account_2.bare_account.balance_wei, - account_2.payment_threshold_intercept_minor, - account_2.creditor_thresholds.permanent_debt_allowed_minor, - ), - ]; - assert_eq!(*determine_limit_params, expected_params); - TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: Total of \ - blah wei in MASQ was ordered while the consuming wallet held only bluh wei of \ - the MASQ token. Adjustment in their count or the amounts is required." - )); - } - - #[test] - fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.bare_account.balance_wei = 1_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.bare_account.balance_wei = 2_000_000_000; - let cw_service_fee_balance = 750_000_001; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(750_000_000) - .determine_limit_result(1_500_000_000); - let original_accounts = [account_1, account_2]; - - test_adjustment_possibility_nearly_rejected( - "adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger", - disqualification_gauge, - original_accounts, - cw_service_fee_balance, - ) - } - - #[test] - fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.bare_account.balance_wei = 2_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(333); - account_2.bare_account.balance_wei = 1_000_000_000; - let cw_service_fee_balance = 750_000_000; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(1_500_000_000) - .determine_limit_result(750_000_000); - let original_accounts = [account_1, account_2]; - - test_adjustment_possibility_nearly_rejected( - "adjustment_possibility_nearly_rejected_when_cw_balance_equal", - disqualification_gauge, - original_accounts, - cw_service_fee_balance, - ) - } - - #[test] - fn insufficient_balance_for_even_the_least_demanding_account_causes_error() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); - account_1.bare_account.balance_wei = 2_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(222); - account_2.bare_account.balance_wei = 1_000_050_000; - let mut account_3 = make_non_guaranteed_qualified_payable(333); - account_3.bare_account.balance_wei = 1_000_111_111; - let cw_service_fee_balance = 1_000_000_100; - let original_accounts = vec![account_1, account_2, account_3]; - let required_fee_total = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(1_500_000_000) - .determine_limit_result(1_000_000_101) - .determine_limit_result(1_000_000_222); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let transaction_fee_past_actions_context = - TransactionFeePastActionsContext::TransactionFeeCheckDone { - limitation_opt: None, - }; - let subject = PreparatoryAnalyzer {}; - - let result = subject.check_need_of_adjustment_by_service_fee( - &disqualification_arbiter, - transaction_fee_past_actions_context, - original_accounts, - cw_service_fee_balance, - &Logger::new("test"), - ); - - assert_eq!( - result, - Err( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: 3, - total_amount_demanded_minor: required_fee_total, - cw_service_fee_balance_minor: cw_service_fee_balance, - appendix_opt: None, - } - ) - ) - } - - #[test] - fn accounts_analyzing_works_even_for_weighted_payable() { - let weighted_account_1 = make_weighed_account(123); - let weighted_account_2 = make_weighed_account(456); - let accounts = vec![weighted_account_1, weighted_account_2]; - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(DisqualificationGaugeMock::default())); - - let analyzed_accounts = PreparatoryAnalyzer::prepare_accounts_with_disqualification_limits( - accounts.clone(), - &disqualification_arbiter, - ); - - assert_eq!(analyzed_accounts, accounts) - } - - #[test] - fn fold_for_find_lowest_weight_and_prepare_accounts_to_proceed_starts_with_u128_max() { - let accounts: Vec = vec![]; - - let minimal_disqualification_limit = - PreparatoryAnalyzer::find_lowest_disqualification_limit(&accounts); - - assert_eq!(minimal_disqualification_limit, u128::MAX); - } -} diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs new file mode 100644 index 000000000..c771ecc98 --- /dev/null +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs @@ -0,0 +1,71 @@ +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeightedPayable; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; + +pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount +where + Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, +{ + // fn process_findings(insufficiency_found: bool)-> + fn prepare_analyzable_account( + self, + disqualification_arbiter: &DisqualificationArbiter, + ) -> Product; +} + +pub trait BalanceProvidingAccount { + fn balance_minor(&self) -> u128; +} + +pub trait DisqualificationLimitProvidingAccount { + fn disqualification_limit(&self) -> u128; +} + +impl DisqualificationAnalysableAccount for WeightedPayable { + fn prepare_analyzable_account( + self, + _disqualification_arbiter: &DisqualificationArbiter, + ) -> WeightedPayable { + self + } +} + +impl BalanceProvidingAccount for WeightedPayable { + fn balance_minor(&self) -> u128 { + self.analyzed_account.balance_minor() + } +} + +impl DisqualificationLimitProvidingAccount for WeightedPayable { + fn disqualification_limit(&self) -> u128 { + self.analyzed_account.disqualification_limit() + } +} + +impl DisqualificationLimitProvidingAccount for AnalyzedPayableAccount { + fn disqualification_limit(&self) -> u128 { + self.disqualification_limit_minor + } +} + +impl BalanceProvidingAccount for AnalyzedPayableAccount { + fn balance_minor(&self) -> u128 { + self.qualified_as.balance_minor() + } +} + +impl DisqualificationAnalysableAccount for QualifiedPayableAccount { + fn prepare_analyzable_account( + self, + disqualification_arbiter: &DisqualificationArbiter, + ) -> AnalyzedPayableAccount { + let dsq_limit = disqualification_arbiter.calculate_disqualification_edge(&self); + AnalyzedPayableAccount::new(self, dsq_limit) + } +} + +impl BalanceProvidingAccount for QualifiedPayableAccount { + fn balance_minor(&self) -> u128 { + self.bare_account.balance_wei + } +} diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs new file mode 100644 index 000000000..09c5e1afe --- /dev/null +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -0,0 +1,542 @@ +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod accounts_abstraction; + +use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; +use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ + log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, +}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustmentPossibilityErrorBuilder, TransactionCountsBy16bits, TransactionFeeLimitation, + TransactionFeePastCheckContext, WeightedPayable, +}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + find_smallest_u128, sum_as, +}; +use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ + BalanceProvidingAccount, DisqualificationAnalysableAccount, + DisqualificationLimitProvidingAccount, +}; +use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; +use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use ethereum_types::U256; +use itertools::{Either, Product}; +use masq_lib::logger::Logger; + +pub struct PreparatoryAnalyzer {} + +impl PreparatoryAnalyzer { + pub fn new() -> Self { + Self {} + } + + pub fn analyze_accounts( + &self, + agent: &dyn BlockchainAgent, + disqualification_arbiter: &DisqualificationArbiter, + qualified_payables: Vec, + logger: &Logger, + ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> + { + let number_of_counts = qualified_payables.len(); + let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); + let per_transaction_requirement_minor = + agent.estimated_transaction_fee_per_transaction_minor(); + + let transaction_fee_limitation_opt = self + .determine_transaction_count_limit_by_transaction_fee( + cw_transaction_fee_balance_minor, + per_transaction_requirement_minor, + number_of_counts, + logger, + )?; + + let cw_service_fee_balance_minor = agent.service_fee_balance_minor(); + let is_service_fee_adjustment_needed = Self::is_service_fee_adjustment_needed( + &qualified_payables, + cw_service_fee_balance_minor, + logger, + ); + + if transaction_fee_limitation_opt.is_none() && !is_service_fee_adjustment_needed { + Ok(Either::Left(qualified_payables)) + } else { + let prepared_accounts = Self::pre_process_accounts_for_adjustments( + qualified_payables, + disqualification_arbiter, + ); + if is_service_fee_adjustment_needed { + let error_builder = AdjustmentPossibilityErrorBuilder::default() + .check_done_context(transaction_fee_limitation_opt); + // TODO rewrite me as ? + match Self::check_adjustment_possibility( + &prepared_accounts, + cw_service_fee_balance_minor, + error_builder, + ) { + Err(e) => todo!(), + _ => (), + } + }; + let adjustment = match transaction_fee_limitation_opt { + None => Adjustment::ByServiceFee, + Some(limit) => { + todo!() + } + }; + Ok(Either::Right(AdjustmentAnalysis::new( + adjustment, + prepared_accounts, + ))) + } + } + + pub fn recheck_if_service_fee_adjustment_is_needed( + &self, + weighted_accounts: &[WeightedPayable], + cw_service_fee_balance_minor: u128, + error_builder: AdjustmentPossibilityErrorBuilder, + logger: &Logger, + ) -> Result { + if Self::is_service_fee_adjustment_needed( + weighted_accounts, + cw_service_fee_balance_minor, + logger, + ) { + //TODO change to ? + match Self::check_adjustment_possibility( + weighted_accounts, + cw_service_fee_balance_minor, + error_builder, + ) { + Ok(_) => Ok(true), + Err(e) => todo!(), + } + } else { + Ok(false) + } + } + + fn determine_transaction_count_limit_by_transaction_fee( + &self, + cw_transaction_fee_balance_minor: U256, + per_transaction_requirement_minor: u128, + number_of_qualified_accounts: usize, + logger: &Logger, + ) -> Result, PaymentAdjusterError> { + let verified_tx_counts = Self::transaction_counts_verification( + cw_transaction_fee_balance_minor, + per_transaction_requirement_minor, + number_of_qualified_accounts, + ); + + let max_tx_count_we_can_afford_u16 = verified_tx_counts.affordable; + let required_tx_count_u16 = verified_tx_counts.required; + + if max_tx_count_we_can_afford_u16 == 0 { + Err( + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: number_of_qualified_accounts, + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, + }, + ) + } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { + Ok(None) + } else { + log_insufficient_transaction_fee_balance( + logger, + required_tx_count_u16, + cw_transaction_fee_balance_minor, + max_tx_count_we_can_afford_u16, + ); + let transaction_fee_limitation_opt = TransactionFeeLimitation::new( + max_tx_count_we_can_afford_u16, + cw_transaction_fee_balance_minor.as_u128(), + per_transaction_requirement_minor, + ); + Ok(Some(transaction_fee_limitation_opt)) + } + } + + fn transaction_counts_verification( + cw_transaction_fee_balance_minor: U256, + txn_fee_required_per_txn_minor: u128, + number_of_qualified_accounts: usize, + ) -> TransactionCountsBy16bits { + let max_possible_tx_count_u256 = + cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); + + TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) + } + + fn check_adjustment_possibility( + prepared_accounts: &[AnalyzableAccounts], + cw_service_fee_balance_minor: u128, + error_builder: AdjustmentPossibilityErrorBuilder, + ) -> Result<(), PaymentAdjusterError> + where + AnalyzableAccounts: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, + { + let lowest_disqualification_limit = + Self::find_lowest_disqualification_limit(&prepared_accounts); + + // We cannot do much in this area but stepping in if the cw balance is zero or nearly + // zero with the assumption that the debt with the lowest disqualification limit in + // the set fits in the available balance. If it doesn't, we're not going to bother + // the payment adjuster by that work, so it'll abort and no payments will come out. + if lowest_disqualification_limit <= cw_service_fee_balance_minor { + Ok(()) + } else { + let analyzed_accounts_count = prepared_accounts.len(); + let required_service_fee_total = + Self::compute_total_of_service_fee_required(prepared_accounts); + let err = error_builder + .all_time_supplied_parameters( + analyzed_accounts_count, + required_service_fee_total, + cw_service_fee_balance_minor, + ) + .build(); + Err(err) + } + } + + fn pre_process_accounts_for_adjustments( + accounts: Vec, + disqualification_arbiter: &DisqualificationArbiter, + ) -> Vec { + accounts + .into_iter() + .map(|account| { + let disqualification_limit = + disqualification_arbiter.calculate_disqualification_edge(&account); + AnalyzedPayableAccount::new(account, disqualification_limit) + }) + .collect() + } + + fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 + where + Account: BalanceProvidingAccount, + { + sum_as(payables, |account| account.balance_minor()) + } + + fn is_service_fee_adjustment_needed( + qualified_payables: &[Account], + cw_service_fee_balance_minor: u128, + logger: &Logger, + ) -> bool + where + Account: BalanceProvidingAccount, + { + let service_fee_totally_required_minor = + Self::compute_total_of_service_fee_required(qualified_payables); + log_adjustment_by_service_fee_is_required( + logger, + service_fee_totally_required_minor, + cw_service_fee_balance_minor, + ); + service_fee_totally_required_minor > cw_service_fee_balance_minor + } + + fn find_lowest_disqualification_limit(accounts: &[Account]) -> u128 + where + Account: DisqualificationLimitProvidingAccount, + { + find_smallest_u128( + &accounts + .iter() + .map(|account| account.disqualification_limit()) + .collect::>(), + ) + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, + }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustmentPossibilityErrorBuilder, TransactionFeeLimitation, TransactionFeePastCheckContext, + }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; + use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; + use crate::accountant::payment_adjuster::test_utils::{ + make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, + }; + use crate::accountant::payment_adjuster::{ + Adjustment, AdjustmentAnalysis, PaymentAdjusterError, + }; + use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::test_utils::{ + make_analyzed_account, make_non_guaranteed_qualified_payable, + }; + use crate::accountant::QualifiedPayableAccount; + use itertools::Either; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use std::sync::{Arc, Mutex}; + use thousands::Separable; + use web3::types::U256; + + fn test_adjustment_possibility_nearly_rejected( + test_name: &str, + disqualification_gauge: DisqualificationGaugeMock, + original_accounts: [QualifiedPayableAccount; 2], + cw_service_fee_balance: u128, + ) { + init_test_logging(); + let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); + let disqualification_gauge = double_mock_results_queue(disqualification_gauge) + .determine_limit_params(&determine_limit_params_arc); + let total_amount_required: u128 = sum_as(original_accounts.as_slice(), |account| { + account.bare_account.balance_wei + }); + let disqualification_arbiter = + DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let subject = PreparatoryAnalyzer {}; + let blockchain_agent = BlockchainAgentMock::default() + .transaction_fee_balance_minor_result(U256::MAX) + .estimated_transaction_fee_per_transaction_minor_result(123456) + .service_fee_balance_minor_result(cw_service_fee_balance); + + let result = subject.analyze_accounts( + &blockchain_agent, + &disqualification_arbiter, + original_accounts.clone().to_vec(), + &Logger::new(test_name), + ); + + let expected_adjustment_analysis = { + let analyzed_accounts = PreparatoryAnalyzer::pre_process_accounts_for_adjustments( + original_accounts.to_vec(), + &disqualification_arbiter, + ); + AdjustmentAnalysis::new(Adjustment::ByServiceFee, analyzed_accounts) + }; + assert_eq!(result, Ok(Either::Right(expected_adjustment_analysis))); + let determine_limit_params = determine_limit_params_arc.lock().unwrap(); + let account_1 = &original_accounts[0]; + let account_2 = &original_accounts[1]; + let expected_params = vec![ + ( + account_1.bare_account.balance_wei, + account_1.payment_threshold_intercept_minor, + account_1.creditor_thresholds.permanent_debt_allowed_minor, + ), + ( + account_2.bare_account.balance_wei, + account_2.payment_threshold_intercept_minor, + account_2.creditor_thresholds.permanent_debt_allowed_minor, + ), + ]; + assert_eq!(&determine_limit_params[0..2], expected_params); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet \ + held only {} wei of MASQ token. Adjustment of the count or amounts is required.", + total_amount_required.separate_with_commas(), + cw_service_fee_balance.separate_with_commas() + )); + } + + #[test] + fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.bare_account.balance_wei = 1_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(333); + account_2.bare_account.balance_wei = 2_000_000_000; + let cw_service_fee_balance = 750_000_001; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(750_000_000) + .determine_limit_result(1_500_000_000); + let original_accounts = [account_1, account_2]; + + test_adjustment_possibility_nearly_rejected( + "adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger", + disqualification_gauge, + original_accounts, + cw_service_fee_balance, + ) + } + + #[test] + fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { + let mut account_1 = make_non_guaranteed_qualified_payable(111); + account_1.bare_account.balance_wei = 2_000_000_000; + let mut account_2 = make_non_guaranteed_qualified_payable(333); + account_2.bare_account.balance_wei = 1_000_000_000; + let cw_service_fee_balance = 750_000_000; + let disqualification_gauge = DisqualificationGaugeMock::default() + .determine_limit_result(1_500_000_000) + .determine_limit_result(750_000_000); + let original_accounts = [account_1, account_2]; + + test_adjustment_possibility_nearly_rejected( + "adjustment_possibility_nearly_rejected_when_cw_balance_equal", + disqualification_gauge, + original_accounts, + cw_service_fee_balance, + ) + } + + fn test_not_enough_for_even_the_least_demanding_account_causes_error( + error_builder: AdjustmentPossibilityErrorBuilder, + expected_error_preparer: F, + ) where + F: FnOnce(u128, u128) -> PaymentAdjusterError, + { + let mut account_1 = make_analyzed_account(111); + account_1.qualified_as.bare_account.balance_wei = 2_000_000_000; + account_1.disqualification_limit_minor = 1_500_000_000; + let mut account_2 = make_analyzed_account(222); + account_2.qualified_as.bare_account.balance_wei = 1_000_050_000; + account_2.disqualification_limit_minor = 1_000_000_101; + let mut account_3 = make_analyzed_account(333); + account_3.qualified_as.bare_account.balance_wei = 1_000_111_111; + account_3.disqualification_limit_minor = 1_000_000_222; + let cw_service_fee_balance = 1_000_000_100; + let original_accounts = vec![account_1, account_2, account_3]; + let service_fee_total_of_the_known_set = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; + let subject = PreparatoryAnalyzer {}; + + let result = PreparatoryAnalyzer::check_adjustment_possibility( + &original_accounts, + cw_service_fee_balance, + error_builder, + ); + + let expected_error = + expected_error_preparer(service_fee_total_of_the_known_set, cw_service_fee_balance); + assert_eq!(result, Err(expected_error)) + } + + #[test] + fn not_enough_for_even_the_least_demanding_account_error_right_after_positive_tx_fee_check() { + let transaction_fee_limitation = TransactionFeeLimitation { + count_limit: 2, + cw_transaction_fee_balance_minor: 200_000_000, + per_transaction_required_fee_minor: 300_000_000, + }; + let error_builder = AdjustmentPossibilityErrorBuilder::default() + .check_done_context(Some(transaction_fee_limitation)); + let expected_error_preparer = + |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: 3, + total_service_fee_required_minor: total_amount_demanded_in_accounts_in_place, + cw_service_fee_balance_minor, + transaction_fee_appendix_opt: Some(transaction_fee_limitation), + } + }; + + test_not_enough_for_even_the_least_demanding_account_causes_error( + error_builder, + expected_error_preparer, + ) + } + + #[test] + fn not_enough_for_even_the_least_demanding_account_error_right_after_negative_tx_fee_check() { + let error_builder = AdjustmentPossibilityErrorBuilder::default().check_done_context(None); + let expected_error_preparer = + |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: 3, + total_service_fee_required_minor: total_amount_demanded_in_accounts_in_place, + cw_service_fee_balance_minor, + transaction_fee_appendix_opt: None, + } + }; + + test_not_enough_for_even_the_least_demanding_account_causes_error( + error_builder, + expected_error_preparer, + ) + } + + #[test] + fn not_enough_for_even_the_least_demanding_account_error_right_after_tx_fee_accounts_dump() { + let accounts = vec![ + make_weighed_account(123), + make_weighed_account(456), + make_weighed_account(789), + make_weighed_account(1011), + ]; + let initial_sum = sum_as(&accounts, |account| account.balance_minor()); + let initial_count = accounts.len(); + let error_builder = + AdjustmentPossibilityErrorBuilder::default().accounts_dumped_context(&accounts); + let expected_error_preparer = |_, cw_service_fee_balance_minor| { + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: initial_count, + total_service_fee_required_minor: initial_sum, + cw_service_fee_balance_minor, + transaction_fee_appendix_opt: None, + } + }; + + test_not_enough_for_even_the_least_demanding_account_causes_error( + error_builder, + expected_error_preparer, + ) + } + + #[test] + fn accounts_analyzing_works_even_for_weighted_payable() { + init_test_logging(); + let test_name = "accounts_analyzing_works_even_for_weighted_payable"; + let balance_1 = multiple_by_billion(2_000_000); + let mut weighted_account_1 = make_weighed_account(123); + weighted_account_1 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = balance_1; + let balance_2 = multiple_by_billion(3_456_000); + let mut weighted_account_2 = make_weighed_account(456); + weighted_account_2 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = balance_2; + let accounts = vec![weighted_account_1, weighted_account_2]; + let service_fee_totally_required_minor = balance_1 + balance_2; + let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1; + let error_builder = AdjustmentPossibilityErrorBuilder::default(); + let logger = Logger::new(test_name); + let subject = PreparatoryAnalyzer::new(); + + [(0, false),(1, false),(2, true)].iter().for_each(|(subtrahend_from_cw_balance, expected_result)| { + let service_fee_balance = cw_service_fee_balance_minor - subtrahend_from_cw_balance; + let result = subject.recheck_if_service_fee_adjustment_is_needed( + &accounts, + service_fee_balance, + error_builder.clone(), + &logger + ).unwrap(); + assert_eq!(result, *expected_result); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet \ + held only {}", + service_fee_totally_required_minor.separate_with_commas(), service_fee_balance.separate_with_commas() + )); + }) + } + + fn double_mock_results_queue(mock: DisqualificationGaugeMock) -> DisqualificationGaugeMock { + let originally_prepared_results = (0..2) + .map(|_| mock.determine_limit(0, 0, 0)) + .collect::>(); + originally_prepared_results + .into_iter() + .cycle() + .take(4) + .fold(mock, |mock, result_to_be_added| { + mock.determine_limit_result(result_to_be_added) + }) + } +} diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index f72f6065d..724cb05a5 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -61,39 +61,6 @@ impl Default for ServiceFeeAdjusterReal { } impl ServiceFeeAdjusterReal { - pub fn assign_accounts_their_minimal_acceptable_balance( - accounts: Vec, - disqualification_arbiter: &DisqualificationArbiter, - ) -> Vec - where - WeightedPayable: From, - OutputAccounts: From, - { - // In some cases taking advantage of that Rust std library implements also From for T - todo!() - // let weighted_accounts: Vec = convert_collection(accounts); - // - // let unconfirmed_accounts = weighted_accounts - // .into_iter() - // .map(|weighted_account| { - // let disqualification_limit = disqualification_arbiter - // .calculate_disqualification_edge( - // &weighted_account.analyzed_account.qualified_as, - // ); - // minimal_acceptable_balance_assigned_diagnostics( - // &weighted_account, - // disqualification_limit, - // ); - // UnconfirmedAdjustment::new( - // weighted_account, - // disqualification_limit, - // ) - // }) - // .collect(); - // - // convert_collection(unconfirmed_accounts) - } - fn new() -> Self { Self { adjustment_computer: Default::default(), @@ -383,66 +350,4 @@ mod tests { ]; assert_eq!(sufficient_gainers, expected_adjusted_outweighed_accounts) } - - #[test] - fn assign_accounts_their_minimal_acceptable_balance_works_for_unconfirmed_accounts() { - let unconfirmed_account_1 = make_non_guaranteed_unconfirmed_adjustment(111); - let weighted_account_1 = unconfirmed_account_1.weighted_account.clone(); - let unconfirmed_account_2 = make_non_guaranteed_unconfirmed_adjustment(222); - let weighted_account_2 = unconfirmed_account_2.weighted_account.clone(); - let accounts = vec![unconfirmed_account_1, unconfirmed_account_2]; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(123456789) - .determine_limit_result(987654321); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - - let result: Vec = - ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( - accounts, - &disqualification_arbiter, - ); - - let expected_result = vec![ - UnconfirmedAdjustment::new(weighted_account_1, 123456789), - UnconfirmedAdjustment::new(weighted_account_2, 987654321), - ]; - assert_eq!(result, expected_result) - } - - #[test] - fn assign_accounts_their_minimal_acceptable_balance_works_for_non_finalized_accounts() { - let unconfirmed_account_1 = make_non_guaranteed_unconfirmed_adjustment(111); - let payable_account_1 = unconfirmed_account_1 - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .clone(); - let unconfirmed_account_2 = make_non_guaranteed_unconfirmed_adjustment(222); - let payable_account_2 = unconfirmed_account_2 - .weighted_account - .analyzed_account - .qualified_as - .bare_account - .clone(); - let accounts = vec![unconfirmed_account_1, unconfirmed_account_2]; - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(1111111) - .determine_limit_result(2222222); - let disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); - - let result: Vec = - ServiceFeeAdjusterReal::assign_accounts_their_minimal_acceptable_balance( - accounts, - &disqualification_arbiter, - ); - - let expected_result = vec![ - AdjustedAccountBeforeFinalization::new(payable_account_1, 1111111), - AdjustedAccountBeforeFinalization::new(payable_account_2, 2222222), - ]; - assert_eq!(result, expected_result) - } } From 369a8e8658c4ff7fae3f0921a50ab4a878d54c05 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Apr 2024 01:16:41 +0200 Subject: [PATCH 170/250] GH-711-c: All is getting sewed into one functional piece. --- .../payment_adjuster/adjustment_runners.rs | 50 +++--- .../logging_and_diagnostics/diagnostics.rs | 21 --- .../logging_and_diagnostics/log_functions.rs | 28 +-- .../account_stages_conversions.rs | 5 + .../miscellaneous/data_structures.rs | 101 +++++++---- node/src/accountant/payment_adjuster/mod.rs | 166 +++++++----------- .../preparatory_analyser/mod.rs | 98 ++++++----- .../payment_adjuster/service_fee_adjuster.rs | 36 ++-- 8 files changed, 247 insertions(+), 258 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index cea75cbf1..2c3f8b992 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -5,9 +5,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; use itertools::Either; +use masq_lib::utils::convert_collection; // TODO review this comment // There are only two runners. They perform adjustment either by both the transaction and service @@ -78,7 +78,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { if check_sum <= unallocated_cw_balance { // Fast return after a direct conversion into the expected type - todo!() + return convert_collection(weighted_accounts); } payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts) @@ -93,7 +93,9 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; - use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; + use crate::accountant::payment_adjuster::test_utils::{ + make_initialized_subject, multiple_by_billion, + }; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_analyzed_account, make_non_guaranteed_qualified_payable, @@ -129,6 +131,7 @@ mod tests { } fn test_surplus_incurred_after_disqualification_in_previous_iteration( + subject: ServiceFeeOnlyAdjustmentRunner, payable_1: WeightedPayable, payable_2: WeightedPayable, cw_service_fee_balance_minor: u128, @@ -146,7 +149,6 @@ mod tests { initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); let initial_balance_minor_1 = payable_1.balance_minor(); let initial_balance_minor_2 = payable_2.balance_minor(); - let subject = ServiceFeeOnlyAdjustmentRunner {}; let result = subject.adjust_accounts( &mut payment_adjuster, @@ -183,14 +185,18 @@ mod tests { } #[test] - fn service_fee_only_runner_cw_balance_equals_requested_money_after_dsq_in_previous_iteration() { + fn means_equal_requested_money_after_dsq_in_previous_iteration_to_return_capped_accounts() { + let subject = ServiceFeeOnlyAdjustmentRunner {}; let cw_service_fee_balance_minor = 10_000_000_000; - let payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); - let payable_2 = weighted_payable_setup_for_surplus_test(222, 5_000_000_000); - let expected_proposed_balance_1 = 3_000_000_000; - let expected_proposed_balance_2 = 3_000_000_000; + let mut payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); + payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; + let mut payable_2 = weighted_payable_setup_for_surplus_test(222, 5_000_000_000); + payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; + let expected_proposed_balance_1 = 3_444_333_444; + let expected_proposed_balance_2 = 3_555_333_555; test_surplus_incurred_after_disqualification_in_previous_iteration( + subject, payable_1, payable_2, cw_service_fee_balance_minor, @@ -200,15 +206,19 @@ mod tests { } #[test] - fn service_fee_only_runner_handles_means_bigger_requested_money_after_dsq_in_previous_iteration( + fn means_become_bigger_than_requested_after_dsq_in_previous_iteration_to_return_capped_accounts( ) { + let subject = ServiceFeeOnlyAdjustmentRunner {}; let cw_service_fee_balance_minor = 10_000_000_000; - let payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); - let payable_2 = weighted_payable_setup_for_surplus_test(222, 4_999_999_999); - let expected_proposed_balance_1 = 3_000_000_000; - let expected_proposed_balance_2 = 2_999_999_999; + let mut payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); + payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; + let mut payable_2 = weighted_payable_setup_for_surplus_test(222, 4_999_999_999); + payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; + let expected_proposed_balance_1 = 3_444_333_444; + let expected_proposed_balance_2 = 3_555_333_555; test_surplus_incurred_after_disqualification_in_previous_iteration( + subject, payable_1, payable_2, cw_service_fee_balance_minor, @@ -219,15 +229,9 @@ mod tests { #[test] fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { - let mut payment_thresholds = PaymentThresholds::default(); - payment_thresholds.maturity_threshold_sec = 100; - payment_thresholds.threshold_interval_sec = 1000; - payment_thresholds.permanent_debt_allowed_gwei = 1; let balance = 5_000_000_000; let mut account = make_non_guaranteed_qualified_payable(111); - account.bare_account.balance_wei = 5_000_000_000; - account.payment_threshold_intercept_minor = 4_000_000_000; - account.creditor_thresholds = CreditorThresholds::new(1_000_000_000); + account.bare_account.balance_wei = balance; let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); let mut account_1 = account.clone(); @@ -237,7 +241,7 @@ mod tests { let adjustment = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; - let service_fee_balance_minor = (5 * balance) / 3; + let service_fee_balance_minor = (10 * balance) / 8; let mut payment_adjuster = PaymentAdjusterReal::new(); payment_adjuster.initialize_inner( service_fee_balance_minor, @@ -247,7 +251,7 @@ mod tests { ); let subject = ServiceFeeOnlyAdjustmentRunner {}; let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { - analyzed_account: AnalyzedPayableAccount::new(account, 5_000_000_000), + analyzed_account: AnalyzedPayableAccount::new(account, 3_000_000_000), weight: 4_000_000_000, }; let weighted_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 40e5f9561..c1640afc6 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -139,27 +139,6 @@ pub mod ordinary_diagnostic_functions { ) } - pub fn handle_last_account_diagnostics( - account: &WeightedPayable, - cw_service_fee_balance_minor: u128, - disqualification_limit_opt: Option, - ) { - diagnostics!( - account.wallet(), - "HANDLING LAST ACCOUNT", - "Remaining CW balance {} is {}", - cw_service_fee_balance_minor, - if let Some(dsq_limit) = disqualification_limit_opt { - format!( - "larger than the disqualification limit {} which is therefore assigned instead", - dsq_limit - ) - } else { - "below the disqualification limit and assigned in full extend".to_string() - } - ) - } - pub fn exhausting_cw_balance_diagnostics( non_finalized_account_info: &AdjustedAccountBeforeFinalization, possible_extra_addition: u128, diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index ec0a17b6f..481f49748 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -16,9 +16,10 @@ use web3::types::U256; const REFILL_RECOMMENDATION: &str = "\ Please be aware that abandoning your debts is going to result in delinquency bans. In order to \ consume services without limitations, you will need to place more funds into your consuming wallet."; -const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ -Passed successfully adjustment by transaction fee, however, that was not enough regarding the other \ -fee. Now, a critical shortage of MASQ balance has been noticed. Operation will abort."; +pub const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ +Passed successfully adjustment by transaction fee, then rechecked the service fee balance to be \ +applied on the adjusted set, but discovered a shortage of MASQ not to suffice even for a single \ +transaction. Operation is aborting."; const BLANK_SPACE: &str = ""; @@ -143,7 +144,7 @@ pub fn log_adjustment_by_service_fee_is_required( warning!( logger, "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of MASQ \ - token. Adjustment of the count or amounts is required.", + token. Adjustment of their count or balances is required.", payables_sum.separate_with_commas(), cw_service_fee_balance.separate_with_commas() ); @@ -158,9 +159,8 @@ pub fn log_insufficient_transaction_fee_balance( ) { warning!( logger, - "Transaction fee amount {} wei from your wallet will not cover anticipated \ - fees to send {} transactions. Maximum is {}. The payments count needs to be \ - adjusted.", + "Your transaction fee balance {} wei is not going to cover the anticipated fees to send {} \ + transactions. Maximum is set to {}. Adjustment will be performed.", transaction_fee_minor.separate_with_commas(), required_transactions_count, limiting_count @@ -193,9 +193,9 @@ mod tests { ); assert_eq!( LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, - "Passed successfully adjustment by transaction fee, however, that was not enough \ - regarding the other fee. Now, a critical shortage of MASQ balance has been noticed. \ - Operation will abort." + "Passed successfully adjustment by transaction fee, then rechecked the service fee \ + balance to be applied on the adjusted set, but discovered a shortage of \ + MASQ not to suffice even for a single transaction. Operation is aborting." ) } @@ -215,10 +215,10 @@ mod tests { info_log_for_disqualified_account(&logger, &disqualified_account); TestLogHandler::new().exists_log_containing(&format!( - "INFO: {}: Shortage of MASQ \ - in your consuming wallet will impact payable 0x0000000000000000000000000000000000616161, \ - ruled out from this round of payments. The proposed adjustment 1,555,666,777 wei was \ - below the disqualification limit 2,000,000,000 wei", + "INFO: {}: Shortage of MASQ in your consuming wallet will impact payable \ + 0x0000000000000000000000000000000000616161, ruled out from this round of payments. \ + The proposed adjustment 1,555,666,777 wei was below the disqualification limit \ + 2,000,000,000 wei", test_name )); } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index e6bf6d70c..680873ff9 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -1,6 +1,7 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::minimal_acceptable_balance_assigned_diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; @@ -59,6 +60,10 @@ impl From for AdjustedAccountBeforeFinalization { impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { let limited_adjusted_balance = weighted_account.disqualification_limit(); + minimal_acceptable_balance_assigned_diagnostics( + &weighted_account, + limited_adjusted_balance, + ); let original_account = weighted_account.analyzed_account.qualified_as.bare_account; AdjustedAccountBeforeFinalization::new(original_account, limited_adjusted_balance) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index bd3cf288c..f70f34cf6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -135,27 +135,14 @@ pub struct AdjustmentPossibilityErrorBuilder { } impl AdjustmentPossibilityErrorBuilder { - pub fn accounts_dumped_context( - mut self, - whole_set_of_analyzed_accounts: &[WeightedPayable], - ) -> Self { - let past_txs_count = whole_set_of_analyzed_accounts.len(); - let past_sum_of_service_fee_balances: u128 = - sum_as(whole_set_of_analyzed_accounts, |account| { - account.balance_minor() - }); - self.context_opt.replace( - TransactionFeePastCheckContext::TransactionFeeAccountsDumped { - past_txs_count, - past_sum_of_service_fee_balances, - }, - ); - self - } - - pub fn check_done_context(mut self, limitation_opt: Option) -> Self { - self.context_opt - .replace(TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt }); + pub fn context(mut self, context: TransactionFeePastCheckContext) -> Self { + if let Some(old) = self.context_opt.replace(context) { + panic!( + "Context must be supplied only once. Was {:?} and {:?} is being set", + old, + self.context_opt.expect("just put in there") + ) + } self } @@ -171,6 +158,18 @@ impl AdjustmentPossibilityErrorBuilder { self } + pub fn build(self) -> PaymentAdjusterError { + let cw_service_fee_balance_minor = self.cw_service_fee_balance_minor; + let (number_of_accounts, total_service_fee_required_minor, transaction_fee_appendix_opt) = + self.derive_params(); + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts, + total_service_fee_required_minor, + cw_service_fee_balance_minor, + transaction_fee_appendix_opt, + } + } + fn derive_params(self) -> (usize, u128, Option) { match self.context_opt.expectv("Tx fee past check context") { TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt } => ( @@ -184,18 +183,6 @@ impl AdjustmentPossibilityErrorBuilder { } => (past_txs_count, past_sum_of_service_fee_balances, None), } } - - pub fn build(self) -> PaymentAdjusterError { - let cw_service_fee_balance_minor = self.cw_service_fee_balance_minor; - let (number_of_accounts, total_service_fee_required_minor, transaction_fee_appendix_opt) = - self.derive_params(); - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts, - total_service_fee_required_minor, - cw_service_fee_balance_minor, - transaction_fee_appendix_opt, - } - } } #[derive(Debug, Clone)] @@ -209,6 +196,25 @@ pub enum TransactionFeePastCheckContext { }, } +impl TransactionFeePastCheckContext { + pub fn accounts_dumped(whole_set_of_analyzed_accounts: &[WeightedPayable]) -> Self { + let past_txs_count = whole_set_of_analyzed_accounts.len(); + let past_sum_of_service_fee_balances: u128 = + sum_as(whole_set_of_analyzed_accounts, |account| { + account.balance_minor() + }); + + TransactionFeePastCheckContext::TransactionFeeAccountsDumped { + past_txs_count, + past_sum_of_service_fee_balances, + } + } + + pub fn initial_check_done(limitation_opt: Option) -> Self { + TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct TransactionFeeLimitation { pub count_limit: u16, @@ -234,7 +240,7 @@ impl TransactionFeeLimitation { mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentPossibilityErrorBuilder, RecursionResults, - TransactionCountsBy16bits, TransactionFeePastCheckContext, + TransactionCountsBy16bits, TransactionFeeLimitation, TransactionFeePastCheckContext, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::make_payable_account; @@ -320,6 +326,28 @@ mod tests { } } + #[test] + #[should_panic( + expected = "Context must be supplied only once. Was TransactionFeeCheckDone { \ + limitation_opt: None } and TransactionFeeCheckDone { limitation_opt: Some(TransactionFeeLimitation \ + { count_limit: 11, cw_transaction_fee_balance_minor: 22, per_transaction_required_fee_minor: 3 }) } \ + is being set" + )] + fn context_can_be_called_just_once() { + let mut subject = AdjustmentPossibilityErrorBuilder::default(); + subject.context_opt = Some(TransactionFeePastCheckContext::TransactionFeeCheckDone { + limitation_opt: None, + }); + + let _ = subject.context(TransactionFeePastCheckContext::TransactionFeeCheckDone { + limitation_opt: Some(TransactionFeeLimitation { + count_limit: 11, + cw_transaction_fee_balance_minor: 22, + per_transaction_required_fee_minor: 3, + }), + }); + } + #[test] fn construction_of_error_context_with_accounts_dumped_works() { let mut account_1 = make_weighed_account(123); @@ -336,10 +364,9 @@ mod tests { .balance_wei = 999888777; let weighted_accounts = vec![account_1, account_2]; - let builder = AdjustmentPossibilityErrorBuilder::default() - .accounts_dumped_context(&weighted_accounts); + let context = TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts); - match builder.context_opt.unwrap() { + match context { TransactionFeePastCheckContext::TransactionFeeAccountsDumped { past_txs_count: txs_count, past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 63600fab5..e7e60bb0a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -254,8 +254,9 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let error_builder = AdjustmentPossibilityErrorBuilder::default() - .accounts_dumped_context(&weighted_accounts_in_descending_order); + let error_builder = AdjustmentPossibilityErrorBuilder::default().context( + TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts_in_descending_order), + ); let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( @@ -271,38 +272,20 @@ impl PaymentAdjusterReal { error_builder, &self.logger, )? { - todo!() + diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); + + let adjustment_result_before_verification = self + .propose_possible_adjustment_recursively( + weighted_accounts_affordable_by_transaction_fee, + ); + + Ok(Either::Left(adjustment_result_before_verification)) } else { - todo!() - } + let accounts_not_needing_adjustment = + convert_collection(weighted_accounts_affordable_by_transaction_fee); - // match self.analyzer.check_adjustment_possibility( - // transaction_fee_past_context, - // weighted_accounts_affordable_by_transaction_fee, - // cw_service_fee_balance_minor, - // ) { - // Ok(outcome) => outcome, - // Err(e) => { - // log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(&self.logger); - // return Err(e); - // } - // }; - // todo!("terrible"); - // match check_outcome { - // Either::Left(weighted_accounts) => { - // let accounts_not_needing_adjustment = convert_collection(weighted_accounts); - // Ok(Either::Right(accounts_not_needing_adjustment)) - // } - // - // Either::Right(weighted_accounts_needing_adjustment) => { - // diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); - // - // let adjustment_result_before_verification = self - // .propose_possible_adjustment_recursively(weighted_accounts_needing_adjustment); - // - // Ok(Either::Left(adjustment_result_before_verification)) - // } - // } + Ok(Either::Right(accounts_not_needing_adjustment)) + } } fn propose_possible_adjustment_recursively( @@ -531,26 +514,15 @@ impl Display for PaymentAdjusterError { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; - use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; - use crate::accountant::payment_adjuster::disqualification_arbiter::{ - DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, - }; - use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, - }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ - LowGainingAccountEliminated, SomeAccountsProcessed, - }; + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::SomeAccountsProcessed; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, TransactionFeeLimitation, TransactionFeePastCheckContext, - WeightedPayable, - }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - find_largest_exceeding_balance, weights_total, - }; - use crate::accountant::payment_adjuster::service_fee_adjuster::{ - AdjustmentComputer, ServiceFeeAdjusterReal, + AdjustmentIterationResult, TransactionFeeLimitation, WeightedPayable, }; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; + use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_analyzed_account_by_wallet, make_extreme_payables, make_initialized_subject, multiple_by_billion, CriterionCalculatorMock, DisqualificationGaugeMock, @@ -565,8 +537,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ make_analyzed_account, make_guaranteed_analyzed_payables, - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - make_payable_account, + make_guaranteed_qualified_payables, make_payable_account, }; use crate::accountant::{ gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, @@ -575,14 +546,10 @@ mod tests { use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use itertools::Either; - use lazy_static::lazy_static; - use libc::RESOLVE_NO_XDEV; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::utils::convert_collection; - use rand::rngs::mock; use std::collections::HashMap; - use std::iter::zip; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; @@ -738,9 +705,9 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Transaction fee amount 16,499,999,000,000,000 wei from your wallet \ - will not cover anticipated fees to send 3 transactions. Maximum is 2. The payments \ - count needs to be adjusted." + "WARN: {test_name}: Your transaction fee balance 16,499,999,000,000,000 wei is not \ + going to cover the anticipated fees to send 3 transactions. Maximum is set to 2. \ + Adjustment will be performed." )); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ @@ -780,7 +747,7 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,\ 000,001 wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of \ - the MASQ token. Adjustment in their count or the amounts is required.")); + MASQ token. Adjustment of their count or balances is required.")); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ delinquency bans. In order to consume services without limitations, you will need to \ @@ -1733,11 +1700,22 @@ mod tests { transaction_fee_appendix_opt: None, } ); - TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Passed successfully adjustment by transaction fee, however, that \ - was not enough regarding the other fee. Now, a critical shortage of MASQ balance has \ - been noticed. Operation will abort." - )); + TestLogHandler::new().assert_logs_contain_in_order(vec![ + &format!( + "WARN: {test_name}: Total of 411,000,000,000,000,000,000 wei in MASQ was \ + ordered while the consuming wallet held only 70,999,999,999,999,999,999 wei of MASQ \ + token. Adjustment of their count or balances is required." + ), + &format!( + "INFO: {test_name}: Please be aware that abandoning your debts is going to \ + result in delinquency bans. In order to consume services without limitations, you \ + will need to place more funds into your consuming wallet.", + ), + &format!( + "ERROR: {test_name}: {}", + LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY + ), + ]); } struct TestConfigForServiceFeeBalances { @@ -1927,7 +1905,7 @@ mod tests { let input_matrix: InputMatrixConfigurator = |(nominal_account_1, nominal_account_2, now)| { vec![ - // First stage: BalanceAndAgeCalculator + // First test case: BalanceAndAgeCalculator { let mut account_1 = nominal_account_1; account_1.bare_account.balance_wei += 123_456_789; @@ -1964,8 +1942,7 @@ mod tests { input_matrix_configurator: InputMatrixConfigurator, nominal_weight: u128, ) { - let defaulted_payment_adjuster = PaymentAdjusterReal::default(); - let calculators_count = defaulted_payment_adjuster.calculators.len(); + let calculators_count = PaymentAdjusterReal::default().calculators.len(); let now = SystemTime::now(); let cw_service_fee_balance_minor = gwei_to_wei::(1_000_000); let (template_accounts, template_computed_weight) = @@ -1982,14 +1959,12 @@ mod tests { assert_eq!( input_matrix.len(), calculators_count, - "If you've recently added in a new \ - calculator, you should add a single test case for it this test. See the input matrix, \ - it is the place where you should use the two accounts you can clone. Make sure you \ - modify only those parameters processed by your new calculator " + "If you've recently added in a new calculator, you should add in its new test case to \ + this test. See the input matrix, it is the place where you should use the two accounts \ + you can clone. Make sure you modify only those parameters processed by your new calculator " ); test_accounts_from_input_matrix( input_matrix, - defaulted_payment_adjuster, now, cw_service_fee_balance_minor, template_computed_weight, @@ -2072,9 +2047,8 @@ mod tests { let service_fee_adjuster_mock = ServiceFeeAdjusterMock::default() // We use this container to intercept those values we are after .perform_adjustment_by_service_fee_params(&perform_adjustment_by_service_fee_params_arc) - // This is just a sentinel for an actual result. - // We care only for the params - // TODO check this carefully...ugly + // This is just a sentinel that allows us to shorten the adjustment execution. + // We care only for the params captured inside the container from above .perform_adjustment_by_service_fee_result(AdjustmentIterationResult { decided_accounts: SomeAccountsProcessed(vec![]), remaining_undecided_accounts: vec![], @@ -2115,7 +2089,6 @@ mod tests { fn test_accounts_from_input_matrix( input_matrix: Vec<[(QualifiedPayableAccount, u128); 2]>, - defaulted_payment_adjuster: PaymentAdjusterReal, now: SystemTime, cw_service_fee_balance_minor: u128, template_computed_weight: TemplateComputedWeight, @@ -2135,27 +2108,24 @@ mod tests { .map(prepare_args_expected_weights_for_comparison) .collect::>() }) - .zip(defaulted_payment_adjuster.calculators.into_iter()) - .for_each( - |(qualified_payments_and_expected_computed_weights, calculator)| { - let (qualified_payments, expected_computed_weights): (Vec<_>, Vec<_>) = - qualified_payments_and_expected_computed_weights - .into_iter() - .unzip(); - - let weighted_accounts = exercise_production_code_to_get_weighted_accounts( - qualified_payments, - now, - cw_service_fee_balance_minor, - ); - - assert_results( - weighted_accounts, - expected_computed_weights, - template_computed_weight, - ) - }, - ); + .for_each(|qualified_payments_and_expected_computed_weights| { + let (qualified_payments, expected_computed_weights): (Vec<_>, Vec<_>) = + qualified_payments_and_expected_computed_weights + .into_iter() + .unzip(); + + let weighted_accounts = exercise_production_code_to_get_weighted_accounts( + qualified_payments, + now, + cw_service_fee_balance_minor, + ); + + assert_results( + weighted_accounts, + expected_computed_weights, + template_computed_weight, + ) + }); } fn make_comparison_hashmap( diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 09c5e1afe..10d36f1c3 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -5,6 +5,7 @@ pub mod accounts_abstraction; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ log_adjustment_by_service_fee_is_required, log_insufficient_transaction_fee_balance, + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentPossibilityErrorBuilder, TransactionCountsBy16bits, TransactionFeeLimitation, @@ -21,7 +22,7 @@ use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, Paymen use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; -use itertools::{Either, Product}; +use itertools::Either; use masq_lib::logger::Logger; pub struct PreparatoryAnalyzer {} @@ -67,22 +68,25 @@ impl PreparatoryAnalyzer { disqualification_arbiter, ); if is_service_fee_adjustment_needed { - let error_builder = AdjustmentPossibilityErrorBuilder::default() - .check_done_context(transaction_fee_limitation_opt); - // TODO rewrite me as ? - match Self::check_adjustment_possibility( + let error_builder = AdjustmentPossibilityErrorBuilder::default().context( + TransactionFeePastCheckContext::initial_check_done( + transaction_fee_limitation_opt, + ), + ); + + Self::check_adjustment_possibility( &prepared_accounts, cw_service_fee_balance_minor, error_builder, - ) { - Err(e) => todo!(), - _ => (), - } + )? }; let adjustment = match transaction_fee_limitation_opt { None => Adjustment::ByServiceFee, - Some(limit) => { - todo!() + Some(limitation) => { + let affordable_transaction_count = limitation.count_limit; + Adjustment::TransactionFeeInPriority { + affordable_transaction_count, + } } }; Ok(Either::Right(AdjustmentAnalysis::new( @@ -104,14 +108,15 @@ impl PreparatoryAnalyzer { cw_service_fee_balance_minor, logger, ) { - //TODO change to ? - match Self::check_adjustment_possibility( + if let Err(e) = Self::check_adjustment_possibility( weighted_accounts, cw_service_fee_balance_minor, error_builder, ) { - Ok(_) => Ok(true), - Err(e) => todo!(), + log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger); + Err(e) + } else { + Ok(true) } } else { Ok(false) @@ -234,12 +239,15 @@ impl PreparatoryAnalyzer { { let service_fee_totally_required_minor = Self::compute_total_of_service_fee_required(qualified_payables); - log_adjustment_by_service_fee_is_required( - logger, - service_fee_totally_required_minor, - cw_service_fee_balance_minor, - ); - service_fee_totally_required_minor > cw_service_fee_balance_minor + (service_fee_totally_required_minor > cw_service_fee_balance_minor) + .then(|| { + log_adjustment_by_service_fee_is_required( + logger, + service_fee_totally_required_minor, + cw_service_fee_balance_minor, + ) + }) + .is_some() } fn find_lowest_disqualification_limit(accounts: &[Account]) -> u128 @@ -337,7 +345,7 @@ mod tests { assert_eq!(&determine_limit_params[0..2], expected_params); TestLogHandler::new().exists_log_containing(&format!( "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet \ - held only {} wei of MASQ token. Adjustment of the count or amounts is required.", + held only {} wei of MASQ token. Adjustment of their count or balances is required.", total_amount_required.separate_with_commas(), cw_service_fee_balance.separate_with_commas() )); @@ -421,8 +429,9 @@ mod tests { cw_transaction_fee_balance_minor: 200_000_000, per_transaction_required_fee_minor: 300_000_000, }; - let error_builder = AdjustmentPossibilityErrorBuilder::default() - .check_done_context(Some(transaction_fee_limitation)); + let error_builder = AdjustmentPossibilityErrorBuilder::default().context( + TransactionFeePastCheckContext::initial_check_done(Some(transaction_fee_limitation)), + ); let expected_error_preparer = |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { @@ -441,7 +450,8 @@ mod tests { #[test] fn not_enough_for_even_the_least_demanding_account_error_right_after_negative_tx_fee_check() { - let error_builder = AdjustmentPossibilityErrorBuilder::default().check_done_context(None); + let error_builder = AdjustmentPossibilityErrorBuilder::default() + .context(TransactionFeePastCheckContext::initial_check_done(None)); let expected_error_preparer = |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { @@ -468,8 +478,8 @@ mod tests { ]; let initial_sum = sum_as(&accounts, |account| account.balance_minor()); let initial_count = accounts.len(); - let error_builder = - AdjustmentPossibilityErrorBuilder::default().accounts_dumped_context(&accounts); + let error_builder = AdjustmentPossibilityErrorBuilder::default() + .context(TransactionFeePastCheckContext::accounts_dumped(&accounts)); let expected_error_preparer = |_, cw_service_fee_balance_minor| { PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: initial_count, @@ -510,21 +520,25 @@ mod tests { let logger = Logger::new(test_name); let subject = PreparatoryAnalyzer::new(); - [(0, false),(1, false),(2, true)].iter().for_each(|(subtrahend_from_cw_balance, expected_result)| { - let service_fee_balance = cw_service_fee_balance_minor - subtrahend_from_cw_balance; - let result = subject.recheck_if_service_fee_adjustment_is_needed( - &accounts, - service_fee_balance, - error_builder.clone(), - &logger - ).unwrap(); - assert_eq!(result, *expected_result); - TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet \ - held only {}", - service_fee_totally_required_minor.separate_with_commas(), service_fee_balance.separate_with_commas() - )); - }) + [(0, false), (1, false), (2, true)].iter().for_each( + |(subtrahend_from_cw_balance, expected_result)| { + let service_fee_balance = cw_service_fee_balance_minor - subtrahend_from_cw_balance; + let result = subject + .recheck_if_service_fee_adjustment_is_needed( + &accounts, + service_fee_balance, + error_builder.clone(), + &logger, + ) + .unwrap(); + assert_eq!(result, *expected_result); + }, + ); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet held \ + only {}", service_fee_totally_required_minor.separate_with_commas(), + (cw_service_fee_balance_minor - 2).separate_with_commas() + )); } fn double_mock_results_queue(mock: DisqualificationGaugeMock) -> DisqualificationGaugeMock { diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 724cb05a5..36589d6b9 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ WeightedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: -ordinary_diagnostic_functions::{minimal_acceptable_balance_assigned_diagnostics, outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; +ordinary_diagnostic_functions::{outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; @@ -44,12 +44,15 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { .adjustment_computer .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - match Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments) { - Either::Left(without_gainers) => { - //TODO arbiter, what about it here? - Self::disqualify_single_account(disqualification_arbiter, without_gainers, logger) - } - Either::Right(with_gainers) => with_gainers, + let checked_accounts = Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments); + + match checked_accounts { + Either::Left(without_sufficient_gainers) => Self::disqualify_single_account( + disqualification_arbiter, + without_sufficient_gainers, + logger, + ), + Either::Right(with_sufficient_gainers) => with_sufficient_gainers, } } } @@ -153,26 +156,13 @@ impl ServiceFeeAdjusterReal { }, ); - // let outweighed_adjusted = if outweighed.is_empty() { - // vec![] - // } else { - // Self::assign_accounts_their_minimal_acceptable_balance( - // outweighed, - // disqualification_arbiter, - // ) - // }; - - let outweighed_adjusted = if sufficient_gainers.is_empty() { + let decided_accounts = if sufficient_gainers.is_empty() { vec![] } else { - // Self::assign_accounts_their_minimal_acceptable_balance( - // outweighed, - // disqualification_arbiter, - // ) convert_collection(sufficient_gainers) }; - //TODO Maybe consider to return the two return types just right from the fold - (outweighed_adjusted, low_gainers) + + (decided_accounts, low_gainers) } } From ebc5b2863dc2c987e54f257d331a5961a4188377 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Apr 2024 14:30:31 +0200 Subject: [PATCH 171/250] GH-711-c: error messages repaired and neatened --- node/src/accountant/payment_adjuster/mod.rs | 81 +++++++++------------ 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e7e60bb0a..48e6878ef 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -155,29 +155,6 @@ impl PaymentAdjusterReal { } } - //TODO delete me - // fn evaluate_checks(&self, results: PreparatoryAnalysisResults) -> AdjustmentAnalysisResult { - // match ( - // results.transaction_fee_limitation_opt, - // results.accounts_after_service_fee_check, - // ) { - // (None, Either::Left(payables_skipping_service_fee_analysis)) => { - // Ok(Either::Left(payables_skipping_service_fee_analysis)) - // } - // (None, Either::Right(analyzed_payables)) => Ok(Either::Right(AdjustmentAnalysis::new( - // Adjustment::ByServiceFee, - // analyzed_payables, - // ))), - // (Some(limitation), Either::Left(payables_skipping_service_fee_analysis)) => { - // Ok(Either::Right(self.work_skipped_accounts_into_analysis( - // limitation, - // payables_skipping_service_fee_analysis, - // ))) - // } - // (Some(limitation), Either::Right(analyzed_accounts)) => Ok(Either::Right(todo!())), - // } - // } - fn initialize_inner( &mut self, cw_service_fee_balance: u128, @@ -480,27 +457,39 @@ impl Display for PaymentAdjusterError { cw_transaction_fee_balance_minor, } => write!( f, - "Found a smaller transaction fee balance than it does for a single payment. \ - Number of canceled payments: {}. Transaction fee by single account: {} wei. \ - Consuming wallet balance: {} wei", + "Found transaction fee balance that is not enough for a single payment. Number of \ + canceled payments: {}. Transaction fee per payment: {} wei, while in wallet: {} wei", number_of_accounts, per_transaction_requirement_minor.separate_with_commas(), cw_transaction_fee_balance_minor.separate_with_commas() ), PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts, - total_service_fee_required_minor: total_amount_demanded_minor, + total_service_fee_required_minor, cw_service_fee_balance_minor, - transaction_fee_appendix_opt: appendix_opt, - } => write!( + transaction_fee_appendix_opt, + } => match transaction_fee_appendix_opt{ + None => write!( + f, + "Found service fee balance that is not enough for a single payment. Number of \ + canceled payments: {}. Total amount required: {} wei, while in wallet: {} wei", + number_of_accounts, + total_service_fee_required_minor.separate_with_commas(), + cw_service_fee_balance_minor.separate_with_commas()), + Some(limitation) => write!( f, - "Found a smaller service fee balance than it does for a single payment. \ - Number of canceled payments: {}. Total amount demanded: {} wei. Consuming \ - wallet balance: {} wei", - number_of_accounts.separate_with_commas(), - total_amount_demanded_minor.separate_with_commas(), + "Both transaction fee and service fee balances are not enough. Number of payments \ + considered: {}. Current transaction fee balance can cover {} payments only. Transaction \ + fee per payment: {} wei, while in wallet: {} wei. Neither does the service fee balance \ + allow a single payment. Total amount required: {} wei, while in wallet: {} wei", + number_of_accounts, + limitation.count_limit, + limitation.per_transaction_required_fee_minor.separate_with_commas(), + limitation.cw_transaction_fee_balance_minor.separate_with_commas(), + total_service_fee_required_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() - ), + ) + }, PaymentAdjusterError::AllAccountsEliminated => write!( f, "The adjustment algorithm had to eliminate each payable from the recently urged \ @@ -826,9 +815,9 @@ mod tests { cw_service_fee_balance_minor: 333_000_000, transaction_fee_appendix_opt: None, }, - "Found a smaller service fee balance than it does for a single payment. \ - Number of canceled payments: 5. Total amount required: 6,000,000,000 wei, - while in wallet: 333,000,000 wei", + "Found service fee balance that is not enough for a single payment. Number of \ + canceled payments: 5. Total amount required: 6,000,000,000 wei, while in wallet: \ + 333,000,000 wei", ), ( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { @@ -841,11 +830,11 @@ mod tests { per_transaction_required_fee_minor: 5_000_000_000, }), }, - "Both transaction fee and service fee balances are not high enough. Number of \ - payments considered: 5. Current transaction fee balance allows maximally 3 payments - for 5,000,000,000 wei required against the actual 3,000,000,000 wei. However, the - service fee balance does not allow even a single payment. Total amount required: - 7,000,000,000 wei, while in wallet: 100,000,000 wei", + "Both transaction fee and service fee balances are not enough. Number of payments \ + considered: 5. Current transaction fee balance can cover 3 payments only. \ + Transaction fee per payment: 5,000,000,000 wei, while in wallet: 3,000,000,000 \ + wei. Neither does the service fee balance allow a single payment. Total amount \ + required: 7,000,000,000 wei, while in wallet: 100,000,000 wei", ), ( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { @@ -853,9 +842,9 @@ mod tests { per_transaction_requirement_minor: 70_000_000_000_000, cw_transaction_fee_balance_minor: U256::from(90_000), }, - "Found a smaller transaction fee balance than it does for a single \ - payment. Number of canceled payments: 4. Transaction fee required \ - 140,000,000,000,000 wei, while in wallet: 90,000 wei", + "Found transaction fee balance that is not enough for a single payment. Number of \ + canceled payments: 4. Transaction fee per payment: 70,000,000,000,000 wei, while in \ + wallet: 90,000 wei", ), ( PaymentAdjusterError::AllAccountsEliminated, From 61dd71d9edee51a20e56cb252415342f7cbfbcf9 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Apr 2024 14:51:29 +0200 Subject: [PATCH 172/250] GH-711: sorting done better --- .../payment_adjuster/adjustment_runners.rs | 13 +- .../miscellaneous/helper_functions.rs | 52 +++++--- node/src/accountant/payment_adjuster/mod.rs | 115 ++++++------------ 3 files changed, 80 insertions(+), 100 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 2c3f8b992..bb185d856 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -24,7 +24,7 @@ pub trait AdjustmentRunner { fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts_in_descending_order: Vec, + weighted_accounts: Vec, ) -> Self::ReturnType; } @@ -39,21 +39,18 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { fn adjust_accounts( &self, payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts_in_descending_order: Vec, + weighted_accounts: Vec, ) -> Self::ReturnType { match payment_adjuster.inner.transaction_fee_count_limit_opt() { Some(limit) => { - return payment_adjuster.begin_with_adjustment_by_transaction_fee( - weighted_accounts_in_descending_order, - limit, - ) + return payment_adjuster + .begin_with_adjustment_by_transaction_fee(weighted_accounts, limit) } None => (), }; Ok(Either::Left( - payment_adjuster - .propose_possible_adjustment_recursively(weighted_accounts_in_descending_order), + payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts), )) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 297c3a0f6..46f626c8d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -33,25 +33,34 @@ pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { } pub fn dump_unaffordable_accounts_by_transaction_fee( - weighted_accounts_in_descending_order: Vec, + weighted_accounts: Vec, affordable_transaction_count: u16, ) -> Vec { + let sorted_accounts = sort_in_descendant_order_by_weights(weighted_accounts); + diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", "Keeping {} out of {} accounts. Dumping these accounts: {:?}", affordable_transaction_count, - weighted_accounts_in_descending_order.len(), - weighted_accounts_in_descending_order + sorted_accounts.len(), + sorted_accounts .iter() .skip(affordable_transaction_count as usize) ); - weighted_accounts_in_descending_order + sorted_accounts .into_iter() .take(affordable_transaction_count as usize) .collect() } +fn sort_in_descendant_order_by_weights(unsorted: Vec) -> Vec { + unsorted + .into_iter() + .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) + .collect() +} + pub fn compute_mul_coefficient_preventing_fractional_numbers( cw_service_fee_balance_minor: u128, ) -> u128 { @@ -186,15 +195,6 @@ impl ConsumingWalletExhaustingStatus { self } } - -pub fn sort_in_descendant_order_by_weights( - unsorted: impl Iterator, -) -> Vec { - unsorted - .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) - .collect() -} - #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; @@ -202,10 +202,12 @@ mod tests { AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_mul_coefficient_preventing_fractional_numbers, exhaust_cw_balance_entirely, + compute_mul_coefficient_preventing_fractional_numbers, + dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, find_largest_u128, find_smallest_u128, zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; + use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::{ make_analyzed_account, make_non_guaranteed_qualified_payable, make_payable_account, }; @@ -213,6 +215,7 @@ mod tests { use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; + use std::result; use std::time::SystemTime; #[test] @@ -289,6 +292,27 @@ mod tests { assert_eq!(result, 4_000_000_000 - 800_000_000) } + #[test] + fn dump_unaffordable_accounts_by_transaction_fee_works() { + let mut account_1 = make_weighed_account(123); + account_1.weight = 1_000_000_000; + let mut account_2 = make_weighed_account(456); + account_2.weight = 999_999_999; + let mut account_3 = make_weighed_account(789); + account_2.weight = 999_999_999; + let mut account_4 = make_weighed_account(1011); + account_4.weight = 1_000_000_001; + let affordable_transaction_count = 2; + + let result = dump_unaffordable_accounts_by_transaction_fee( + vec![account_1.clone(), account_2, account_3, account_4.clone()], + affordable_transaction_count, + ); + + let expected_result = vec![account_4, account_1]; + assert_eq!(result, expected_result) + } + #[test] fn compute_mul_coefficient_preventing_fractional_numbers_works() { let cw_service_fee_balance_minor = 12345678; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 48e6878ef..767b38b19 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -38,7 +38,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{Adjust use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, - sort_in_descendant_order_by_weights, sum_as, zero_affordable_accounts_found, + sum_as, zero_affordable_accounts_found, }; use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; use crate::accountant::payment_adjuster::service_fee_adjuster::{ @@ -183,9 +183,9 @@ impl PaymentAdjusterReal { &mut self, analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let weighted_accounts_sorted = self.calculate_weights(analyzed_accounts); + let weighted_accounts = self.calculate_weights(analyzed_accounts); let processed_accounts = self.propose_adjustments_recursively( - weighted_accounts_sorted, + weighted_accounts, TransactionAndServiceFeeAdjustmentRunner {}, )?; @@ -225,19 +225,19 @@ impl PaymentAdjusterReal { fn begin_with_adjustment_by_transaction_fee( &mut self, - weighted_accounts_in_descending_order: Vec, + weighted_accounts: Vec, already_known_affordable_transaction_count: u16, ) -> Result< Either, Vec>, PaymentAdjusterError, > { let error_builder = AdjustmentPossibilityErrorBuilder::default().context( - TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts_in_descending_order), + TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts), ); let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( - weighted_accounts_in_descending_order, + weighted_accounts, already_known_affordable_transaction_count, ); @@ -333,32 +333,31 @@ impl PaymentAdjusterReal { criteria_calculators: &[Box], qualified_accounts: Vec, ) -> Vec { - let weighted_accounts = qualified_accounts.into_iter().map(|payable| { - let weight = - criteria_calculators - .iter() - .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = criterion_calculator - .calculate(&payable.qualified_as, self.inner.as_ref()); - - let summed_up = weight + new_criterion; - - calculated_criterion_and_weight_diagnostics( - &payable.qualified_as.bare_account.wallet, - criterion_calculator.as_ref(), - new_criterion, - summed_up, - ); - - summed_up - }); - - WeightedPayable::new(payable, weight) - }); - - //TODO take this and move it before the tx fee cut-off; then go and tide up - //the awkward variables referring to the descending order for no good reason - sort_in_descendant_order_by_weights(weighted_accounts) + qualified_accounts + .into_iter() + .map(|payable| { + let weight = + criteria_calculators + .iter() + .fold(0_u128, |weight, criterion_calculator| { + let new_criterion = criterion_calculator + .calculate(&payable.qualified_as, self.inner.as_ref()); + + let summed_up = weight + new_criterion; + + calculated_criterion_and_weight_diagnostics( + &payable.qualified_as.bare_account.wallet, + criterion_calculator.as_ref(), + new_criterion, + summed_up, + ); + + summed_up + }); + + WeightedPayable::new(payable, weight) + }) + .collect() } fn adjust_remaining_unallocated_cw_balance_down( @@ -856,46 +855,6 @@ mod tests { .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) } - #[test] - fn apply_criteria_returns_accounts_sorted_by_criteria_in_descending_order() { - let calculator = CriterionCalculatorMock::default() - .calculate_result(1_000_000_002) - .calculate_result(1_000_000_001) - .calculate_result(1_000_000_003); - let subject = make_initialized_subject(None, None, Some(calculator), Some(12345678), None); - let make_account = |n: u64| { - let account = make_analyzed_account(n); - let wallet = account.qualified_as.bare_account.wallet.clone(); - (wallet, account) - }; - let (wallet_1, payable_1) = make_account(111); - let (wallet_2, payable_2) = make_account(222); - let (wallet_3, payable_3) = make_account(333); - - let criteria_and_accounts = - subject.calculate_weights(vec![payable_1, payable_2, payable_3]); - - let mut previous_weight = u128::MAX; - let accounts_alone = criteria_and_accounts - .into_iter() - .map(|weighted_account| { - assert!( - previous_weight > weighted_account.weight, - "Previous criteria {} wasn't larger than {} but should've been", - previous_weight, - weighted_account.weight - ); - previous_weight = weighted_account.weight; - weighted_account - .analyzed_account - .qualified_as - .bare_account - .wallet - }) - .collect::>(); - assert_eq!(accounts_alone, vec![wallet_3, wallet_1, wallet_2]) - } - #[test] fn tinier_but_larger_in_weight_account_is_prioritized_and_gains_up_to_its_disqualification_limit( ) { @@ -929,14 +888,14 @@ mod tests { .determine_limit_params(&determine_limit_params_arc); subject.disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); - let weighted_payables_in_descending_order = vec![ - WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), + let weighted_payables = vec![ WeightedPayable::new(account_1, multiple_by_billion(2_000_100)), + WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), ]; let mut result = subject .propose_adjustments_recursively( - weighted_payables_in_descending_order.clone(), + weighted_payables.clone(), TransactionAndServiceFeeAdjustmentRunner {}, ) .unwrap() @@ -948,7 +907,7 @@ mod tests { prove_that_proposed_adjusted_balance_would_exceed_the_original_value( subject, cw_service_fee_balance_minor, - weighted_payables_in_descending_order.clone(), + weighted_payables.clone(), wallet_2, balance_2, 2.3, @@ -958,7 +917,7 @@ mod tests { // Outweighed accounts always take the first places assert_eq!( &first_returned_account.original_account, - &weighted_payables_in_descending_order[0] + &weighted_payables[0] .analyzed_account .qualified_as .bare_account @@ -970,7 +929,7 @@ mod tests { let second_returned_account = result.remove(0); assert_eq!( &second_returned_account.original_account, - &weighted_payables_in_descending_order[1] + &weighted_payables[1] .analyzed_account .qualified_as .bare_account From 9c3bcff5d492b2cda2702edab4cca3ef01174646 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Apr 2024 16:22:50 +0200 Subject: [PATCH 173/250] GH-711: test fixed --- node/src/accountant/payment_adjuster/inner.rs | 13 +-- .../logging_and_diagnostics/diagnostics.rs | 11 ++- node/src/accountant/payment_adjuster/mod.rs | 87 +++++++++++++------ .../payment_adjuster/service_fee_adjuster.rs | 4 +- 4 files changed, 75 insertions(+), 40 deletions(-) diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index d74a6703c..82ff80c7e 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -63,6 +63,7 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { } } +#[derive(Default)] pub struct PaymentAdjusterInnerNull {} impl PaymentAdjusterInnerNull { @@ -145,7 +146,7 @@ mod tests { expected = "Broken code: Called the null implementation of the now() method in PaymentAdjusterInner" )] fn inner_null_calling_now() { - let subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull::default(); let _ = subject.now(); } @@ -156,7 +157,7 @@ mod tests { method in PaymentAdjusterInner" )] fn inner_null_calling_largest_exceeding_balance_recently_qualified() { - let subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull::default(); let _ = subject.largest_exceeding_balance_recently_qualified(); } @@ -166,7 +167,7 @@ mod tests { expected = "Broken code: Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" )] fn inner_null_calling_transaction_fee_count_limit_opt() { - let subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull::default(); let _ = subject.transaction_fee_count_limit_opt(); } @@ -176,7 +177,7 @@ mod tests { expected = "Broken code: Called the null implementation of the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_original_cw_service_fee_balance_minor() { - let subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull::default(); let _ = subject.original_cw_service_fee_balance_minor(); } @@ -186,7 +187,7 @@ mod tests { expected = "Broken code: Called the null implementation of the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_unallocated_cw_balance() { - let subject = PaymentAdjusterInnerNull {}; + let subject = PaymentAdjusterInnerNull::default(); let _ = subject.unallocated_cw_service_fee_balance_minor(); } @@ -196,7 +197,7 @@ mod tests { expected = "Broken code: Called the null implementation of the subtract_from_unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" )] fn inner_null_calling_subtract_from_unallocated_cw_service_fee_balance_minor() { - let mut subject = PaymentAdjusterInnerNull {}; + let mut subject = PaymentAdjusterInnerNull::default(); let _ = subject.subtract_from_unallocated_cw_service_fee_balance_minor(123); } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index c1640afc6..e48049ded 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -101,12 +101,15 @@ pub mod ordinary_diagnostic_functions { use crate::sub_lib::wallet::Wallet; use thousands::Separable; - pub fn outweighed_accounts_diagnostics(account_info: &UnconfirmedAdjustment) { + pub fn sufficient_gainer_found_diagnostics( + account_info: &UnconfirmedAdjustment, + disqualification_limit: u128, + ) { diagnostics!( &account_info.wallet(), - "OUTWEIGHED ACCOUNT FOUND", - "Original balance: {}, proposed balance: {}", - account_info.balance_minor().separate_with_commas(), + "SUFFICIENTLY GAINING ACCOUNT FOUND", + "Disqualification limit: {}, proposed balance: {}", + disqualification_limit.separate_with_commas(), account_info .proposed_adjusted_balance_minor .separate_with_commas() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 767b38b19..95b661048 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -127,6 +127,8 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.complete_debug_info_if_enabled(sketched_debug_info_opt, &affordable_accounts); + self.reset_inner(); + Ok(OutboundPaymentsInstructions::new( Either::Right(affordable_accounts), agent, @@ -150,7 +152,7 @@ impl PaymentAdjusterReal { disqualification_arbiter: DisqualificationArbiter::default(), service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], - inner: Box::new(PaymentAdjusterInnerNull {}), + inner: Box::new(PaymentAdjusterInnerNull::default()), logger: Logger::new("PaymentAdjuster"), } } @@ -179,6 +181,10 @@ impl PaymentAdjusterReal { self.inner = Box::new(inner); } + fn reset_inner(&mut self) { + self.inner = Box::new(PaymentAdjusterInnerNull::default()) + } + fn run_adjustment( &mut self, analyzed_accounts: Vec, @@ -538,6 +544,7 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::utils::convert_collection; use std::collections::HashMap; + use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use std::{usize, vec}; @@ -858,7 +865,7 @@ mod tests { #[test] fn tinier_but_larger_in_weight_account_is_prioritized_and_gains_up_to_its_disqualification_limit( ) { - let cw_service_fee_balance_minor = multiple_by_billion(3_600_000); + let cw_service_fee_balance_minor = multiple_by_billion(4_200_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); let mut account_1 = make_analyzed_account_by_wallet("abc"); let balance_1 = multiple_by_billion(3_000_000); @@ -867,8 +874,8 @@ mod tests { account_1.disqualification_limit_minor = disqualification_limit_1; let mut account_2 = make_analyzed_account_by_wallet("def"); let wallet_2 = account_2.qualified_as.bare_account.wallet.clone(); - let balance_2 = multiple_by_billion(1_000_000); - let disqualification_limit_2 = multiple_by_billion(800_000); + let balance_2 = multiple_by_billion(2_500_000); + let disqualification_limit_2 = multiple_by_billion(1_800_000); account_2.qualified_as.bare_account.balance_wei = balance_2; account_2.disqualification_limit_minor = disqualification_limit_2; let largest_exceeding_balance = (balance_1 @@ -903,21 +910,20 @@ mod tests { .unwrap(); // Let's have an example to explain why this test is important. - // First, the mock must be renewed; the available cw balance updated to the original value. - prove_that_proposed_adjusted_balance_would_exceed_the_original_value( + prove_that_proposed_adjusted_balance_could_have_exceeded_the_original_value( subject, cw_service_fee_balance_minor, weighted_payables.clone(), wallet_2, balance_2, - 2.3, + disqualification_limit_2, ); // So the assertion above showed the concern true. let first_returned_account = result.remove(0); // Outweighed accounts always take the first places assert_eq!( &first_returned_account.original_account, - &weighted_payables[0] + &weighted_payables[1] .analyzed_account .qualified_as .bare_account @@ -929,7 +935,7 @@ mod tests { let second_returned_account = result.remove(0); assert_eq!( &second_returned_account.original_account, - &weighted_payables[1] + &weighted_payables[0] .analyzed_account .qualified_as .bare_account @@ -941,13 +947,13 @@ mod tests { assert!(result.is_empty()); } - fn prove_that_proposed_adjusted_balance_would_exceed_the_original_value( + fn prove_that_proposed_adjusted_balance_could_have_exceeded_the_original_value( mut subject: PaymentAdjusterReal, cw_service_fee_balance_minor: u128, weighted_accounts: Vec, wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, - outweighed_by_multiple_of: f64, + disqualification_limit_of_outweighed_account: u128, ) { let garbage_largest_exceeding_balance_recently_qualified = 123456789; subject.inner = Box::new(PaymentAdjusterInnerReal::new( @@ -959,23 +965,27 @@ mod tests { let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); // The results are sorted from the biggest weights down - let proposed_adjusted_balance = unconfirmed_adjustments[0].proposed_adjusted_balance_minor; assert_eq!( - unconfirmed_adjustments[0].wallet(), + unconfirmed_adjustments[1].wallet(), &wallet_of_expected_outweighed ); // The weight of this account grew progressively due to the additional criterion added // in to the sum. Consequences would've been that redistribution of the adjusted balances // would've attributed this account with a larger amount to pay than it would've - // contained before the test started. To prevent that, we secure a rule that an account can - // never demand more than 100% of itself, ever + // contained before the test started. To prevent that, we used to secure a rule that + // an account could never demand more than 100% of itself. + // + // Later it was changed to other + // policy. so called "outweighed" account gains automatically a balance equal to its + // disqualification limit, also a prominent front position in the resulting set of + // the accounts to pay out. Additionally, due to its favorable position, it can be given + // a bit more from the remains still languishing in the consuming wallet. + let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( - proposed_adjusted_balance - > (outweighed_by_multiple_of * original_balance_of_outweighed_account as f64) - as u128, - "we expected the proposed balance clearly bigger than the original which is {} \ - but it was {}", - original_balance_of_outweighed_account.separate_with_commas(), + proposed_adjusted_balance > (disqualification_limit_of_outweighed_account * 11 / 10), + "we expected the proposed balance at least 1.1 times bigger than the original balance \ + which is {} but it was {}", + disqualification_limit_of_outweighed_account.separate_with_commas(), proposed_adjusted_balance.separate_with_commas() ); } @@ -1028,11 +1038,14 @@ mod tests { qualified_payables, &subject.logger, ); - // If concluded at the entry into the PaymentAdjuster that it has no point going off - // because away the least demanding account cannot be satisfied we would get an error here. - // However, it can only assess the balance (that early - in the real world) and accounts - // with the smallest balance is outplayed by the other one gaining some kind of extra - // significance + // If the initial analysis at the entry into the PaymentAdjuster concludes there is no point + // going off because even the least demanding account could not be satisfied, and we would + // get an error here. + // However, it can only assess the lowest disqualification limit of an account in that set. + // Probably not as usual, but this particular account can be later outplayed by another one + // that is equipped with some extra significance while its disqualification limit does not + // fit inder the consuming wallet balance anymore. A late error, possibly two different, is + // born. let adjustment_analysis = match analysis_result { Ok(Either::Right(analysis)) => analysis, x => panic!( @@ -1067,9 +1080,9 @@ mod tests { #[test] fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them() { - // Tests that a condition to short-circuit through is integrated for situations when + // Tests a condition to short-circuit through is integrated into for situations when // a disqualification frees means for other accounts and there is suddenly more to give - // than how much the remaining accounts demand + // than how much the remaining accounts require us to pay init_test_logging(); let test_name = "account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); @@ -1320,6 +1333,7 @@ mod tests { expected_adjusted_balance_1.separate_with_commas() ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + test_inner_was_reset_to_null(subject) } #[test] @@ -1387,6 +1401,7 @@ mod tests { |0x0000000000000000000000000000000000646566 300,000,000,000,000,000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + test_inner_was_reset_to_null(subject) } #[test] @@ -1453,6 +1468,7 @@ mod tests { assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + test_inner_was_reset_to_null(subject) } #[test] @@ -1522,6 +1538,7 @@ mod tests { The proposed adjustment 189,999,999,999,999,944 wei was below the disqualification \ limit 300,000,000,000,000,000 wei" )); + test_inner_was_reset_to_null(subject) } #[test] @@ -1593,6 +1610,7 @@ mod tests { |0x0000000000000000000000000000000000676869 250,000,000,000,000,000,000" ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); + test_inner_was_reset_to_null(subject) } #[test] @@ -1801,6 +1819,19 @@ mod tests { Box::new(blockchain_agent) } + fn test_inner_was_reset_to_null(subject: PaymentAdjusterReal) { + let err = catch_unwind(AssertUnwindSafe(|| { + subject.inner.original_cw_service_fee_balance_minor() + })) + .unwrap_err(); + let panic_msg = err.downcast_ref::().unwrap(); + assert_eq!( + panic_msg, + "Broken code: Broken code: Called the null implementation of \ + the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + ) + } + // The following tests together prove the use of correct calculators in the production code #[test] diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 36589d6b9..c7235e09e 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ WeightedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: -ordinary_diagnostic_functions::{outweighed_accounts_diagnostics, proposed_adjusted_balance_diagnostics}; +ordinary_diagnostic_functions::{sufficient_gainer_found_diagnostics, proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; @@ -145,7 +145,7 @@ impl ServiceFeeAdjusterReal { if current.proposed_adjusted_balance_minor >= disqualification_limit //TODO is the operator tested?? { - outweighed_accounts_diagnostics(¤t); + sufficient_gainer_found_diagnostics(¤t, disqualification_limit); let mut adjusted = current; adjusted.proposed_adjusted_balance_minor = disqualification_limit; sufficient_gainers.push(adjusted) From 6c1623927fc1dac7167f97b4b28c3c3a6c01f674 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Apr 2024 22:19:40 +0200 Subject: [PATCH 174/250] GH-711: improvings in the loading test --- .../logging_and_diagnostics/diagnostics.rs | 2 +- .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 34 +++++----- .../payment_adjuster/non_unit_tests/mod.rs | 66 +++++++++++++------ .../preparatory_analyser/mod.rs | 1 - 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index e48049ded..d6b79638e 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -3,7 +3,7 @@ use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = true; +const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 58; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 46f626c8d..f80afd30c 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -299,7 +299,7 @@ mod tests { let mut account_2 = make_weighed_account(456); account_2.weight = 999_999_999; let mut account_3 = make_weighed_account(789); - account_2.weight = 999_999_999; + account_3.weight = 999_999_999; let mut account_4 = make_weighed_account(1011); account_4.weight = 1_000_000_001; let affordable_transaction_count = 2; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 95b661048..207818769 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -916,7 +916,6 @@ mod tests { weighted_payables.clone(), wallet_2, balance_2, - disqualification_limit_2, ); // So the assertion above showed the concern true. let first_returned_account = result.remove(0); @@ -953,7 +952,6 @@ mod tests { weighted_accounts: Vec, wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, - disqualification_limit_of_outweighed_account: u128, ) { let garbage_largest_exceeding_balance_recently_qualified = 123456789; subject.inner = Box::new(PaymentAdjusterInnerReal::new( @@ -982,10 +980,10 @@ mod tests { // a bit more from the remains still languishing in the consuming wallet. let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( - proposed_adjusted_balance > (disqualification_limit_of_outweighed_account * 11 / 10), + proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), "we expected the proposed balance at least 1.1 times bigger than the original balance \ which is {} but it was {}", - disqualification_limit_of_outweighed_account.separate_with_commas(), + original_balance_of_outweighed_account.separate_with_commas(), proposed_adjusted_balance.separate_with_commas() ); } @@ -1359,11 +1357,10 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.calculators = vec![Box::new(calculator_mock)]; subject.logger = Logger::new(test_name); - let cw_service_fee_balance_minor = balance_1 + balance_2 + balance_3; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(10_u128.pow(22)); + .service_fee_balance_minor_result(u128::MAX); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysis::new( @@ -1430,6 +1427,7 @@ mod tests { .calculate_result(multiple_by_billion(200_000_000)) .calculate_result(multiple_by_billion(300_000_000)); let mut subject = PaymentAdjusterReal::new(); + subject.calculators = vec![Box::new(calculator_mock)]; let cw_service_fee_balance_minor = disqualification_limit_1 + disqualification_limit_3 + multiple_by_billion(10_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1479,15 +1477,12 @@ mod tests { // Account to be adjusted to keep as much as it is left in the cw balance let balance_1 = multiple_by_billion(333_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 200_000_000, 50_000_000); - let wallet_1 = account_1.bare_account.wallet.clone(); // Account to be outweighed and fully preserved let balance_2 = multiple_by_billion(111_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000, 10_000_000); - let wallet_2 = account_2.bare_account.wallet.clone(); // Account to be disqualified let balance_3 = multiple_by_billion(600_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); - let wallet_3 = account_3.bare_account.wallet.clone(); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() @@ -1541,6 +1536,11 @@ mod tests { test_inner_was_reset_to_null(subject) } + #[test] + fn reminder() { + todo!("change gainers for losing and thriving competitors") + } + #[test] fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { init_test_logging(); @@ -1549,14 +1549,14 @@ mod tests { let balance_1 = multiple_by_billion(100_000_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 60_000_000_000, 10_000_000_000); - // Thrown away as the first one due to shortage of transaction fee, - // as it is the least significant by criteria at the moment + // The second is thrown away first in a response to the shortage of transaction fee, + // as its weight is the least significant let balance_2 = multiple_by_billion(500_000_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 100_000_000_000, 30_000_000_000); - // Thrown away as the second one due to shortage of service fee, - // for the proposed adjusted balance insignificance (the third account withdraws - // most of the available balance from the consuming wallet for itself) + // Thrown away as the second one due to a shortage in the service fee, + // listed among accounts to disqualify and picked eventually for its + // lowest weight let balance_3 = multiple_by_billion(250_000_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 90_000_000_000, 20_000_000_000); @@ -1567,6 +1567,7 @@ mod tests { .calculate_result(multiple_by_billion(500_000_000_000)) .calculate_result(multiple_by_billion(750_000_000_000)); let mut subject = PaymentAdjusterReal::new(); + subject.calculators = vec![Box::new(calculator_mock)]; subject.logger = Logger::new(test_name); let service_fee_balance_in_minor = balance_1 - multiple_by_billion(10_000_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1784,8 +1785,7 @@ mod tests { ) -> Vec { payable_accounts .into_iter() - .enumerate() - .map(|(idx, payable)| { + .map(|payable| { let balance = payable.balance_wei; QualifiedPayableAccount { bare_account: payable, @@ -1882,7 +1882,7 @@ mod tests { // It's recommended to orientate the modifications rather positively (additions), because // there is a smaller chance you would run into some limit let input_matrix: InputMatrixConfigurator = - |(nominal_account_1, nominal_account_2, now)| { + |(nominal_account_1, nominal_account_2, _now)| { vec![ // First test case: BalanceAndAgeCalculator { diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index f4c48c1fe..b3be7dd5c 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -16,6 +16,7 @@ use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; +use masq_lib::utils::convert_collection; use rand; use rand::rngs::ThreadRng; use rand::{thread_rng, Rng}; @@ -37,8 +38,8 @@ fn loading_test_with_randomized_params() { // that it can live up to its original purpose and the vast majority of the attempted adjustments // end up with reasonable results. That said, a smaller amount of these attempts are expected // to be vain because of some chance always be there that with a given combination of payables - // the algorithm will go step by step eliminating completely all accounts. There's hardly - // a way for the adjustment procedure as it proceeds now to anticipate if this is going to happen. + // the algorithm will go step by step eliminating completely all accounts. There's hardly a way + // for the adjustment procedure as it proceeds now to anticipate if this is going to happen. let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); @@ -115,7 +116,7 @@ fn loading_test_with_randomized_params() { let payment_adjuster_result = subject.adjust_payments(prepared_adjustment, now); - prepare_single_scenario_result( + administrate_single_scenario_result( payment_adjuster_result, account_infos, required_adjustment, @@ -156,9 +157,9 @@ fn try_making_single_valid_scenario( if payables_len != qualified_payables.len() { return None; } - let analyzed_accounts: Vec = todo!(); + let analyzed_accounts: Vec = convert_collection(qualified_payables); let agent = make_agent(cw_service_fee_balance); - let adjustment = make_adjustment(gn, qualified_payables.len()); + let adjustment = make_adjustment(gn, analyzed_accounts.len()); Some(PreparedAdjustment::new( Box::new(agent), None, @@ -226,7 +227,7 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { } } -fn prepare_single_scenario_result( +fn administrate_single_scenario_result( payment_adjuster_result: Result, account_infos: Vec, required_adjustment: Adjustment, @@ -398,8 +399,12 @@ fn write_brief_test_summary_into_file( Requested:............................. {}\n\ Actually evaluated:.................... {}\n\n\ Successful:............................ {}\n\ - Successes with no accounts eliminated:. {}\n\ - Fulfillment distribution (service fee adjustment only):\n\ + Successes with no accounts eliminated:. {}\n\n\ + Transaction fee / mixed adjustments:... {}\n\ + Bills fulfillment distribution:\n\ + {}\n\n\ + Plain service fee adjustments:......... {}\n\ + Bills fulfillment distribution:\n\ {}\n\n\ Unsuccessful\n\ Caught by the entry check:............. {}\n\ @@ -410,7 +415,16 @@ fn write_brief_test_summary_into_file( overall_output_collector.oks, overall_output_collector.with_no_accounts_eliminated, overall_output_collector - .fulfillment_distribution + .fulfillment_distribution_for_transaction_fee_adjustments + .total_scenarios(), + overall_output_collector + .fulfillment_distribution_for_transaction_fee_adjustments + .render_in_two_lines(), + overall_output_collector + .fulfillment_distribution_for_service_fee_adjustments + .total_scenarios(), + overall_output_collector + .fulfillment_distribution_for_service_fee_adjustments .render_in_two_lines(), overall_output_collector.scenarios_denied_before_adjustment_started, overall_output_collector.all_accounts_eliminated, @@ -429,9 +443,18 @@ fn do_final_processing_of_single_scenario( if positive.were_no_accounts_eliminated { test_overall_output.with_no_accounts_eliminated += 1 } - if Adjustment::ByServiceFee == positive.common.required_adjustment { + if matches!( + positive.common.required_adjustment, + Adjustment::TransactionFeeInPriority { .. } + ) { + test_overall_output + .fulfillment_distribution_for_transaction_fee_adjustments + .collected_fulfillment_percentages + .push(positive.portion_of_cw_cumulatively_used_percents) + } + if positive.common.required_adjustment == Adjustment::ByServiceFee { test_overall_output - .fulfillment_distribution + .fulfillment_distribution_for_service_fee_adjustments .collected_fulfillment_percentages .push(positive.portion_of_cw_cumulatively_used_percents) } @@ -488,7 +511,7 @@ fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { file, account.initial_balance, account.debt_age_s, - account.bill_coverage_in_percentage_opt, + account.bills_coverage_in_percentage_opt, ) }) } @@ -582,7 +605,7 @@ fn prepare_interpretable_account_resolution( let adjusted_account_idx_opt = resulted_affordable_accounts .iter() .position(|account| account.wallet == account_info.wallet); - let bill_coverage_in_percentage_opt = match adjusted_account_idx_opt { + let bills_coverage_in_percentage_opt = match adjusted_account_idx_opt { Some(idx) => { let adjusted_account = resulted_affordable_accounts.remove(idx); let bill_coverage_in_percentage = u8::try_from( @@ -597,7 +620,7 @@ fn prepare_interpretable_account_resolution( InterpretableAdjustmentResult { initial_balance: account_info.initially_requested_service_fee_minor, debt_age_s: account_info.debt_age_s, - bill_coverage_in_percentage_opt, + bills_coverage_in_percentage_opt, } } @@ -609,12 +632,12 @@ fn sort_interpretable_adjustments( Vec, ) = interpretable_adjustments .into_iter() - .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); + .partition(|adjustment| adjustment.bills_coverage_in_percentage_opt.is_some()); let were_no_accounts_eliminated = eliminated.is_empty(); let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { Ord::cmp( - &result_b.bill_coverage_in_percentage_opt.unwrap(), - &result_a.bill_coverage_in_percentage_opt.unwrap(), + &result_b.bills_coverage_in_percentage_opt.unwrap(), + &result_a.bills_coverage_in_percentage_opt.unwrap(), ) }); let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { @@ -649,7 +672,8 @@ struct TestOverallOutputCollector { // ____________________________________ oks: usize, with_no_accounts_eliminated: usize, - fulfillment_distribution: PercentageFulfillmentDistribution, + fulfillment_distribution_for_transaction_fee_adjustments: PercentageFulfillmentDistribution, + fulfillment_distribution_for_service_fee_adjustments: PercentageFulfillmentDistribution, // Errors all_accounts_eliminated: usize, insufficient_service_fee_balance: usize, @@ -725,6 +749,10 @@ impl PercentageFulfillmentDistribution { ranges_populated.from_90_to_100 ) } + + fn total_scenarios(&self) -> usize { + self.collected_fulfillment_percentages.len() + } } struct CommonScenarioInfo { @@ -736,7 +764,7 @@ struct InterpretableAdjustmentResult { initial_balance: u128, debt_age_s: u64, // Account was eliminated from payment if None - bill_coverage_in_percentage_opt: Option, + bills_coverage_in_percentage_opt: Option, } struct AccountInfo { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 10d36f1c3..5c52254f2 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -409,7 +409,6 @@ mod tests { let cw_service_fee_balance = 1_000_000_100; let original_accounts = vec![account_1, account_2, account_3]; let service_fee_total_of_the_known_set = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; - let subject = PreparatoryAnalyzer {}; let result = PreparatoryAnalyzer::check_adjustment_possibility( &original_accounts, From f56e8a7185d1ef28de526c030fd9b7dfddac271b Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 Apr 2024 11:06:29 +0200 Subject: [PATCH 175/250] GH-711: removed bunch of warnings --- .../accountant/payment_adjuster/adjustment_runners.rs | 5 +---- .../balance_and_age_calculator.rs | 2 +- .../payment_adjuster/criterion_calculators/mod.rs | 2 +- .../logging_and_diagnostics/diagnostics.rs | 1 - .../miscellaneous/account_stages_conversions.rs | 2 +- .../payment_adjuster/miscellaneous/data_structures.rs | 3 +-- .../payment_adjuster/miscellaneous/helper_functions.rs | 10 ++++------ node/src/accountant/payment_adjuster/mod.rs | 5 +++-- .../accountant/payment_adjuster/non_unit_tests/mod.rs | 2 +- .../payment_adjuster/preparatory_analyser/mod.rs | 2 +- .../payment_adjuster/service_fee_adjuster.rs | 5 ++--- .../mid_scan_msg_handling/payable_scanner/mod.rs | 4 ++-- node/src/accountant/test_utils.rs | 2 +- 13 files changed, 19 insertions(+), 26 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index bb185d856..25c16c358 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -91,14 +91,13 @@ mod tests { AdjustedAccountBeforeFinalization, WeightedPayable, }; use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, multiple_by_billion, + make_initialized_subject, }; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_analyzed_account, make_non_guaranteed_qualified_payable, }; use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; - use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use std::time::SystemTime; @@ -144,8 +143,6 @@ mod tests { let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); - let initial_balance_minor_1 = payable_1.balance_minor(); - let initial_balance_minor_2 = payable_2.balance_minor(); let result = subject.adjust_accounts( &mut payment_adjuster, diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 10f155ffc..0cda18054 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -36,7 +36,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; use crate::accountant::test_utils::{ - make_analyzed_account, make_non_guaranteed_qualified_payable, + make_analyzed_account, }; use std::time::SystemTime; diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index b028fe810..3378b5fc6 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -3,7 +3,7 @@ pub mod balance_and_age_calculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::accountant::{QualifiedPayableAccount}; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index d6b79638e..a72da8e58 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -97,7 +97,6 @@ pub mod ordinary_diagnostic_functions { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; - use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use thousands::Separable; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 680873ff9..be1612d7d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -71,6 +71,7 @@ impl From for AdjustedAccountBeforeFinalization { #[cfg(test)] mod tests { + use crate::accountant::AnalyzedPayableAccount; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, @@ -78,7 +79,6 @@ mod tests { use crate::accountant::test_utils::{ make_non_guaranteed_qualified_payable, make_payable_account, }; - use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; #[test] fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index f70f34cf6..c5269c748 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -3,9 +3,8 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::PaymentAdjusterError; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::accountant::{AnalyzedPayableAccount}; use crate::sub_lib::wallet::Wallet; -use itertools::Either; use masq_lib::utils::ExpectValue; use web3::types::U256; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index f80afd30c..5be1366e3 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -5,8 +5,8 @@ use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable}; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeightedPayable}; +use crate::accountant::{AnalyzedPayableAccount}; use itertools::{Either, Itertools}; pub fn zero_affordable_accounts_found( @@ -199,7 +199,7 @@ impl ConsumingWalletExhaustingStatus { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, @@ -209,13 +209,11 @@ mod tests { }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::{ - make_analyzed_account, make_non_guaranteed_qualified_payable, make_payable_account, + make_analyzed_account, make_payable_account, }; - use crate::accountant::{CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; - use std::result; use std::time::SystemTime; #[test] diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 207818769..2f69c721a 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -23,7 +23,7 @@ use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalcula use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::{collection_diagnostics, diagnostics}; use crate::accountant::payment_adjuster::disqualification_arbiter::{ - DisqualificationArbiter, DisqualificationGauge, + DisqualificationArbiter, }; use crate::accountant::payment_adjuster::inner::{ PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, @@ -530,7 +530,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ - make_analyzed_account, make_guaranteed_analyzed_payables, + make_guaranteed_analyzed_payables, make_guaranteed_qualified_payables, make_payable_account, }; use crate::accountant::{ @@ -1243,6 +1243,7 @@ mod tests { #[test] fn count_of_qualified_accounts_before_equals_the_one_of_payments_after() { + todo!("add param assertions for the calculator mock"); // In other words, adjustment by service fee with no account eliminated init_test_logging(); let test_name = "count_of_qualified_accounts_before_equals_the_one_of_payments_after"; diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index b3be7dd5c..3d3191300 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -10,7 +10,7 @@ use crate::accountant::payment_adjuster::{ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::accountant::{AnalyzedPayableAccount}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 5c52254f2..518d865d3 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -15,7 +15,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_smallest_u128, sum_as, }; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ - BalanceProvidingAccount, DisqualificationAnalysableAccount, + BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index c7235e09e..1c70f103c 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -224,13 +224,12 @@ impl AdjustmentComputer { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, + AdjustedAccountBeforeFinalization, }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, DisqualificationGaugeMock, + make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, }; #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 829ef4990..1fcf91ead 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -6,11 +6,11 @@ pub mod blockchain_agent; pub mod msgs; pub mod test_utils; -use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis}; +use crate::accountant::payment_adjuster::{AdjustmentAnalysis}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::Scanner; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount, ResponseSkeleton}; +use crate::accountant::{ResponseSkeleton}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use actix::Message; use itertools::Either; diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 4ee4953e4..6e3d9a643 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -14,7 +14,7 @@ use crate::accountant::db_access_objects::receivable_dao::{ }; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, + AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ From d143e0f194210d7a476709b2afe93b1823777dd4 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 Apr 2024 22:47:04 +0200 Subject: [PATCH 176/250] GH-711: more little fixes --- .../payment_adjuster/adjustment_runners.rs | 4 +- .../balance_and_age_calculator.rs | 4 +- .../criterion_calculators/mod.rs | 2 +- .../disqualification_arbiter.rs | 32 ++++----- .../account_stages_conversions.rs | 2 +- .../miscellaneous/data_structures.rs | 2 +- .../miscellaneous/helper_functions.rs | 8 +-- node/src/accountant/payment_adjuster/mod.rs | 65 ++++++++++--------- .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../preparatory_analyser/mod.rs | 3 +- .../payment_adjuster/service_fee_adjuster.rs | 4 +- .../accountant/payment_adjuster/test_utils.rs | 11 +++- .../payable_scanner/mod.rs | 4 +- node/src/test_utils/mod.rs | 16 ++--- 14 files changed, 78 insertions(+), 81 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 25c16c358..078024611 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -90,9 +90,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, WeightedPayable, }; - use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, - }; + use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ make_analyzed_account, make_non_guaranteed_qualified_payable, diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 0cda18054..703bbddf3 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -35,9 +35,7 @@ mod tests { use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; - use crate::accountant::test_utils::{ - make_analyzed_account, - }; + use crate::accountant::test_utils::make_analyzed_account; use std::time::SystemTime; #[test] diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index 3378b5fc6..df7a4765e 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -3,7 +3,7 @@ pub mod balance_and_age_calculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; -use crate::accountant::{QualifiedPayableAccount}; +use crate::accountant::QualifiedPayableAccount; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index efd2807ad..3b7b053f7 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -50,29 +50,25 @@ impl DisqualificationArbiter { let disqualification_suspected_accounts = Self::list_accounts_nominated_for_disqualification(unconfirmed_adjustments); - if !disqualification_suspected_accounts.is_empty() { - let account_to_disqualify = - Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); + let account_to_disqualify = + Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); - let wallet = account_to_disqualify.wallet.clone(); + let wallet = account_to_disqualify.wallet.clone(); - try_finding_an_account_to_disqualify_diagnostics( - &disqualification_suspected_accounts, - &wallet, - ); + try_finding_an_account_to_disqualify_diagnostics( + &disqualification_suspected_accounts, + &wallet, + ); - debug!( - logger, - "Found accounts {:?} applying for disqualification", - disqualification_suspected_accounts, - ); + debug!( + logger, + "Found accounts {:?} applying for disqualification", + disqualification_suspected_accounts, + ); - info_log_for_disqualified_account(logger, account_to_disqualify); + info_log_for_disqualified_account(logger, account_to_disqualify); - wallet - } else { - todo!() - } + wallet } fn list_accounts_nominated_for_disqualification( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index be1612d7d..6143d50fd 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -71,7 +71,6 @@ impl From for AdjustedAccountBeforeFinalization { #[cfg(test)] mod tests { - use crate::accountant::AnalyzedPayableAccount; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, @@ -79,6 +78,7 @@ mod tests { use crate::accountant::test_utils::{ make_non_guaranteed_qualified_payable, make_payable_account, }; + use crate::accountant::AnalyzedPayableAccount; #[test] fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index c5269c748..d54a22623 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -3,7 +3,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::PaymentAdjusterError; -use crate::accountant::{AnalyzedPayableAccount}; +use crate::accountant::AnalyzedPayableAccount; use crate::sub_lib::wallet::Wallet; use masq_lib::utils::ExpectValue; use web3::types::U256; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 5be1366e3..8ff1be15a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -198,9 +198,7 @@ impl ConsumingWalletExhaustingStatus { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization - }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, @@ -208,9 +206,7 @@ mod tests { zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; - use crate::accountant::test_utils::{ - make_analyzed_account, make_payable_account, - }; + use crate::accountant::test_utils::{make_analyzed_account, make_payable_account}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 2f69c721a..bb1e428ca 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -530,8 +530,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ - make_guaranteed_analyzed_payables, - make_guaranteed_qualified_payables, make_payable_account, + make_guaranteed_analyzed_payables, make_guaranteed_qualified_payables, make_payable_account, }; use crate::accountant::{ gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, @@ -1078,9 +1077,10 @@ mod tests { #[test] fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them() { - // Tests a condition to short-circuit through is integrated into for situations when - // a disqualification frees means for other accounts and there is suddenly more to give - // than how much the remaining accounts require us to pay + // We test that a condition to short-circuit through is integrated in for a situation where + // a performed disqualification frees means that will become available for other accounts, + // and it happens that the remaining accounts require together less than what is left to + // give out. init_test_logging(); let test_name = "account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); @@ -1243,9 +1243,9 @@ mod tests { #[test] fn count_of_qualified_accounts_before_equals_the_one_of_payments_after() { - todo!("add param assertions for the calculator mock"); // In other words, adjustment by service fee with no account eliminated init_test_logging(); + let calculate_params_arc = Arc::new(Mutex::new(vec![])); let test_name = "count_of_qualified_accounts_before_equals_the_one_of_payments_after"; let now = SystemTime::now(); let balance_1 = multiple_by_billion(5_444_444_444); @@ -1265,6 +1265,7 @@ mod tests { let analyzed_payables = convert_collection(qualified_payables); let mut subject = PaymentAdjusterReal::new(); let calculator_mock = CriterionCalculatorMock::default() + .calculate_params(&calculate_params_arc) .calculate_result(multiple_by_billion(4_500_000_000)) .calculate_result(multiple_by_billion(4_200_000_000)) .calculate_result(multiple_by_billion(3_800_000_000)); @@ -1293,15 +1294,15 @@ mod tests { let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, - ..qualified_account_1.bare_account + ..qualified_account_1.bare_account.clone() }; let account_2_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_2, - ..qualified_account_2.bare_account + ..qualified_account_2.bare_account.clone() }; let account_3_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_3, - ..qualified_account_3.bare_account + ..qualified_account_3.bare_account.clone() }; vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] }; @@ -1311,6 +1312,15 @@ mod tests { ); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + let calculate_params = calculate_params_arc.lock().unwrap(); + assert_eq!( + *calculate_params, + vec![ + qualified_account_1, + qualified_account_2, + qualified_account_3 + ] + ); let log_msg = format!( "DEBUG: {test_name}: \n\ |Payable Account Balance Wei @@ -1907,15 +1917,9 @@ mod tests { common_weight: u128, } - struct SingleAccountInput { - account: QualifiedPayableAccount, - assertion_value: AssertionValue, - } - - //TODO implement this in - struct AssertionValue { - wallet_to_match_result_with: Wallet, - expected_computed_weight: u128, + struct ExpectedWeightWithWallet { + wallet: Wallet, + weight: u128, } fn test_calculators_reactivity( @@ -2074,10 +2078,14 @@ mod tests { template_computed_weight: TemplateComputedWeight, ) { fn prepare_args_expected_weights_for_comparison( - (qualified_payable, expected_computed_payable): (QualifiedPayableAccount, u128), - ) -> (QualifiedPayableAccount, (Wallet, u128)) { + (qualified_payable, expected_computed_weight): (QualifiedPayableAccount, u128), + ) -> (QualifiedPayableAccount, ExpectedWeightWithWallet) { let wallet = qualified_payable.bare_account.wallet.clone(); - (qualified_payable, (wallet, expected_computed_payable)) + let expected_weight = ExpectedWeightWithWallet { + wallet, + weight: expected_computed_weight, + }; + (qualified_payable, expected_weight) } input_matrix @@ -2119,18 +2127,17 @@ mod tests { fn assert_results( weighted_accounts: Vec, - expected_computed_weights: Vec<(Wallet, u128)>, + expected_computed_weights: Vec, template_computed_weight: TemplateComputedWeight, ) { let weighted_accounts_as_hash_map = make_comparison_hashmap(weighted_accounts); expected_computed_weights.into_iter().fold( 0, - |previous_account_actual_weight, (account_wallet, expected_computed_weight)| { + |previous_account_actual_weight, expected_account_weight| { + let wallet = expected_account_weight.wallet; let actual_account = weighted_accounts_as_hash_map - .get(&account_wallet) - .unwrap_or_else(|| { - panic!("Account for wallet {:?} disappeared", account_wallet) - }); + .get(&wallet) + .unwrap_or_else(|| panic!("Account for wallet {:?} disappeared", wallet)); assert_ne!( actual_account.weight, template_computed_weight.common_weight, "Weight is exactly the same as that one from the template. The inputs \ @@ -2139,10 +2146,10 @@ mod tests { ); assert_eq!( actual_account.weight, - expected_computed_weight, + expected_account_weight.weight, "Computed weight {} differs from what was expected {}", actual_account.weight.separate_with_commas(), - expected_computed_weight.separate_with_commas() + expected_account_weight.weight.separate_with_commas() ); assert_ne!( actual_account.weight, previous_account_actual_weight, diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 3d3191300..c4bd7c9cc 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -10,7 +10,7 @@ use crate::accountant::payment_adjuster::{ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; -use crate::accountant::{AnalyzedPayableAccount}; +use crate::accountant::AnalyzedPayableAccount; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 518d865d3..fd1defe08 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -15,8 +15,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_smallest_u128, sum_as, }; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ - BalanceProvidingAccount, - DisqualificationLimitProvidingAccount, + BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 1c70f103c..a0846dbf9 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -224,9 +224,7 @@ impl AdjustmentComputer { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, - }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 55e62e9d5..cec292475 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -107,16 +107,17 @@ pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustme #[derive(Default)] pub struct CriterionCalculatorMock { + calculate_params: Arc>>, calculate_results: RefCell>, } impl CriterionCalculator for CriterionCalculatorMock { fn calculate( &self, - _account: &QualifiedPayableAccount, + account: &QualifiedPayableAccount, _context: &dyn PaymentAdjusterInner, ) -> u128 { - // TODO consider using params assertions + self.calculate_params.lock().unwrap().push(account.clone()); self.calculate_results.borrow_mut().remove(0) } @@ -126,6 +127,10 @@ impl CriterionCalculator for CriterionCalculatorMock { } impl CriterionCalculatorMock { + pub fn calculate_params(mut self, params: &Arc>>) -> Self { + self.calculate_params = params.clone(); + self + } pub fn calculate_result(self, result: u128) -> Self { self.calculate_results.borrow_mut().push(result); self @@ -175,7 +180,7 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { fn perform_adjustment_by_service_fee( &self, weighted_accounts: Vec, - disqualification_arbiter: &DisqualificationArbiter, + _disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, _logger: &Logger, ) -> AdjustmentIterationResult { diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 1fcf91ead..506a94bee 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -6,11 +6,11 @@ pub mod blockchain_agent; pub mod msgs; pub mod test_utils; -use crate::accountant::payment_adjuster::{AdjustmentAnalysis}; +use crate::accountant::payment_adjuster::AdjustmentAnalysis; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::Scanner; -use crate::accountant::{ResponseSkeleton}; +use crate::accountant::ResponseSkeleton; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use actix::Message; use itertools::Either; diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index a7fe4ee0d..8d7e2bd24 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -524,14 +524,6 @@ pub struct TestRawTransaction { pub data: Vec, } -#[macro_export] -macro_rules! arbitrary_id_stamp_in_trait { - () => { - #[cfg(test)] - $crate::arbitrary_id_stamp_in_trait_internal___!(); - }; -} - #[cfg(test)] pub mod unshared_test_utils { use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; @@ -1019,6 +1011,14 @@ pub mod unshared_test_utils { } } + #[macro_export] + macro_rules! arbitrary_id_stamp_in_trait { + () => { + #[cfg(test)] + $crate::arbitrary_id_stamp_in_trait_internal___!(); + }; + } + // To be added together with other methods in your trait // DO NOT USE ME DIRECTLY, USE arbitrary_id_stamp_in_trait INSTEAD! #[macro_export] From 66dc1b03b331d8c1ac3683648865a2986d89d958 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 Apr 2024 23:58:50 +0200 Subject: [PATCH 177/250] GH-711: knocked off some more todos --- .../payment_adjuster/adjustment_runners.rs | 9 -- .../logging_and_diagnostics/diagnostics.rs | 4 +- node/src/accountant/payment_adjuster/mod.rs | 5 - .../payment_adjuster/service_fee_adjuster.rs | 107 ++++++++---------- 4 files changed, 52 insertions(+), 73 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 078024611..7307e6fac 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -9,15 +9,6 @@ use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterR use itertools::Either; use masq_lib::utils::convert_collection; -// TODO review this comment -// There are only two runners. They perform adjustment either by both the transaction and service -// fee, or exclusively by the transaction fee. The idea is that the adjustment by the transaction -// fee may ever appear in the initial iteration of the recursion. In any of the other iterations, -// if it proceeded, this feature would be staying around useless. Therefor the runner with more -// features is used only at the beginning. Its benefit is that it also allows to short-circuit -// through the computation of the account weights, because it can detect that dropped accounts due -// to the transaction fee scarcity lowered demand for the service fee and this adjustment is not -// needed. For the things just described, each runner gives back a different result type. pub trait AdjustmentRunner { type ReturnType; diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index a72da8e58..c1a6b272c 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -100,13 +100,13 @@ pub mod ordinary_diagnostic_functions { use crate::sub_lib::wallet::Wallet; use thousands::Separable; - pub fn sufficient_gainer_found_diagnostics( + pub fn thriving_competitor_found_diagnostics( account_info: &UnconfirmedAdjustment, disqualification_limit: u128, ) { diagnostics!( &account_info.wallet(), - "SUFFICIENTLY GAINING ACCOUNT FOUND", + "THRIVING COMPETITOR FOUND", "Disqualification limit: {}, proposed balance: {}", disqualification_limit.separate_with_commas(), account_info diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bb1e428ca..69694162d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1547,11 +1547,6 @@ mod tests { test_inner_was_reset_to_null(subject) } - #[test] - fn reminder() { - todo!("change gainers for losing and thriving competitors") - } - #[test] fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { init_test_logging(); diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index a0846dbf9..26ce42b19 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ WeightedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: -ordinary_diagnostic_functions::{sufficient_gainer_found_diagnostics, proposed_adjusted_balance_diagnostics}; +ordinary_diagnostic_functions::{proposed_adjusted_balance_diagnostics}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, weights_total, }; @@ -17,6 +17,7 @@ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; use std::vec; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::thriving_competitor_found_diagnostics; pub trait ServiceFeeAdjuster { fn perform_adjustment_by_service_fee( @@ -44,15 +45,15 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { .adjustment_computer .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - let checked_accounts = Self::handle_sufficiently_filled_accounts(unconfirmed_adjustments); + let checked_accounts = Self::handle_winning_accounts(unconfirmed_adjustments); match checked_accounts { - Either::Left(without_sufficient_gainers) => Self::disqualify_single_account( + Either::Left(no_thriving_competitors) => Self::disqualify_single_account( disqualification_arbiter, - without_sufficient_gainers, + no_thriving_competitors, logger, ), - Either::Right(with_sufficient_gainers) => with_sufficient_gainers, + Either::Right(thriving_competitors) => thriving_competitors, } } } @@ -70,39 +71,38 @@ impl ServiceFeeAdjusterReal { } } - //TODO review this text - // The term "outweighed account" comes from a phenomenon with account weight increasing - // significantly based on a different parameter than the debt size. Untreated, we would - // grant the account (much) more money than what the accountancy has actually recorded. + // The thin term "outweighed account" coma from a phenomenon with an account whose weight + // increases significantly based on a different parameter than the debt size. Untreated, we + // would wind up granting the account (much) more money than what it got recorded by + // the Accountant. // - // Outweighed accounts gain instantly the portion that tallies with the disqualification - // limit for this account. That's why we use the disqualification arbiter also in other - // places than only where we test an account on its minimal allowed size, considering - // elimination of such accounts. + // Each outweighed account, as well as any one with the proposed balance computed as a value + // between the disqualification limit of this account and the entire balance (originally + // requested), will gain instantly the same portion that equals the disqualification limit + // of this account. Anything below that is, in turn, considered unsatisfying, hence a reason + // for that account to go away simply disqualified. // - // The idea is that we want to spare as much as possible means that could be distributed - // among the rest of accounts. Given we declare that accounts having at least the size - // of the disqualification limit are fine, we don't exhaust the means right away on - // these. + // The idea is that we want to spare as much as possible in the held means that could be + // continuously distributed among the rest of accounts until it is possible to adjust an account + // and unmeet the condition for a disqualification. // - // On the other hand, if it turns out the spared money cannot be effectively used - // to adjust more accounts for the minimal necessary value, letting them fall away - // eventually, there is still the ending operation where the already prepared accounts - // are reconsidered to be give more bits from the fund of unallocated money, all down - // to zero. - fn handle_sufficiently_filled_accounts( + // On the other hand, if it begins being clear that the remaining money can keep no other + // account up in the selection there is yet another operation to come where the already + // selected accounts are reviewed again in the order of their significance and some of + // the unused money is poured into them, which goes on until zero. + fn handle_winning_accounts( unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let (sufficient_gainers, low_gainers) = - Self::filter_and_process_sufficient_gainers(unconfirmed_adjustments); + let (thriving_competitors, losing_competitors) = + Self::filter_and_process_winners(unconfirmed_adjustments); - if sufficient_gainers.is_empty() { - Either::Left(low_gainers) + if thriving_competitors.is_empty() { + Either::Left(losing_competitors) } else { let remaining_undecided_accounts: Vec = - convert_collection(low_gainers); + convert_collection(losing_competitors); let pre_processed_decided_accounts: Vec = - convert_collection(sufficient_gainers); + convert_collection(thriving_competitors); Either::Right(AdjustmentIterationResult { decided_accounts: SomeAccountsProcessed(pre_processed_decided_accounts), remaining_undecided_accounts, @@ -131,38 +131,36 @@ impl ServiceFeeAdjusterReal { } } - fn filter_and_process_sufficient_gainers( + fn filter_and_process_winners( unconfirmed_adjustments: Vec, ) -> ( Vec, Vec, ) { let init: (Vec, Vec) = (vec![], vec![]); - let (sufficient_gainers, low_gainers) = unconfirmed_adjustments.into_iter().fold( + let (thriving_competitors, losing_competitors) = unconfirmed_adjustments.into_iter().fold( init, - |(mut sufficient_gainers, mut low_gainers), current| { + |(mut thriving_competitors, mut losing_competitors), current| { let disqualification_limit = current.disqualification_limit_minor(); - if current.proposed_adjusted_balance_minor >= disqualification_limit - //TODO is the operator tested?? - { - sufficient_gainer_found_diagnostics(¤t, disqualification_limit); + if current.proposed_adjusted_balance_minor >= disqualification_limit { + thriving_competitor_found_diagnostics(¤t, disqualification_limit); let mut adjusted = current; adjusted.proposed_adjusted_balance_minor = disqualification_limit; - sufficient_gainers.push(adjusted) + thriving_competitors.push(adjusted) } else { - low_gainers.push(current) + losing_competitors.push(current) } - (sufficient_gainers, low_gainers) + (thriving_competitors, losing_competitors) }, ); - let decided_accounts = if sufficient_gainers.is_empty() { + let decided_accounts = if thriving_competitors.is_empty() { vec![] } else { - convert_collection(sufficient_gainers) + convert_collection(thriving_competitors) }; - (decided_accounts, low_gainers) + (decided_accounts, losing_competitors) } } @@ -231,8 +229,7 @@ mod tests { }; #[test] - fn filter_and_process_sufficient_gainers_limits_them_by_the_standard_disqualification_edge() { - let proposed_adjusted_balance_1 = multiple_by_billion(3_000_000_000); + fn filter_and_process_winners_limits_them_by_their_disqualification_edges() { let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); account_1 .weighted_account @@ -240,12 +237,11 @@ mod tests { .qualified_as .bare_account .balance_wei = multiple_by_billion(2_000_000_000); - account_1.proposed_adjusted_balance_minor = proposed_adjusted_balance_1; account_1 .weighted_account .analyzed_account .disqualification_limit_minor = multiple_by_billion(1_800_000_000); - let proposed_adjusted_balance_2 = multiple_by_billion(4_200_000_000); + account_1.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); account_2 .weighted_account @@ -253,12 +249,11 @@ mod tests { .qualified_as .bare_account .balance_wei = multiple_by_billion(5_000_000_000); - account_2.proposed_adjusted_balance_minor = proposed_adjusted_balance_2; account_2 .weighted_account .analyzed_account .disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; - let proposed_adjusted_balance_3 = multiple_by_billion(2_000_000_000); + account_2.proposed_adjusted_balance_minor = multiple_by_billion(4_200_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 .weighted_account @@ -266,12 +261,11 @@ mod tests { .qualified_as .bare_account .balance_wei = multiple_by_billion(3_000_000_000); - account_3.proposed_adjusted_balance_minor = proposed_adjusted_balance_3; account_3 .weighted_account .analyzed_account .disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; - let proposed_adjusted_balance_4 = multiple_by_billion(500_000_000); + account_3.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); account_4 .weighted_account @@ -279,12 +273,11 @@ mod tests { .qualified_as .bare_account .balance_wei = multiple_by_billion(1_500_000_000); - account_4.proposed_adjusted_balance_minor = proposed_adjusted_balance_4; account_4 .weighted_account .analyzed_account .disqualification_limit_minor = multiple_by_billion(500_000_000); - let proposed_adjusted_balance_5 = multiple_by_billion(1_000_000_000); + account_4.proposed_adjusted_balance_minor = multiple_by_billion(500_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 .weighted_account @@ -292,11 +285,11 @@ mod tests { .qualified_as .bare_account .balance_wei = multiple_by_billion(2_000_000_000); - account_5.proposed_adjusted_balance_minor = proposed_adjusted_balance_5; account_5 .weighted_account .analyzed_account .disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; + account_5.proposed_adjusted_balance_minor = multiple_by_billion(1_000_000_000); let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -305,10 +298,10 @@ mod tests { account_5.clone(), ]; - let (sufficient_gainers, low_gainers) = - ServiceFeeAdjusterReal::filter_and_process_sufficient_gainers(unconfirmed_accounts); + let (thriving_competitors, losing_competitors) = + ServiceFeeAdjusterReal::filter_and_process_winners(unconfirmed_accounts); - assert_eq!(low_gainers, vec![account_3, account_5]); + assert_eq!(losing_competitors, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ AdjustedAccountBeforeFinalization { original_account: account_1 @@ -335,6 +328,6 @@ mod tests { proposed_adjusted_balance_minor: multiple_by_billion(500_000_000), }, ]; - assert_eq!(sufficient_gainers, expected_adjusted_outweighed_accounts) + assert_eq!(thriving_competitors, expected_adjusted_outweighed_accounts) } } From 60372976e2c4addea348fa9812b49da29e64a9a8 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Apr 2024 00:02:10 +0200 Subject: [PATCH 178/250] GH-711: detail --- node/src/accountant/payment_adjuster/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 69694162d..bbeb9d3f6 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -93,12 +93,11 @@ impl PaymentAdjuster for PaymentAdjusterReal { qualified_payables: Vec, agent: &dyn BlockchainAgent, ) -> AdjustmentAnalysisResult { - self.analyzer.analyze_accounts( - agent, - &self.disqualification_arbiter, - qualified_payables, - &self.logger, - ) + let disqualification_arbiter = &self.disqualification_arbiter; + let logger = &self.logger; + + self.analyzer + .analyze_accounts(agent, disqualification_arbiter, qualified_payables, logger) } fn adjust_payments( From 4440236b408b317ddbfda9d689d8eb38cec12d8d Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Apr 2024 21:56:26 +0200 Subject: [PATCH 179/250] GH-711: tx fee margin implemented at least inside PA; also decided to invent a new data type for easier work --- automap/Cargo.lock | 62 ++++- dns_utility/Cargo.lock | 76 ++++++ masq_lib/Cargo.toml | 1 + masq_lib/src/lib.rs | 1 + masq_lib/src/percentage.rs | 243 ++++++++++++++++++ node/Cargo.lock | 1 + node/src/accountant/payment_adjuster/mod.rs | 14 +- .../preparatory_analyser/mod.rs | 28 +- .../payable_scanner/agent_null.rs | 5 + .../payable_scanner/agent_web3.rs | 5 + .../payable_scanner/blockchain_agent.rs | 2 + .../payable_scanner/test_utils.rs | 11 + node/src/accountant/scanners/mod.rs | 8 +- .../blockchain_interface_web3/mod.rs | 7 + 14 files changed, 449 insertions(+), 15 deletions(-) create mode 100644 masq_lib/src/percentage.rs diff --git a/automap/Cargo.lock b/automap/Cargo.lock index dfd2a58cb..e1c10537b 100644 --- a/automap/Cargo.lock +++ b/automap/Cargo.lock @@ -1063,6 +1063,7 @@ dependencies = [ "lazy_static", "log 0.4.17", "nix", + "num", "regex", "serde", "serde_derive", @@ -1205,6 +1206,40 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1215,11 +1250,34 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg 1.1.0", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg 1.1.0", ] diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock index 04c8bf983..371b834e0 100644 --- a/dns_utility/Cargo.lock +++ b/dns_utility/Cargo.lock @@ -866,6 +866,7 @@ dependencies = [ "lazy_static", "log 0.4.17", "nix", + "num", "regex", "serde", "serde_derive", @@ -1008,6 +1009,81 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg 1.1.0", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "num_cpus" version = "1.15.0" diff --git a/masq_lib/Cargo.toml b/masq_lib/Cargo.toml index 60fa87c79..679353bf0 100644 --- a/masq_lib/Cargo.toml +++ b/masq_lib/Cargo.toml @@ -17,6 +17,7 @@ ethereum-types = "0.9.0" itertools = "0.10.1" lazy_static = "1.4.0" log = "0.4.8" +num = "=0.4.0" regex = "1.5.4" serde = "1.0.133" serde_derive = "1.0.133" diff --git a/masq_lib/src/lib.rs b/masq_lib/src/lib.rs index e5232b221..b3a8b2902 100644 --- a/masq_lib/src/lib.rs +++ b/masq_lib/src/lib.rs @@ -20,6 +20,7 @@ pub mod command; pub mod constants; pub mod crash_point; pub mod data_version; +pub mod percentage; pub mod shared_schema; pub mod test_utils; pub mod type_obfuscation; diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs new file mode 100644 index 000000000..2aa198b13 --- /dev/null +++ b/masq_lib/src/percentage.rs @@ -0,0 +1,243 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use num::integer::mod_floor; +use num::CheckedAdd; +use num::CheckedSub; +use num::{CheckedDiv, CheckedMul, Integer}; +use std::any::type_name; +use std::fmt::Debug; +use std::ops::Mul; + +// It's designed for a storage of values from 0 to 100, after which it can be used to compute +// the corresponding portion of many integer types. It should also take care of the least significant +// digit in order to diminish the effect of a precision loss genuinly implied by this kind of math +// operations done on integers. + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Percentage { + per_cent: u8, +} + +impl Percentage { + pub fn new(num: u8) -> Self { + match num { + 0..=100 => Self { per_cent: num }, + x => panic!("Accepts only range from 0 to 100 but {} was supplied", x), + } + } + + pub fn of(&self, num: N) -> N + where + N: From + CheckedMul + CheckedAdd + CheckedDiv + PartialOrd + Integer + Debug + Copy, + { + let zero = N::from(0); + if num == zero || N::from(self.per_cent) == zero { + return zero; + } + + let a = N::from(self.per_cent) + .checked_mul(&num) + .unwrap_or_else(|| panic!( + "Overflow when using 'Percentage' for num {:?} of type {}, trying to compute {} per \ + cent of it.", num, type_name::(),self.per_cent + )); + if a < N::from(10) { + return N::from(0); + } + + let rounding = if Percentage::should_be_rounded_down(a) { + N::from(0) + } else { + N::from(1) + }; + + let hundred = N::from(100); + + if a < hundred { + rounding + } else { + a.checked_div(&hundred) + .expect("div failed") + .checked_add(&rounding) + .expect("rounding failed") + } + } + + pub fn add_percent_to(&self, num: N) -> N + where + N: From + CheckedMul + CheckedAdd + CheckedDiv + PartialOrd + Integer + Debug + Copy, + { + self.of(num).checked_add(&num).unwrap_or_else(|| { + panic!( + "Overflowed during addition of {} per cent, that is {:?}, to {:?} of type {}.", + self.per_cent, + self.of(num), + num, + type_name::() + ) + }) + } + + pub fn subtract_percent_from(&self, num: N) -> N + where + N: From + + CheckedMul + + CheckedAdd + + CheckedSub + + CheckedDiv + + PartialOrd + + Integer + + Debug + + Copy, + { + num.checked_sub(&self.of(num)) + .expect("should never happen by its principle") + } + + fn should_be_rounded_down(num: N) -> bool + where + N: From + PartialEq + PartialOrd + Mul + CheckedMul + Integer + Copy, + { + let ten = N::from(10); + let upper_limit = ten * ten; + let enough_limit = ten; + if num == upper_limit { + true + } else if num >= enough_limit { + let modulo = mod_floor(num, upper_limit); + modulo + < N::from(5) + .checked_mul(&ten) + .expect("Couldn't create limit to compare with") + } else { + unreachable!("Check to prevent numbers with fewer than two digits failed") + } + } +} + +#[cfg(test)] +mod tests { + use crate::percentage::Percentage; + use std::panic::catch_unwind; + + #[test] + fn zero() { + assert_eq!(Percentage::new(45).of(0), 0); + assert_eq!(Percentage::new(0).of(33), 0) + } + + #[test] + fn end_to_end_test() { + let range = 0..=100; + + let round_returned_range = range + .clone() + .into_iter() + .map(|per_cent| Percentage::new(per_cent).of(100_u64)) + .collect::>(); + + let expected = range + .into_iter() + .map(|num| num as u64) + .collect::>(); + assert_eq!(round_returned_range, expected) + } + + #[test] + fn only_numbers_up_to_100_are_accepted() { + (101..=u8::MAX) + .map(|num| { + ( + catch_unwind(|| Percentage::new(num)).expect_err("expected panic"), + num, + ) + }) + .map(|(panic, num)| { + ( + panic + .downcast_ref::() + .expect("couldn't downcast to String") + .to_owned(), + num, + ) + }) + .for_each(|(panic_msg, num)| { + assert_eq!( + panic_msg, + format!("Accepts only range from 0 to 100 but {} was supplied", num) + ) + }); + } + + #[test] + fn too_low_values() { + vec![((10, 1), 0), ((9, 1), 0), ((5, 14), 1), ((55, 40), 22)] + .into_iter() + .for_each(|((per_cent, examined_number), expected_result)| { + let result = Percentage::new(per_cent).of(examined_number); + assert_eq!( + result, expected_result, + "For {} per cent and number {} the expected result was {} but we got {}", + per_cent, examined_number, expected_result, result + ) + }) + } + + #[test] + fn should_be_rounded_down_works_for_last_but_one_digit() { + [ + (787879, false), + (1114545, true), + (100, true), + (49, true), + (50, false), + ] + .into_iter() + .for_each(|(num, expected_result)| { + assert_eq!(Percentage::should_be_rounded_down(num), expected_result) + }) + } + + #[test] + fn add_percent_to_works() { + let percentage = Percentage::new(13); + + let result = percentage.add_percent_to(100); + + assert_eq!(result, 113) + } + + #[test] + #[should_panic(expected = "Overflowed during addition of 1 per cent, that is \ + 184467440737095516, to 18446744073709551615 of type u64.")] + fn add_percent_to_hits_overflow() { + let _ = Percentage::new(1).add_percent_to(u64::MAX); + } + + #[test] + fn subtract_percent_from_works() { + let percentage = Percentage::new(55); + + let result = percentage.subtract_percent_from(100); + + assert_eq!(result, 45) + } + + #[test] + #[should_panic( + expected = "Overflow when using 'Percentage' for num 18446744073709551615 of type u64, \ + trying to compute 2 per cent of it" + )] + fn hits_upper_overflow() { + let _: u64 = Percentage::new(2).of(u64::MAX); + } + + #[test] + #[should_panic( + expected = "internal error: entered unreachable code: Check to prevent numbers with fewer \ + than two digits failed" + )] + fn broken_code_for_violation_of_already_checked_range() { + let _ = Percentage::should_be_rounded_down(2); + } +} diff --git a/node/Cargo.lock b/node/Cargo.lock index 67aed1891..835a5d3dc 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -1834,6 +1834,7 @@ dependencies = [ "lazy_static", "log 0.4.18", "nix 0.23.1", + "num", "regex", "serde", "serde_derive", diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index bbeb9d3f6..0f604f9f2 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -534,6 +534,7 @@ mod tests { use crate::accountant::{ gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, }; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; @@ -636,7 +637,11 @@ mod tests { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_per_transaction: 53_000, - cw_transaction_fee_balance_major: (100 * 6 * 53_000) + 1, + cw_transaction_fee_balance_major: { + let base_value = 100 * 6 * 53_000; + let exact_equality = TRANSACTION_FEE_MARGIN.add_percent_to(base_value); + exact_equality + 1 + }, }), ); // transaction fee balance == payments @@ -646,7 +651,10 @@ mod tests { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_per_transaction: 53_000, - cw_transaction_fee_balance_major: 100 * 6 * 53_000, + cw_transaction_fee_balance_major: { + let base_value = 100 * 6 * 53_000; + TRANSACTION_FEE_MARGIN.add_percent_to(base_value) + }, }), ); @@ -1025,6 +1033,7 @@ mod tests { let cw_service_fee_balance_minor = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; let agent_for_analysis = BlockchainAgentMock::default() + .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(12356); @@ -1815,6 +1824,7 @@ mod tests { ); let blockchain_agent = BlockchainAgentMock::default() + .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) .transaction_fee_balance_minor_result(cw_transaction_fee_minor) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .estimated_transaction_fee_per_transaction_minor_result( diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index fd1defe08..0d5fa1635 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -23,6 +23,7 @@ use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; use itertools::Either; use masq_lib::logger::Logger; +use masq_lib::percentage::Percentage; pub struct PreparatoryAnalyzer {} @@ -43,10 +44,12 @@ impl PreparatoryAnalyzer { let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction_minor(); + let agreed_transaction_fee_margin = agent.agreed_transaction_fee_margin(); let transaction_fee_limitation_opt = self .determine_transaction_count_limit_by_transaction_fee( cw_transaction_fee_balance_minor, + agreed_transaction_fee_margin, per_transaction_requirement_minor, number_of_counts, logger, @@ -125,6 +128,7 @@ impl PreparatoryAnalyzer { fn determine_transaction_count_limit_by_transaction_fee( &self, cw_transaction_fee_balance_minor: U256, + agreed_transaction_fee_margin: Percentage, per_transaction_requirement_minor: u128, number_of_qualified_accounts: usize, logger: &Logger, @@ -132,13 +136,14 @@ impl PreparatoryAnalyzer { let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, per_transaction_requirement_minor, + agreed_transaction_fee_margin, number_of_qualified_accounts, ); - let max_tx_count_we_can_afford_u16 = verified_tx_counts.affordable; - let required_tx_count_u16 = verified_tx_counts.required; + let max_tx_count_we_can_afford: u16 = verified_tx_counts.affordable; + let required_tx_count_with_margin: u16 = verified_tx_counts.required; - if max_tx_count_we_can_afford_u16 == 0 { + if max_tx_count_we_can_afford == 0 { Err( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: number_of_qualified_accounts, @@ -146,17 +151,17 @@ impl PreparatoryAnalyzer { cw_transaction_fee_balance_minor, }, ) - } else if max_tx_count_we_can_afford_u16 >= required_tx_count_u16 { + } else if max_tx_count_we_can_afford >= required_tx_count_with_margin { Ok(None) } else { log_insufficient_transaction_fee_balance( logger, - required_tx_count_u16, + required_tx_count_with_margin, cw_transaction_fee_balance_minor, - max_tx_count_we_can_afford_u16, + max_tx_count_we_can_afford, ); let transaction_fee_limitation_opt = TransactionFeeLimitation::new( - max_tx_count_we_can_afford_u16, + max_tx_count_we_can_afford, cw_transaction_fee_balance_minor.as_u128(), per_transaction_requirement_minor, ); @@ -167,10 +172,13 @@ impl PreparatoryAnalyzer { fn transaction_counts_verification( cw_transaction_fee_balance_minor: U256, txn_fee_required_per_txn_minor: u128, + txn_fee_margin: Percentage, number_of_qualified_accounts: usize, ) -> TransactionCountsBy16bits { - let max_possible_tx_count_u256 = - cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); + let txn_fee_required_per_txn_minor_with_margin = + txn_fee_margin.add_percent_to(txn_fee_required_per_txn_minor); + let max_possible_tx_count_u256 = cw_transaction_fee_balance_minor + / U256::from(txn_fee_required_per_txn_minor_with_margin); TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) } @@ -283,6 +291,7 @@ mod tests { make_analyzed_account, make_non_guaranteed_qualified_payable, }; use crate::accountant::QualifiedPayableAccount; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use itertools::Either; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; @@ -307,6 +316,7 @@ mod tests { DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; let blockchain_agent = BlockchainAgentMock::default() + .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(123456) .service_fee_balance_minor_result(cw_service_fee_balance); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 2d55a78dd..439409aec 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -4,6 +4,7 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockch use crate::sub_lib::wallet::Wallet; use ethereum_types::U256; use masq_lib::logger::Logger; +use masq_lib::percentage::Percentage; #[derive(Clone)] pub struct BlockchainAgentNull { @@ -32,6 +33,10 @@ impl BlockchainAgent for BlockchainAgentNull { 0 } + fn agreed_transaction_fee_margin(&self) -> Percentage { + todo!() + } + fn consuming_wallet(&self) -> &Wallet { self.log_function_call("consuming_wallet()"); &self.wallet diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 8f1b890bf..109d392a0 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -6,6 +6,7 @@ use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::accountant::gwei_to_wei; +use masq_lib::percentage::Percentage; use web3::types::U256; #[derive(Debug, Clone)] @@ -39,6 +40,10 @@ impl BlockchainAgent for BlockchainAgentWeb3 { self.gas_price_gwei } + fn agreed_transaction_fee_margin(&self) -> Percentage { + todo!() + } + fn consuming_wallet(&self) -> &Wallet { &self.consuming_wallet } diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index fa0805da9..cceba2963 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -2,6 +2,7 @@ use crate::arbitrary_id_stamp_in_trait; use crate::sub_lib::wallet::Wallet; +use masq_lib::percentage::Percentage; use web3::types::U256; // Table of chains by @@ -25,6 +26,7 @@ pub trait BlockchainAgent: Send { fn transaction_fee_balance_minor(&self) -> U256; fn service_fee_balance_minor(&self) -> u128; fn agreed_fee_per_computation_unit(&self) -> u64; + fn agreed_transaction_fee_margin(&self) -> Percentage; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index 3c19845f1..6bb667c0f 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -7,6 +7,7 @@ use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; use ethereum_types::U256; +use masq_lib::percentage::Percentage; use std::cell::RefCell; #[derive(Default)] @@ -15,6 +16,7 @@ pub struct BlockchainAgentMock { transaction_fee_balance_minor_results: RefCell>, service_fee_balance_minor_results: RefCell>, agreed_fee_per_computation_unit_results: RefCell>, + agreed_transaction_fee_margin: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, arbitrary_id_stamp_opt: Option, @@ -45,6 +47,10 @@ impl BlockchainAgent for BlockchainAgentMock { .remove(0) } + fn agreed_transaction_fee_margin(&self) -> Percentage { + self.agreed_transaction_fee_margin.borrow_mut().remove(0) + } + fn consuming_wallet(&self) -> &Wallet { self.consuming_wallet_result_opt.as_ref().unwrap() } @@ -89,6 +95,11 @@ impl BlockchainAgentMock { self } + pub fn agreed_transaction_fee_margin_result(self, result: Percentage) -> Self { + self.agreed_transaction_fee_margin.borrow_mut().push(result); + self + } + pub fn consuming_wallet_result(mut self, consuming_wallet_result: Wallet) -> Self { self.consuming_wallet_result_opt = Some(consuming_wallet_result); self diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index ddf438f00..04b7cd23e 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -796,7 +796,11 @@ impl PendingPayableScanner { records due to {:?}", serialize_hashes(&fingerprints), e ) } else { - self.add_to_the_total_of_paid_payable(&fingerprints, serialize_hashes, logger); + self.add_percent_to_the_total_of_paid_payable( + &fingerprints, + serialize_hashes, + logger, + ); let rowids = fingerprints .iter() .map(|fingerprint| fingerprint.rowid) @@ -815,7 +819,7 @@ impl PendingPayableScanner { } } - fn add_to_the_total_of_paid_payable( + fn add_percent_to_the_total_of_paid_payable( &mut self, fingerprints: &[PendingPayableFingerprint], serialize_hashes: fn(&[PendingPayableFingerprint]) -> String, 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 79e4bef85..d4a1be324 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -28,6 +28,7 @@ use serde_json::Value; use std::fmt::Debug; use std::iter::once; use std::rc::Rc; +use lazy_static::lazy_static; use thousands::Separable; use web3::contract::Contract; use web3::transports::{Batch, EventLoopHandle}; @@ -36,6 +37,7 @@ use web3::types::{ H160, H256, U256, }; use web3::{BatchTransport, Error, Web3}; +use masq_lib::percentage::Percentage; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; use crate::blockchain::blockchain_interface::data_structures::{BlockchainTransaction, ProcessedPayableFallible, RpcPayablesFailure}; use crate::sub_lib::blockchain_interface_web3::transaction_data_web3; @@ -67,6 +69,11 @@ const TRANSACTION_LITERAL: H256 = H256([ pub const REQUESTS_IN_PARALLEL: usize = 1; +lazy_static! { + // TODO In the future, we'll replace this by a dynamical value of the user's choice. + pub static ref TRANSACTION_FEE_MARGIN: Percentage = Percentage::new(15); +} + pub struct BlockchainInterfaceWeb3 where T: 'static + BatchTransport + Debug, From 73ec487a74a25414ddc89e2f40402570dbab4567 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 24 Apr 2024 23:40:23 +0200 Subject: [PATCH 180/250] GH-711: some last fixes of things I knew of, like todos!() and a detail in the Percentage class --- masq_lib/src/percentage.rs | 38 +++++++++---- node/src/accountant/mod.rs | 35 ++++++------ node/src/accountant/payment_adjuster/mod.rs | 55 +++++++++++++++++-- .../payable_scanner/agent_null.rs | 17 +++++- .../payable_scanner/agent_web3.rs | 9 ++- node/src/accountant/scanners/mod.rs | 25 +++++---- .../blockchain_interface_web3/mod.rs | 6 +- 7 files changed, 136 insertions(+), 49 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 2aa198b13..ff86b07d2 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -6,6 +6,7 @@ use num::CheckedSub; use num::{CheckedDiv, CheckedMul, Integer}; use std::any::type_name; use std::fmt::Debug; +use std::ops::Div; use std::ops::Mul; // It's designed for a storage of values from 0 to 100, after which it can be used to compute @@ -35,12 +36,11 @@ impl Percentage { return zero; } - let a = N::from(self.per_cent) - .checked_mul(&num) - .unwrap_or_else(|| panic!( - "Overflow when using 'Percentage' for num {:?} of type {}, trying to compute {} per \ - cent of it.", num, type_name::(),self.per_cent - )); + let a = match N::from(self.per_cent).checked_mul(&num) { + Some(num) => num, + None => return self.handle_upper_overflow(num), + }; + if a < N::from(10) { return N::from(0); } @@ -113,6 +113,13 @@ impl Percentage { unreachable!("Check to prevent numbers with fewer than two digits failed") } } + + fn handle_upper_overflow(&self, num: N) -> N + where + N: From + Div + Mul, + { + num / From::from(100) * N::from(self.per_cent) + } } #[cfg(test)] @@ -224,12 +231,19 @@ mod tests { } #[test] - #[should_panic( - expected = "Overflow when using 'Percentage' for num 18446744073709551615 of type u64, \ - trying to compute 2 per cent of it" - )] - fn hits_upper_overflow() { - let _: u64 = Percentage::new(2).of(u64::MAX); + fn preventing_early_upper_overflow() { + let quite_large_value = u64::MAX / 60; + // The standard algorythm begins by a multiplication with this 61, which would cause + // an overflow, so for such large numbers like this one we switch the order of operations. + // We're gonna devide it by 100 first and multiple after it. (However, we'd lose some + // precision in smaller numbers that same way). Why that much effort? I don't want to see + // an overflow happen where most people would't anticipate it: when going for a percentage + // from their number, implaying a request to receive another number, but always smaller + // than that passed in. + let result = Percentage::new(61).of(quite_large_value); + + let expected_result = (quite_large_value / 100) * 61; + assert_eq!(result, expected_result); } #[test] diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 4c83d0664..92919926c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1737,20 +1737,20 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Insolvency detected, followed by considering a payment adjustment, \ - however, giving no satisfactory solution. Please note that your disponible means are \ - not able to cover even an reasonable portion out of any payable among those recently \ - qualified for an imminent payment. You are kindly requested to add funds into your \ - consuming wallet in order to stay free of delinquency bans that your creditors may \ - apply against you. Failure reason: Found a smaller transaction fee balance than it does \ - for a single payment. Number of canceled payments: 1. Transaction fee by single account: \ - 3,300,000 wei. Consuming wallet balance: 123,000,000,000 wei." + "WARN: {test_name}: Insolvency detected led to an analysis of feasibility for making \ + payments adjustment, however, giving no satisfactory solution. Please be advised that \ + your balances can cover neither reasonable portion of any of those payables recently \ + qualified for an imminent payment. You must add more funds into your consuming wallet \ + in order to stay off delinquency bans that your creditors may apply against you \ + otherwise. Details: Found transaction fee balance that is not enough for a single \ + payment. Number of canceled payments: 1. Transaction fee per payment: 3,300,000 wei, \ + while in wallet: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. \ - If matured payables stay untreated long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay \ + untreated long, your creditors may impose a ban on you" )); } @@ -1770,15 +1770,16 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Payment adjustment has not produced any executable payments. Please add funds \ - into your consuming wallet in order to avoid bans from your creditors. Failure reason: The adjustment \ - algorithm had to eliminate each payable from the recently urged payment due to lack of resources" + "WARN: {test_name}: Payment adjustment has not produced any executable payments. Please \ + add funds into your consuming wallet in order to avoid bans from your creditors. Details: \ + The adjustment algorithm had to eliminate each payable from the recently urged payment due \ + to lack of resources" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated long, your \ - creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated \ + long, your creditors may impose a ban on you" )); } @@ -1815,8 +1816,8 @@ mod tests { // Test didn't blow up while the subject was unbound to other actors // therefore we didn't attempt to send the NodeUiMessage TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated long, \ - your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated \ + long, your creditors may impose a ban on you" )); } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0f604f9f2..ff61554fe 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -56,6 +56,7 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::time::SystemTime; use thousands::Separable; +use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; @@ -436,7 +437,7 @@ impl AdjustmentAnalysis { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, VariantCount)] pub enum PaymentAdjusterError { NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: usize, @@ -452,6 +453,22 @@ pub enum PaymentAdjusterError { AllAccountsEliminated, } +impl PaymentAdjusterError { + pub fn insolvency_detected(&self) -> bool { + match self { + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => true, + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + .. + } => true, + PaymentAdjusterError::AllAccountsEliminated => true, + // So far, we don't have to worry much, but adding an error that doesn't mean at the same + // time that the insolvency is a thing might be just a matter of time. Then, it's important + // to check for those consequences (Hint: It is anticipated to affect the wording + // of error announcements back nearer the Accountant's own area) + } + } +} + impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -819,7 +836,7 @@ mod tests { #[test] fn payment_adjuster_error_implements_display() { - vec![ + let inputs = vec![ ( PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { number_of_accounts: 5, @@ -863,9 +880,37 @@ mod tests { "The adjustment algorithm had to eliminate each payable from the recently urged \ payment due to lack of resources.", ), - ] - .into_iter() - .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)) + ]; + let inputs_count = inputs.len(); + inputs + .into_iter() + .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)); + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 1) + } + + #[test] + fn we_can_say_if_error_occurred_after_insolvency_was_detected() { + let inputs = vec![ + PaymentAdjusterError::AllAccountsEliminated, + PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + number_of_accounts: 0, + total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + transaction_fee_appendix_opt: None, + }, + PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + number_of_accounts: 0, + per_transaction_requirement_minor: 0, + cw_transaction_fee_balance_minor: Default::default(), + }, + ]; + let inputs_count = inputs.len(); + let results = inputs + .into_iter() + .map(|err| err.insolvency_detected()) + .collect::>(); + assert_eq!(results, vec![true, true, true]); + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT) } #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 439409aec..b3bef6be5 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -34,7 +34,8 @@ impl BlockchainAgent for BlockchainAgentNull { } fn agreed_transaction_fee_margin(&self) -> Percentage { - todo!() + self.log_function_call("agreed_transaction_fee_margin()"); + Percentage::new(0) } fn consuming_wallet(&self) -> &Wallet { @@ -84,6 +85,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; + use masq_lib::percentage::Percentage; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use web3::types::U256; @@ -173,6 +175,19 @@ mod tests { assert_error_log(test_name, "agreed_fee_per_computation_unit") } + #[test] + fn null_agent_agreed_transaction_fee_margin() { + init_test_logging(); + let test_name = "null_agent_agreed_transaction_fee_margin"; + let mut subject = BlockchainAgentNull::new(); + subject.logger = Logger::new(test_name); + + let result = subject.agreed_transaction_fee_margin(); + + assert_eq!(result, Percentage::new(0)); + assert_error_log(test_name, "agreed_transaction_fee_margin") + } + #[test] fn null_agent_consuming_wallet() { init_test_logging(); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 109d392a0..cc4caf32f 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -6,6 +6,7 @@ use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::accountant::gwei_to_wei; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use masq_lib::percentage::Percentage; use web3::types::U256; @@ -13,6 +14,7 @@ use web3::types::U256; pub struct BlockchainAgentWeb3 { gas_price_gwei: u64, gas_limit_const_part: u64, + agreed_transaction_fee_margin: Percentage, maximum_added_gas_margin: u64, consuming_wallet: Wallet, consuming_wallet_balances: ConsumingWalletBalances, @@ -41,7 +43,7 @@ impl BlockchainAgent for BlockchainAgentWeb3 { } fn agreed_transaction_fee_margin(&self) -> Percentage { - todo!() + self.agreed_transaction_fee_margin } fn consuming_wallet(&self) -> &Wallet { @@ -65,11 +67,14 @@ impl BlockchainAgentWeb3 { consuming_wallet_balances: ConsumingWalletBalances, pending_transaction_id: U256, ) -> Self { + let agreed_transaction_fee_margin = *TRANSACTION_FEE_MARGIN; + let maximum_added_gas_margin = WEB3_MAXIMAL_GAS_LIMIT_MARGIN; Self { gas_price_gwei, gas_limit_const_part, + agreed_transaction_fee_margin, consuming_wallet, - maximum_added_gas_margin: WEB3_MAXIMAL_GAS_LIMIT_MARGIN, + maximum_added_gas_margin, consuming_wallet_balances, pending_transaction_id, } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 04b7cd23e..990b9a2bc 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -285,16 +285,20 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { Some(either) } Err(e) => { - warning!( + if e.insolvency_detected() { + warning!( logger, - "Insolvency detected, followed by considering a payment adjustment, however, \ - giving no satisfactory solution. Please note that your disponible means are not \ - able to cover even an reasonable portion out of any payable among those \ - recently qualified for an imminent payment. You are kindly requested to add \ - funds into your consuming wallet in order to stay free of delinquency bans \ - that your creditors may apply against you. Failure reason: {}.", + "Insolvency detected led to an analysis of feasibility for making payments \ + adjustment, however, giving no satisfactory solution. Please be advised that \ + your balances can cover neither reasonable portion of any of those payables \ + recently qualified for an imminent payment. You must add more funds into your \ + consuming wallet in order to stay off delinquency bans that your creditors may \ + apply against you otherwise. Details: {}.", e - ); + ) + } else { + unimplemented!("This situation is not possible yet, but may be in the future") + } None } } @@ -315,9 +319,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { Err(e) => { warning!( logger, - "Payment adjustment has not produced any executable payments. Please \ - add funds into your consuming wallet in order to avoid bans from your creditors. \ - Failure reason: {}", + "Payment adjustment has not produced any executable payments. Please add funds \ + into your consuming wallet in order to avoid bans from your creditors. Details: {}", e ); None 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 d4a1be324..1b0baa947 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -626,7 +626,8 @@ mod tests { use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ - BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, + BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_FEE_MARGIN, + TRANSACTION_LITERAL, }; use crate::blockchain::blockchain_interface::test_utils::{ test_blockchain_interface_is_connected_and_functioning, LowBlockchainIntMock, @@ -671,6 +672,7 @@ mod tests { BlockchainTransaction, RpcPayablesFailure, }; use indoc::indoc; + use masq_lib::percentage::Percentage; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::SystemTime; @@ -712,6 +714,7 @@ mod tests { assert_eq!(CONTRACT_ABI, contract_abi_expected); assert_eq!(TRANSACTION_LITERAL, transaction_literal_expected); assert_eq!(REQUESTS_IN_PARALLEL, 1); + assert_eq!(*TRANSACTION_FEE_MARGIN, Percentage::new(15)); } #[test] @@ -1068,6 +1071,7 @@ mod tests { transaction_fee_balance ); assert_eq!(result.service_fee_balance_minor(), masq_balance.as_u128()); + assert_eq!(result.agreed_transaction_fee_margin(), Percentage::new(15)); assert_eq!(result.agreed_fee_per_computation_unit(), 50); assert_eq!( result.estimated_transaction_fee_per_transaction_minor(), From 8d89773ed62a678a1c8da8fd1b9a67c2b3c64986 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 Apr 2024 00:15:28 +0200 Subject: [PATCH 181/250] GH-711: seems to work with all/sh; let's try m. tests --- masq_lib/src/percentage.rs | 2 +- .../payment_adjuster/disqualification_arbiter.rs | 6 +----- .../miscellaneous/data_structures.rs | 2 +- .../payment_adjuster/preparatory_analyser/mod.rs | 2 +- node/src/sub_lib/utils.rs | 12 ++++++++++++ node/src/test_utils/mod.rs | 11 +---------- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index ff86b07d2..20c03f14a 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -14,7 +14,7 @@ use std::ops::Mul; // digit in order to diminish the effect of a precision loss genuinly implied by this kind of math // operations done on integers. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Percentage { per_cent: u8, } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 3b7b053f7..dbe288a5b 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -191,11 +191,7 @@ impl DisqualificationGaugeReal { let second_condition = considered_forgiven >= Self::SECOND_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; - if first_condition && second_condition { - true - } else { - false - } + first_condition && second_condition } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index d54a22623..d19d38a5d 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -97,7 +97,7 @@ impl UnconfirmedAdjustment { } pub fn wallet(&self) -> &Wallet { - &self.weighted_account.wallet() + self.weighted_account.wallet() } pub fn balance_minor(&self) -> u128 { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 0d5fa1635..3f28dfd40 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -192,7 +192,7 @@ impl PreparatoryAnalyzer { AnalyzableAccounts: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, { let lowest_disqualification_limit = - Self::find_lowest_disqualification_limit(&prepared_accounts); + Self::find_lowest_disqualification_limit(prepared_accounts); // We cannot do much in this area but stepping in if the cw balance is zero or nearly // zero with the assumption that the debt with the lowest disqualification limit in diff --git a/node/src/sub_lib/utils.rs b/node/src/sub_lib/utils.rs index 4b493cb5d..c40053f0d 100644 --- a/node/src/sub_lib/utils.rs +++ b/node/src/sub_lib/utils.rs @@ -251,6 +251,18 @@ pub struct MessageScheduler { pub delay: Duration, } +// Yes, this refers to code from the test tree, but we pull a trick by letting the guts vanish in +// the release mode, therefore we can use this outer macro without bad effects, and we also don't +// have to add the #[cfg(test)] marks above the invocation itself, but also the related 'use' +// clause. +#[macro_export] +macro_rules! arbitrary_id_stamp_in_trait { + () => { + #[cfg(test)] + $crate::arbitrary_id_stamp_in_trait_internal___!(); + }; +} + #[cfg(test)] mod tests { use super::*; diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 8d7e2bd24..037d36f63 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -962,7 +962,6 @@ pub mod unshared_test_utils { pub mod arbitrary_id_stamp { use super::*; - use crate::arbitrary_id_stamp_in_trait; //The issues we are to solve might look as follows: @@ -1011,16 +1010,8 @@ pub mod unshared_test_utils { } } - #[macro_export] - macro_rules! arbitrary_id_stamp_in_trait { - () => { - #[cfg(test)] - $crate::arbitrary_id_stamp_in_trait_internal___!(); - }; - } - // To be added together with other methods in your trait - // DO NOT USE ME DIRECTLY, USE arbitrary_id_stamp_in_trait INSTEAD! + // DO NOT USE ME DIRECTLY, INSTEAD: arbitrary_id_stamp_in_trait! #[macro_export] macro_rules! arbitrary_id_stamp_in_trait_internal___ { () => { From e9af434385ab79e80ba22dc0f9700f5d4c3e4488 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 Apr 2024 20:16:24 +0200 Subject: [PATCH 182/250] GH-711: verify_bill_payments repaired for pure linux --- multinode_integration_tests/ci/all.sh | 2 +- .../tests/verify_bill_payment.rs | 154 ++++++++++++------ .../logging_and_diagnostics/log_functions.rs | 9 +- node/src/accountant/payment_adjuster/mod.rs | 7 +- .../preparatory_analyser/mod.rs | 24 +-- .../blockchain_interface_web3/mod.rs | 46 +----- node/src/sub_lib/blockchain_interface_web3.rs | 29 +++- 7 files changed, 156 insertions(+), 115 deletions(-) diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index 6ce38689c..09b1832ce 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -46,5 +46,5 @@ popd pushd "$CI_DIR/.." export RUSTFLAGS="-D warnings -Anon-snake-case" ci/lint.sh -cargo test --release -- --nocapture --test-threads=1 +cargo test payments_were_adjusted_due_to_insufficient_balances --release -- --nocapture --test-threads=1 popd diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 52c472694..f150a39b9 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -6,6 +6,7 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; +use masq_lib::percentage::Percentage; use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; @@ -30,7 +31,9 @@ use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, }; use node_lib::sub_lib::accountant::PaymentThresholds; -use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; +use node_lib::sub_lib::blockchain_interface_web3::{ + compute_gas_limit, transaction_data_web3, web3_gas_limit_const_part, +}; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; use rusqlite::ToSql; @@ -169,9 +172,31 @@ fn payments_were_adjusted_due_to_insufficient_balances() { let owed_to_serv_node_3_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 60_000_000); // Assuming all Nodes rely on the same set of payment thresholds - let cons_node_initial_service_fee_balance_minor = (owed_to_serv_node_2_minor - + owed_to_serv_node_3_minor) - - gwei_to_wei::(40_000_000); + let cons_node_initial_service_fee_balance_minor = (owed_to_serv_node_1_minor + + owed_to_serv_node_2_minor) + - gwei_to_wei::(2_345_678); + let agreed_transaction_fee_unit_price_major = 60; + let transaction_fee_needed_to_pay_for_one_payment_major = { + // We will need less but this should be accurate enough + let txn_data_with_maximized_cost = [0xff; 68]; + let gas_limit_dev_chain = { + let const_part = web3_gas_limit_const_part(Chain::Dev); + u64::try_from(compute_gas_limit( + const_part, + txn_data_with_maximized_cost.as_slice(), + )) + .unwrap() + }; + let transaction_fee_margin = Percentage::new(15); + transaction_fee_margin + .add_percent_to(gas_limit_dev_chain * agreed_transaction_fee_unit_price_major) + }; + eprintln!( + "Computed transaction fee: {}", + transaction_fee_needed_to_pay_for_one_payment_major + ); + let cons_node_transaction_fee_balance_minor = + 2 * gwei_to_wei::(transaction_fee_needed_to_pay_for_one_payment_major); let test_global_config = TestInputsOutputsConfig { ui_ports_opt: Some(Ports { consuming_node: consuming_node_ui_port, @@ -179,9 +204,10 @@ fn payments_were_adjusted_due_to_insufficient_balances() { serving_node_2: serving_node_2_ui_port, serving_node_3: serving_node_3_ui_port, }), - // Should be enough only for two payments therefore the least significant one will - // need to go away - cons_node_initial_transaction_fee_balance_minor_opt: Some(gwei_to_wei::<_, u64>(8_000_000)), + // Should be enough only for two payments therefore the least significant one will fall out + cons_node_initial_transaction_fee_balance_minor_opt: Some( + cons_node_transaction_fee_balance_minor + 1, + ), cons_node_initial_service_fee_balance_minor, debts_config: Either::Right(FullySpecifiedSimulatedDebts { // This account will be the least significant and be eliminated for the reasons @@ -190,7 +216,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { balance_minor: owed_to_serv_node_1_minor, age_s: payment_thresholds.maturity_threshold_sec + 1000, }, - // This account has the middle amount in the balance but + // This account has the middle amount in the balance, but // it is stressed by the age, which will cause this one will // evaluate with the highest significance owed_to_serving_node_2: AccountedDebt { @@ -206,15 +232,20 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }, }), payment_thresholds_all_nodes: payment_thresholds, - cons_node_transaction_fee_agreed_unit_price_opt: Some(60), - exp_final_cons_node_transaction_fee_balance_minor: 2_601_680_000_000_000, - exp_final_cons_node_service_fee_balance_minor: 0, // Because the algorithm is designed to - // exhaust the wallet till the last drop - exp_final_service_fee_balance_serv_node_1_minor: 0, // This account had to be dropped so received no money - exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor, // This account was granted with - // the full size as the higher age had this effect - exp_final_service_fee_balance_serv_node_3_minor: owed_to_serv_node_3_minor - - gwei_to_wei::(40_000_000), + cons_node_transaction_fee_agreed_unit_price_opt: Some( + agreed_transaction_fee_unit_price_major, + ), + // It seems like the ganache server sucked up quite less than those 55_000 units of gas?? + exp_final_cons_node_transaction_fee_balance_minor: 2_828_352_000_000_001, + // Because the algorithm is designed to exhaust the wallet till the last drop + exp_final_cons_node_service_fee_balance_minor: 0, + // This account was granted with the full size as the lowest balance make it weight + // the most + exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, + exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor + - gwei_to_wei::(2_345_678), + // This account had to be dropped so received no money + exp_final_service_fee_balance_serv_node_3_minor: 0, }; let process_scan_request_to_node = @@ -280,36 +311,6 @@ fn make_init_config(chain: Chain) -> DbInitializationConfig { )) } -fn assert_balances( - wallet: &Wallet, - blockchain_interface: &BlockchainInterfaceWeb3, - expected_eth_balance: u128, - expected_token_balance: u128, -) { - let eth_balance = blockchain_interface - .lower_interface() - .get_transaction_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); - assert_eq!( - eth_balance, - web3::types::U256::from(expected_eth_balance), - "Actual EthBalance {} doesn't much with expected {}", - eth_balance, - expected_eth_balance - ); - let token_balance = blockchain_interface - .lower_interface() - .get_service_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); - assert_eq!( - token_balance, - web3::types::U256::from(expected_token_balance), - "Actual TokenBalance {} doesn't match with expected {}", - token_balance, - expected_token_balance - ); -} - fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { let data = "608060405234801561001057600080fd5b5060038054600160a060020a031916331790819055604051600160a060020a0391909116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3610080336b01866de34549d620d8000000640100000000610b9461008582021704565b610156565b600160a060020a038216151561009a57600080fd5b6002546100b490826401000000006109a461013d82021704565b600255600160a060020a0382166000908152602081905260409020546100e790826401000000006109a461013d82021704565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60008282018381101561014f57600080fd5b9392505050565b610c6a806101656000396000f3006080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610100578063095ea7b31461018a57806318160ddd146101c257806323b872dd146101e95780632ff2e9dc14610213578063313ce56714610228578063395093511461025357806342966c681461027757806370a0823114610291578063715018a6146102b257806379cc6790146102c75780638da5cb5b146102eb5780638f32d59b1461031c57806395d89b4114610331578063a457c2d714610346578063a9059cbb1461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561014f578181015183820152602001610137565b50505050905090810190601f16801561017c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019657600080fd5b506101ae600160a060020a0360043516602435610436565b604080519115158252519081900360200190f35b3480156101ce57600080fd5b506101d7610516565b60408051918252519081900360200190f35b3480156101f557600080fd5b506101ae600160a060020a036004358116906024351660443561051c565b34801561021f57600080fd5b506101d76105b9565b34801561023457600080fd5b5061023d6105c9565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b506101ae600160a060020a03600435166024356105ce565b34801561028357600080fd5b5061028f60043561067e565b005b34801561029d57600080fd5b506101d7600160a060020a036004351661068b565b3480156102be57600080fd5b5061028f6106a6565b3480156102d357600080fd5b5061028f600160a060020a0360043516602435610710565b3480156102f757600080fd5b5061030061071e565b60408051600160a060020a039092168252519081900360200190f35b34801561032857600080fd5b506101ae61072d565b34801561033d57600080fd5b5061011561073e565b34801561035257600080fd5b506101ae600160a060020a0360043516602435610775565b34801561037657600080fd5b506101ae600160a060020a03600435166024356107c0565b34801561039a57600080fd5b506101d7600160a060020a03600435811690602435166107d6565b3480156103c157600080fd5b5061028f600160a060020a0360043516610801565b606060405190810160405280602481526020017f486f7420746865206e657720746f6b656e20796f75277265206c6f6f6b696e6781526020017f20666f720000000000000000000000000000000000000000000000000000000081525081565b600081158061044c575061044a33846107d6565b155b151561050557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f55736520696e637265617365417070726f76616c206f7220646563726561736560448201527f417070726f76616c20746f2070726576656e7420646f75626c652d7370656e6460648201527f2e00000000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b61050f838361081d565b9392505050565b60025490565b600160a060020a038316600090815260016020908152604080832033845290915281205482111561054c57600080fd5b600160a060020a0384166000908152600160209081526040808320338452909152902054610580908363ffffffff61089b16565b600160a060020a03851660009081526001602090815260408083203384529091529020556105af8484846108b2565b5060019392505050565b6b01866de34549d620d800000081565b601281565b6000600160a060020a03831615156105e557600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff6109a416565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b61068833826109b6565b50565b600160a060020a031660009081526020819052604090205490565b6106ae61072d565b15156106b957600080fd5b600354604051600091600160a060020a0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36003805473ffffffffffffffffffffffffffffffffffffffff19169055565b61071a8282610a84565b5050565b600354600160a060020a031690565b600354600160a060020a0316331490565b60408051808201909152600381527f484f540000000000000000000000000000000000000000000000000000000000602082015281565b6000600160a060020a038316151561078c57600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff61089b16565b60006107cd3384846108b2565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b61080961072d565b151561081457600080fd5b61068881610b16565b6000600160a060020a038316151561083457600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b600080838311156108ab57600080fd5b5050900390565b600160a060020a0383166000908152602081905260409020548111156108d757600080fd5b600160a060020a03821615156108ec57600080fd5b600160a060020a038316600090815260208190526040902054610915908263ffffffff61089b16565b600160a060020a03808516600090815260208190526040808220939093559084168152205461094a908263ffffffff6109a416565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282018381101561050f57600080fd5b600160a060020a03821615156109cb57600080fd5b600160a060020a0382166000908152602081905260409020548111156109f057600080fd5b600254610a03908263ffffffff61089b16565b600255600160a060020a038216600090815260208190526040902054610a2f908263ffffffff61089b16565b600160a060020a038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600160a060020a0382166000908152600160209081526040808320338452909152902054811115610ab457600080fd5b600160a060020a0382166000908152600160209081526040808320338452909152902054610ae8908263ffffffff61089b16565b600160a060020a038316600090815260016020908152604080832033845290915290205561071a82826109b6565b600160a060020a0381161515610b2b57600080fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600160a060020a0382161515610ba957600080fd5b600254610bbc908263ffffffff6109a416565b600255600160a060020a038216600090815260208190526040902054610be8908263ffffffff6109a416565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350505600a165627a7a72305820d4ad56dfe541fec48c3ecb02cebad565a998dfca7774c0c4f4b1f4a8e2363a590029".from_hex::>().unwrap(); let gas_price = 50_000_000_000_u64; @@ -370,7 +371,7 @@ fn transfer_service_fee_amount_to_address( .wait() { Ok(tx_hash) => eprintln!( - "Transaction {:?} of {} wei of MASQ was sent from wallet {:?} to {:?}", + "Transaction {:?} of {} wei of MASQ was sent from wallet {} to {}", tx_hash, amount_minor, from_wallet, to_wallet ), Err(e) => panic!("Transaction for token transfer failed {:?}", e), @@ -420,14 +421,11 @@ fn transfer_transaction_fee_amount_to_address( { Ok(was_successful) => { if was_successful { - eprintln!( - "Account {:?} unlocked for a single transaction", - from_wallet.address() - ) + eprintln!("Account {} unlocked for a single transaction", from_wallet) } else { panic!( - "Couldn't unlock account {:?} for the purpose of signing the next transaction", - from_wallet.address() + "Couldn't unlock account {} for the purpose of signing the next transaction", + from_wallet ) } } @@ -447,6 +445,38 @@ fn transfer_transaction_fee_amount_to_address( } } +fn assert_balances( + wallet: &Wallet, + blockchain_interface: &BlockchainInterfaceWeb3, + expected_eth_balance: u128, + expected_token_balance: u128, +) { + let eth_balance = blockchain_interface + .lower_interface() + .get_transaction_fee_balance(&wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); + assert_eq!( + eth_balance, + web3::types::U256::from(expected_eth_balance), + "Actual EthBalance {} doesn't much with expected {} for {}", + eth_balance, + expected_eth_balance, + wallet + ); + let token_balance = blockchain_interface + .lower_interface() + .get_service_fee_balance(&wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); + assert_eq!( + token_balance, + web3::types::U256::from(expected_token_balance), + "Actual TokenBalance {} doesn't match with expected {} for {}", + token_balance, + expected_token_balance, + wallet + ); +} + fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { let extended_priv_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); let secret = extended_priv_key.secret().to_hex::(); @@ -701,6 +731,10 @@ fn test_body(100), + per_transaction_requirement_minor: TRANSACTION_FEE_MARGIN + .add_percent_to(55_000 * gwei_to_wei::(100)), cw_transaction_fee_balance_minor: U256::from(54_000) * gwei_to_wei::(100) } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 3f28dfd40..87f0e60cd 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -133,37 +133,40 @@ impl PreparatoryAnalyzer { number_of_qualified_accounts: usize, logger: &Logger, ) -> Result, PaymentAdjusterError> { + let txn_fee_required_per_txn_minor_with_margin = + agreed_transaction_fee_margin.add_percent_to(per_transaction_requirement_minor); + let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, - per_transaction_requirement_minor, - agreed_transaction_fee_margin, + txn_fee_required_per_txn_minor_with_margin, number_of_qualified_accounts, ); let max_tx_count_we_can_afford: u16 = verified_tx_counts.affordable; - let required_tx_count_with_margin: u16 = verified_tx_counts.required; + let required_tx_count: u16 = verified_tx_counts.required; if max_tx_count_we_can_afford == 0 { Err( PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { number_of_accounts: number_of_qualified_accounts, - per_transaction_requirement_minor, + per_transaction_requirement_minor: txn_fee_required_per_txn_minor_with_margin, cw_transaction_fee_balance_minor, }, ) - } else if max_tx_count_we_can_afford >= required_tx_count_with_margin { + } else if max_tx_count_we_can_afford >= required_tx_count { Ok(None) } else { log_insufficient_transaction_fee_balance( logger, - required_tx_count_with_margin, + required_tx_count, + txn_fee_required_per_txn_minor_with_margin, cw_transaction_fee_balance_minor, max_tx_count_we_can_afford, ); let transaction_fee_limitation_opt = TransactionFeeLimitation::new( max_tx_count_we_can_afford, cw_transaction_fee_balance_minor.as_u128(), - per_transaction_requirement_minor, + txn_fee_required_per_txn_minor_with_margin, ); Ok(Some(transaction_fee_limitation_opt)) } @@ -172,13 +175,10 @@ impl PreparatoryAnalyzer { fn transaction_counts_verification( cw_transaction_fee_balance_minor: U256, txn_fee_required_per_txn_minor: u128, - txn_fee_margin: Percentage, number_of_qualified_accounts: usize, ) -> TransactionCountsBy16bits { - let txn_fee_required_per_txn_minor_with_margin = - txn_fee_margin.add_percent_to(txn_fee_required_per_txn_minor); - let max_possible_tx_count_u256 = cw_transaction_fee_balance_minor - / U256::from(txn_fee_required_per_txn_minor_with_margin); + let max_possible_tx_count_u256 = + cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) } 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 1b0baa947..b2eee2521 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -40,7 +40,7 @@ use web3::{BatchTransport, Error, Web3}; use masq_lib::percentage::Percentage; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; use crate::blockchain::blockchain_interface::data_structures::{BlockchainTransaction, ProcessedPayableFallible, RpcPayablesFailure}; -use crate::sub_lib::blockchain_interface_web3::transaction_data_web3; +use crate::sub_lib::blockchain_interface_web3::{compute_gas_limit, transaction_data_web3, web3_gas_limit_const_part}; const CONTRACT_ABI: &str = indoc!( r#"[{ @@ -349,7 +349,7 @@ where Rc::clone(&web3_batch), contract, )); - let gas_limit_const_part = Self::web3_gas_limit_const_part(chain); + let gas_limit_const_part = web3_gas_limit_const_part(chain); Self { logger: Logger::new("BlockchainInterface"), @@ -514,7 +514,7 @@ where gas_price: u64, ) -> Result { let data = transaction_data_web3(recipient, amount); - let gas_limit = self.compute_gas_limit(data.as_slice()); + let gas_limit = compute_gas_limit(self.gas_limit_const_part, data.as_slice()); let gas_price = gwei_to_wei::(gas_price); let transaction_parameters = TransactionParameters { nonce: Some(nonce), @@ -566,20 +566,6 @@ where introduction.chain(body).collect() } - fn compute_gas_limit(&self, data: &[u8]) -> U256 { - ethereum_types::U256::try_from(data.iter().fold(self.gas_limit_const_part, |acc, v| { - acc + if v == &0u8 { 4 } else { 68 } - })) - .expect("Internal error") - } - - fn web3_gas_limit_const_part(chain: Chain) -> u64 { - match chain { - Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => 55_000, - Chain::PolyMainnet | Chain::PolyMumbai => 70_000, - } - } - fn extract_transactions_from_logs(&self, logs: Vec) -> Vec { logs.iter() .filter_map(|log: &Log| match log.block_number { @@ -671,6 +657,7 @@ mod tests { use crate::blockchain::blockchain_interface::data_structures::{ BlockchainTransaction, RpcPayablesFailure, }; + use crate::sub_lib::blockchain_interface_web3::web3_gas_limit_const_part; use indoc::indoc; use masq_lib::percentage::Percentage; use std::str::FromStr; @@ -1572,28 +1559,6 @@ mod tests { assert_eq!(accountant_recording.len(), 1) } - #[test] - fn web3_gas_limit_const_part_returns_reasonable_values() { - type Subject = BlockchainInterfaceWeb3; - assert_eq!( - Subject::web3_gas_limit_const_part(Chain::EthMainnet), - 55_000 - ); - assert_eq!( - Subject::web3_gas_limit_const_part(Chain::EthRopsten), - 55_000 - ); - assert_eq!( - Subject::web3_gas_limit_const_part(Chain::PolyMainnet), - 70_000 - ); - assert_eq!( - Subject::web3_gas_limit_const_part(Chain::PolyMumbai), - 70_000 - ); - assert_eq!(Subject::web3_gas_limit_const_part(Chain::Dev), 55_000); - } - #[test] fn gas_limit_for_polygon_mainnet_lies_within_limits_for_raw_transaction() { test_gas_limit_is_between_limits(Chain::PolyMainnet); @@ -1609,8 +1574,7 @@ mod tests { let transport = TestTransport::default(); let mut subject = BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain); - let not_under_this_value = - BlockchainInterfaceWeb3::::web3_gas_limit_const_part(chain); + let not_under_this_value = web3_gas_limit_const_part(chain); let not_above_this_value = not_under_this_value + WEB3_MAXIMAL_GAS_LIMIT_MARGIN; let consuming_wallet_secret_raw_bytes = b"my-wallet"; let batch_payable_tools = BatchPayableToolsMock::::default() diff --git a/node/src/sub_lib/blockchain_interface_web3.rs b/node/src/sub_lib/blockchain_interface_web3.rs index 98224b49a..d9b8044fb 100644 --- a/node/src/sub_lib/blockchain_interface_web3.rs +++ b/node/src/sub_lib/blockchain_interface_web3.rs @@ -1,6 +1,7 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::sub_lib::wallet::Wallet; +use masq_lib::blockchains::chains::Chain; use web3::types::U256; const TRANSFER_METHOD_ID: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; @@ -15,10 +16,27 @@ pub fn transaction_data_web3(recipient: &Wallet, amount: u128) -> [u8; 68] { data } +pub fn compute_gas_limit(gas_limit_const_part: u64, data: &[u8]) -> U256 { + ethereum_types::U256::try_from(data.iter().fold(gas_limit_const_part, |acc, v| { + acc + if v == &0u8 { 4 } else { 68 } + })) + .expect("Internal error") +} + +pub fn web3_gas_limit_const_part(chain: Chain) -> u64 { + match chain { + Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => 55_000, + Chain::PolyMainnet | Chain::PolyMumbai => 70_000, + } +} + #[cfg(test)] mod tests { - use crate::sub_lib::blockchain_interface_web3::TRANSFER_METHOD_ID; + use crate::sub_lib::blockchain_interface_web3::{ + web3_gas_limit_const_part, TRANSFER_METHOD_ID, + }; use ethsign_crypto::Keccak256; + use masq_lib::blockchains::chains::Chain; #[test] fn constants_are_correct() { @@ -32,4 +50,13 @@ mod tests { TRANSFER_METHOD_ID, ); } + + #[test] + fn web3_gas_limit_const_part_returns_reasonable_values() { + assert_eq!(web3_gas_limit_const_part(Chain::EthMainnet), 55_000); + assert_eq!(web3_gas_limit_const_part(Chain::EthRopsten), 55_000); + assert_eq!(web3_gas_limit_const_part(Chain::PolyMainnet), 70_000); + assert_eq!(web3_gas_limit_const_part(Chain::PolyMumbai), 70_000); + assert_eq!(web3_gas_limit_const_part(Chain::Dev), 55_000); + } } From d4b452eacf9d4376f601f6ed62def8e2c09039f8 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Fri, 26 Apr 2024 04:25:18 +0800 Subject: [PATCH 183/250] GH-711: interestingly, I had fixed it so that the parameter values for gnache are always the same even for linux/arch64, but forgot it went so well. Surprise! --- multinode_integration_tests/tests/verify_bill_payment.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index f150a39b9..9cabe725a 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -239,8 +239,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { exp_final_cons_node_transaction_fee_balance_minor: 2_828_352_000_000_001, // Because the algorithm is designed to exhaust the wallet till the last drop exp_final_cons_node_service_fee_balance_minor: 0, - // This account was granted with the full size as the lowest balance make it weight - // the most + // This account was granted with the full size as the lowest balance make it weight the most exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor - gwei_to_wei::(2_345_678), From 3eed724bdac42d56207c54f47c8ad600aa314520 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 28 Apr 2024 19:11:57 +0200 Subject: [PATCH 184/250] GH-711: self-review ended; will ponder because of the loading test and unexpected results, anyway --- multinode_integration_tests/ci/all.sh | 2 +- .../blockchain/amd64_linux/entrypoint.sh | 14 +- .../blockchain/arm64_linux/entrypoint.sh | 14 +- .../tests/verify_bill_payment.rs | 47 +++-- .../payment_adjuster/adjustment_runners.rs | 6 +- .../balance_and_age_calculator.rs | 2 +- .../disqualification_arbiter.rs | 3 +- .../miscellaneous/helper_functions.rs | 20 +- node/src/accountant/payment_adjuster/mod.rs | 26 +-- .../payment_adjuster/non_unit_tests/mod.rs | 178 +++++++++++++----- .../accounts_abstraction.rs | 1 - node/src/accountant/scanners/mod.rs | 10 +- .../src/accountant/scanners/scanners_utils.rs | 2 + node/src/sub_lib/blockchain_bridge.rs | 9 +- node/src/sub_lib/utils.rs | 8 +- 15 files changed, 207 insertions(+), 135 deletions(-) diff --git a/multinode_integration_tests/ci/all.sh b/multinode_integration_tests/ci/all.sh index 09b1832ce..6ce38689c 100755 --- a/multinode_integration_tests/ci/all.sh +++ b/multinode_integration_tests/ci/all.sh @@ -46,5 +46,5 @@ popd pushd "$CI_DIR/.." export RUSTFLAGS="-D warnings -Anon-snake-case" ci/lint.sh -cargo test payments_were_adjusted_due_to_insufficient_balances --release -- --nocapture --test-threads=1 +cargo test --release -- --nocapture --test-threads=1 popd diff --git a/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh index d9f99800c..013f9a68d 100755 --- a/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh +++ b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh @@ -1,12 +1,12 @@ #!/bin/sh -# All ordinal wallets begin with zero balances. Except the contract owner wallet as the only one populated with usable -# means. The means are meant to be redistributed from here to any accounts that will need it. Notice the argument -# --account ',' that assigns a certain initial balance. -# -# This same principle of wallets fed from this centric wallet at the test setup is followed by both the gas currency -# and the MASQ tokens. With those, there is practically no other option for their strong dependency on the blockchain -# smart contract that defines the entire supply is deployed to the contract owner's wallet. +# All wallets begin with null balances. The only exception is the contract owner wallet whose means are to be +# redistributed from there to every account that would need it. (Notice the argument --account ',' that assigns a certain initial balance.) This same principle of initialization needs to be +# regarded, during the test setup, and applied with both the transaction fee (wei of ETH) and the service fee (MASQ). +# While on the transaction fee it's a choice done by us, with the latter, there probably isn't any other solution given +# the mechanism how the deployment of the blockchain smart contract generates the entire token supply only on +# the account of the contract owner's wallet from where it must be sent out to other wallets if needed. node /app/ganache-core.docker.cli.js \ -p 18545 \ diff --git a/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh b/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh index 5d97a1121..2ba804e4f 100755 --- a/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh +++ b/multinode_integration_tests/docker/blockchain/arm64_linux/entrypoint.sh @@ -1,12 +1,12 @@ #!/bin/sh -# All ordinal wallets begin with zero balances. Except the contract owner wallet as the only one populated with usable -# means. The means are meant to be redistributed from here to any accounts that will need it. Notice the argument -# --account ',' that assigns a certain initial balance. -# -# This same principle of wallets fed from this centric wallet at the test setup is followed by both the gas currency -# and the MASQ tokens. With those, there is practically no other option for their strong dependency on the blockchain -# smart contract that defines the entire supply is deployed to the contract owner's wallet. +# All wallets begin with null balances. The only exception is the contract owner wallet whose means are to be +# redistributed from there to every account that would need it. (Notice the argument --account ',' that assigns a certain initial balance.) This same principle of initialization needs to be +# regarded, during the test setup, and applied with both the transaction fee (wei of ETH) and the service fee (MASQ). +# While on the transaction fee it's a choice done by us, with the latter, there probably isn't any other solution given +# the mechanism how the deployment of the blockchain smart contract generates the entire token supply only on +# the account of the contract owner's wallet from where it must be sent out to other wallets if needed. ganache-cli \ -h 0.0.0.0 \ diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 9cabe725a..fb19a283f 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -49,14 +49,14 @@ use web3::Web3; #[test] fn verify_bill_payment() { - // Note: besides the main objectives of this test, it relies on (and so it proves) - // the premise each Node, when reaching its full connectivity, becoming able to make a route, + // Note: besides the main objectives of this test, it relies on (and so it proves) the premise + // that each Node, after it reaches its full connectivity and becomes able to make a route, // activates its accountancy module whereas it also unleashes the first cycle of the scanners - // immediately. That's why a consideration has been made not to take out the passages with - // intensive startups of bunch of Nodes, with the only reason to fulfill the above discussed - // condition, even though the test could be rewritten simpler using directly the `scans` - // command from a UI, with less PCU work and in a shorter time. (Such approach is implemented - // for another test in this file.) + // immediately. That's why some consideration has been made not to take out the passage with + // the intense startups of a bunch of Nodes, with that particular reason to fulfill the above + // depicted scenario, even though this test could be written more simply with the use of + // the `scans` command emitted from a UI, with a smaller PCU burden. (You may want to know that + // such an approach is implemented for another test in this file.) let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_500_000, debt_threshold_gwei: 1_000_000_000, @@ -204,28 +204,24 @@ fn payments_were_adjusted_due_to_insufficient_balances() { serving_node_2: serving_node_2_ui_port, serving_node_3: serving_node_3_ui_port, }), - // Should be enough only for two payments therefore the least significant one will fall out + // Should be enough only for two payments, the least significant one will fall out cons_node_initial_transaction_fee_balance_minor_opt: Some( cons_node_transaction_fee_balance_minor + 1, ), cons_node_initial_service_fee_balance_minor, debts_config: Either::Right(FullySpecifiedSimulatedDebts { - // This account will be the least significant and be eliminated for the reasons - // said above + // This account will be the most significant and will deserve the full balance owed_to_serving_node_1: AccountedDebt { balance_minor: owed_to_serv_node_1_minor, age_s: payment_thresholds.maturity_threshold_sec + 1000, }, - // This account has the middle amount in the balance, but - // it is stressed by the age, which will cause this one will - // evaluate with the highest significance + // This balance is of a middle size it will be reduced as there won't be enough + // after the first one is filled up. owed_to_serving_node_2: AccountedDebt { balance_minor: owed_to_serv_node_2_minor, age_s: payment_thresholds.maturity_threshold_sec + 100_000, }, - // This balance is biggest but in the adjusted payment it will - // be reduced on the account of the second serving node, - // gaining the biggest portion from the available means for fees + // This account will be the least significant and therefore eliminated owed_to_serving_node_3: AccountedDebt { balance_minor: owed_to_serv_node_3_minor, age_s: payment_thresholds.maturity_threshold_sec + 30_000, @@ -237,13 +233,14 @@ fn payments_were_adjusted_due_to_insufficient_balances() { ), // It seems like the ganache server sucked up quite less than those 55_000 units of gas?? exp_final_cons_node_transaction_fee_balance_minor: 2_828_352_000_000_001, - // Because the algorithm is designed to exhaust the wallet till the last drop + // Zero reached, because the algorithm is designed to exhaust the wallet completely exp_final_cons_node_service_fee_balance_minor: 0, - // This account was granted with the full size as the lowest balance make it weight the most + // This account was granted with the full size as its lowest balance from the set makes + // it weight the most exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor - gwei_to_wei::(2_345_678), - // This account had to be dropped so received no money + // This account dropped out from the payment, so received no money exp_final_service_fee_balance_serv_node_3_minor: 0, }; @@ -593,12 +590,12 @@ fn expire_receivables(path: PathBuf) { struct TestInputsOutputsConfig { ui_ports_opt: Option, - // Gets ganache default 100 ETH if None. - // Uses an arg where the private key of the wallet and the amount in - // wei must be specified (in these tests the key is hardcoded and - // corresponds to the path m/44'/60'/0'/0/0/1 that belongs to - // the consuming Node). - // Specify number of wei this account should possess at its initialisation + // The contract owner wallet is populated with 100 ETH as defined in the set of commands + // with which we start up the Ganache server. + // + // Specify number of wei this account should possess at its initialisation. + // The consuming node gets the full balance of the contract owner if left as None. + // Cannot ever get more than what the "owner" has. cons_node_initial_transaction_fee_balance_minor_opt: Option, cons_node_initial_service_fee_balance_minor: u128, debts_config: Either, diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 7307e6fac..e692ed2a2 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -126,9 +126,9 @@ mod tests { // Explanation: The hypothesis is that the previous iteration disqualified an account after // which the remaining means are enough for the other accounts. // We could assign the accounts all they initially requested but a fairer way to do that - // is to give out only that much up to the disqualification limit of these accounts. Later on, - // the accounts that deserves it more will split the rest of the means among them (Their - // weights were higher). + // is to give out only that much up to the disqualification limit of these accounts. Later + // on, the accounts that deserves it more, according to their ordering based on their former + // weights, will split the rest of the means among them (Their weights were higher). let now = SystemTime::now(); let mut payment_adjuster = initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs index 703bbddf3..d1c531588 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs @@ -19,7 +19,7 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { account.bare_account.balance_wei - account.payment_threshold_intercept_minor; let diff = largest - this_account; - // We invert the magnitude for smaller debts + // We invert the magnitude of smaller debts, so they weight the most largest + diff } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index dbe288a5b..d26ba4368 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -434,8 +434,7 @@ mod tests { pending_payable_opt: None, }; let wallet_3 = make_wallet("ghi"); - // This account has the largest exceeding balance - // and therefore has the smallest weight + // This account has the largest exceeding balance and therefore has the smallest weight let account_3 = PayableAccount { wallet: wallet_3.clone(), balance_wei: 120_000_000_000 + 2, diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8ff1be15a..9cd22bdb0 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -122,12 +122,12 @@ pub fn exhaust_cw_balance_entirely( }) .fold( init, - run_cw_exhausting_on_possibly_sub_optimal_account_balances, + run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances, ) .accounts_finalized_so_far } -fn run_cw_exhausting_on_possibly_sub_optimal_account_balances( +fn run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances( status: ConsumingWalletExhaustingStatus, non_finalized_account: AdjustedAccountBeforeFinalization, ) -> ConsumingWalletExhaustingStatus { @@ -367,15 +367,13 @@ mod tests { #[test] fn three_non_exhaustive_accounts_all_refilled() { - // A seemingly irrational situation, this can happen when some of those - // originally qualified payables could get disqualified. Those would free some - // means that could be used for the other accounts. - // In the end, we have a final set with suboptimal balances, despite - // the unallocated cw balance is larger than the entire sum of the original balances - // for this few resulting accounts. - // We can pay every account fully, so, why did we need to call the PaymentAdjuster - // in first place? - // The detail is in the loss of some accounts, allowing to pay more for the others. + // A seemingly irrational situation, this can happen when some of those originally qualified + // payables could get disqualified. Those would free some means that could be used for + // the other accounts. In the end, we have a final set with suboptimal balances, despite + // the unallocated cw balance is larger than the entire sum of the original balances for + // this few resulting accounts. We can pay every account fully, so, why did we need to call + // the PaymentAdjuster in first place? The detail is in the loss of some accounts, allowing + // to pay more for the others. let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 45_000_000_000; let proposed_adjusted_balance_1 = 44_999_897_000; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0d19fa993..ab3c953cb 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -461,10 +461,11 @@ impl PaymentAdjusterError { .. } => true, PaymentAdjusterError::AllAccountsEliminated => true, - // So far, we don't have to worry much, but adding an error that doesn't mean at the same - // time that the insolvency is a thing might be just a matter of time. Then, it's important - // to check for those consequences (Hint: It is anticipated to affect the wording - // of error announcements back nearer the Accountant's own area) + // So far, we don't have to worry much, but adding an error, that doesn't imply at the + // same time that an insolvency was proved before it, might become relevant in + // the future. Then, it'll be important to check for those consequences (Hint: It is + // anticipated to affect the wording of error announcements that take place back nearer + // to the Accountant's general area) } } } @@ -647,7 +648,7 @@ mod tests { }), None, ); - // transaction fee balance > payments + // Transaction fee balance > payments let input_3 = make_input_for_initial_check_tests( None, Some(TestConfigForTransactionFees { @@ -661,7 +662,7 @@ mod tests { }, }), ); - // transaction fee balance == payments + // Transaction fee balance == payments let input_4 = make_input_for_initial_check_tests( None, Some(TestConfigForTransactionFees { @@ -1025,11 +1026,10 @@ mod tests { // contained before the test started. To prevent that, we used to secure a rule that // an account could never demand more than 100% of itself. // - // Later it was changed to other - // policy. so called "outweighed" account gains automatically a balance equal to its - // disqualification limit, also a prominent front position in the resulting set of - // the accounts to pay out. Additionally, due to its favorable position, it can be given - // a bit more from the remains still languishing in the consuming wallet. + // Later it was changed to other policy. so called "outweighed" account gains automatically + // a balance equal to its disqualification limit, also a prominent front position in + // the resulting set of the accounts to pay out. Additionally, due to its favorable position, + // it can be given a bit more from the remains still languishing in the consuming wallet. let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), @@ -1201,8 +1201,8 @@ mod tests { let test_name = "overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely"; let now = SystemTime::now(); - // Each of the 3 accounts refers to a debt sized as the entire masq token supply and being 10 years old which - // generates enormously large numbers in the criteria + // Each of the 3 accounts refers to a debt sized as the entire MASQ token supply and being + // 10 years old which generates enormously large numbers in the criteria let extreme_payables = { let debt_age_in_months = vec![120, 120, 120]; make_extreme_payables( diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index c4bd7c9cc..97e7427bb 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -1,8 +1,11 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +#![cfg(test)] + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; +use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::BalanceProvidingAccount; use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -10,11 +13,12 @@ use crate::accountant::payment_adjuster::{ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; -use crate::accountant::AnalyzedPayableAccount; +use crate::accountant::{gwei_to_wei, AnalyzedPayableAccount}; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; +use masq_lib::percentage::Percentage; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::convert_collection; use rand; @@ -27,7 +31,7 @@ use thousands::Separable; use web3::types::U256; #[test] -#[ignore] +//#[ignore] fn loading_test_with_randomized_params() { // This test needs to be understood as a generator of extensive amount of scenarios that // the PaymentAdjuster might come to be asked to resolve while there are quite many combinations @@ -43,9 +47,11 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 500; + let number_of_requested_scenarios = 5000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); - let test_overall_output_collector = TestOverallOutputCollector::default(); + let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); + let test_overall_output_collector = + TestOverallOutputCollector::new(invalidly_generated_scenarios); struct FirstStageOutput { test_overall_output_collector: TestOverallOutputCollector, @@ -147,14 +153,15 @@ fn try_making_single_valid_scenario( now: SystemTime, ) -> Option { let (cw_service_fee_balance, payables) = make_payables(gn, now); - let payables_len = payables.len(); let qualified_payables = try_making_guaranteed_qualified_payables( payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now, false, ); - if payables_len != qualified_payables.len() { + let required_service_fee_total: u128 = + sum_as(&qualified_payables, |account| account.balance_minor()); + if required_service_fee_total <= cw_service_fee_balance { return None; } let analyzed_accounts: Vec = convert_collection(qualified_payables); @@ -167,35 +174,80 @@ fn try_making_single_valid_scenario( )) } +fn make_payable_account( + idx: usize, + threshold_limit: u128, + guarantee_age: u64, + now: SystemTime, + gn: &mut ThreadRng, +) -> PayableAccount { + // Why is this construction so complicated? Well, I wanted to get the test showing partial + // adjustments where the final accounts can be paid enough but still not all up to their formerly + // claimed balance. It turned out it is very difficult to achieve that, I couldn't really come + // up with parameters that would certainly bring this condition. I ended up experimenting and + // looking for an algorithm that would make the parameters as random as possible because + // the generator alone is not much good at it. This isn't optimal either, but it allows to + // observe some of those partial adjustments, however, with a rate of maybe 0.5 % out of + // those all attempts to create a proper test scenario. + let wallet = make_wallet(&format!("wallet{}", idx)); + let mut generate_age_segment = || generate_non_zero_usize(gn, (guarantee_age / 2) as usize); + let debt_age = generate_age_segment() + generate_age_segment() + generate_age_segment(); + let service_fee_balance_minor = { + let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; + let parameter_a = generate_u128(); + let parameter_b = generate_u128(); + let parameter_c = generate_u128(); + let parameter_d = generate_u128(); + let mut use_variable_exponent = |parameter: u128, up_to: usize| { + parameter.pow(generate_non_zero_usize(gn, up_to) as u32) + }; + let a_b_c = use_variable_exponent(parameter_a, 3) + * use_variable_exponent(parameter_b, 4) + * use_variable_exponent(parameter_c, 5) + * parameter_d; + let addition = (0..6).fold(a_b_c, |so_far, subtrahend| { + if so_far != a_b_c { + so_far + } else { + if let Some(num) = + a_b_c.checked_sub(use_variable_exponent(parameter_c, 6 - subtrahend)) + { + num + } else { + so_far + } + } + }); + + threshold_limit + addition + }; + let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); + PayableAccount { + wallet, + balance_wei: service_fee_balance_minor, + last_paid_timestamp, + pending_payable_opt: None, + } +} + fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { - let accounts_count = generate_non_zero_usize(gn, 20) + 1; + let accounts_count = generate_non_zero_usize(gn, 25) + 1; + let threshold_limit = + gwei_to_wei::(PRESERVED_TEST_PAYMENT_THRESHOLDS.permanent_debt_allowed_gwei); + let guarantee_age = PRESERVED_TEST_PAYMENT_THRESHOLDS.maturity_threshold_sec + + PRESERVED_TEST_PAYMENT_THRESHOLDS.threshold_interval_sec; let accounts = (0..accounts_count) - .map(|idx| { - let wallet = make_wallet(&format!("wallet{}", idx)); - let debt_age = 2000 + generate_non_zero_usize(gn, 200000); - let service_fee_balance_minor = { - let mut generate_u128 = || -> u128 { gn.gen_range(1_000_000_000..2_000_000_000) }; - let parameter_a = generate_u128(); - let parameter_b = generate_u128(); - parameter_a * parameter_b - }; - let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); - PayableAccount { - wallet, - balance_wei: service_fee_balance_minor, - last_paid_timestamp, - pending_payable_opt: None, - } - }) + .map(|idx| make_payable_account(idx, threshold_limit, guarantee_age, now, gn)) .collect::>(); let balance_average = { let sum: u128 = sum_as(&accounts, |account| account.balance_wei); sum / accounts_count as u128 }; let cw_service_fee_balance_minor = { - let max_pieces = accounts_count * 6; + let multiplier = 1000; + let max_pieces = accounts_count * multiplier; let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; - balance_average / 6 * number_of_pieces + balance_average / multiplier as u128 * number_of_pieces }; (cw_service_fee_balance_minor, accounts) } @@ -212,6 +264,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) + .agreed_transaction_fee_margin_result(Percentage::new(15)) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { @@ -245,7 +298,7 @@ fn administrate_single_scenario_result( ((100 * used_absolute) / common.cw_service_fee_balance_minor) as u8 }; let adjusted_accounts = - interpretable_account_resolutions(account_infos, &mut adjusted_accounts); + interpretable_adjustment_results(account_infos, &mut adjusted_accounts); let (partially_sorted_interpretable_adjustments, were_no_accounts_eliminated) = sort_interpretable_adjustments(adjusted_accounts); Ok(SuccessfulAdjustment { @@ -265,14 +318,14 @@ fn administrate_single_scenario_result( ScenarioResult::new(reinterpreted_result) } -fn interpretable_account_resolutions( +fn interpretable_adjustment_results( account_infos: Vec, adjusted_accounts: &mut Vec, ) -> Vec { account_infos .into_iter() .map(|account_info| { - prepare_interpretable_account_resolution(account_info, adjusted_accounts) + prepare_interpretable_adjustment_result(account_info, adjusted_accounts) }) .collect() } @@ -342,11 +395,12 @@ fn render_results_to_file_and_attempt_basic_assertions( number_of_requested_scenarios, total_scenarios_evaluated, ); - + let total_scenarios_handled_including_invalid_ones = + total_scenarios_evaluated + test_overall_output_collector.invalidly_generated_scenarios; assert_eq!( - total_scenarios_evaluated, number_of_requested_scenarios, - "Evaluated scenarios count ({}) != requested scenarios count ({})", - total_scenarios_evaluated, number_of_requested_scenarios + total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios, + "All handled scenarios including those invalid ones ({}) != requested scenarios count ({})", + total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios ); // The next assertions depend heavily on the setup for the scenario generator!! // It rather indicates how well the setting is so that you can adjust it eventually, @@ -354,7 +408,7 @@ fn render_results_to_file_and_attempt_basic_assertions( let entry_check_pass_rate = 100 - ((test_overall_output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); - let required_pass_rate = 80; + let required_pass_rate = 70; assert!( entry_check_pass_rate >= required_pass_rate, "Not at least {}% from {} the scenarios \ @@ -409,7 +463,9 @@ fn write_brief_test_summary_into_file( Unsuccessful\n\ Caught by the entry check:............. {}\n\ With 'AllAccountsEliminated':.......... {}\n\ - With late insufficient balance errors:. {}", + With late insufficient balance errors:. {}\n\n\ + Legend\n\ + Partially adjusted accounts mark:...... {}", number_of_requested_scenarios, total_of_scenarios_evaluated, overall_output_collector.oks, @@ -428,7 +484,8 @@ fn write_brief_test_summary_into_file( .render_in_two_lines(), overall_output_collector.scenarios_denied_before_adjustment_started, overall_output_collector.all_accounts_eliminated, - overall_output_collector.insufficient_service_fee_balance + overall_output_collector.insufficient_service_fee_balance, + NON_EXHAUSTED_ACCOUNT_MARKER )) .unwrap() } @@ -537,11 +594,20 @@ fn single_account_output( .unwrap(); } +const NON_EXHAUSTED_ACCOUNT_MARKER: &str = "# # # # # # # #"; + fn resolve_account_ending_status_graphically( bill_coverage_in_percentage_opt: Option, ) -> String { match bill_coverage_in_percentage_opt { - Some(percentage) => format!("{} %", percentage), + Some(percentage) => { + let highlighting = if percentage != 100 { + NON_EXHAUSTED_ACCOUNT_MARKER + } else { + "" + }; + format!("{} %{:>shift$}", percentage, highlighting, shift = 40) + } None => "X".to_string(), } } @@ -598,7 +664,7 @@ fn write_ln_made_of(file: &mut File, char: char) { .unwrap(); } -fn prepare_interpretable_account_resolution( +fn prepare_interpretable_adjustment_result( account_info: AccountInfo, resulted_affordable_accounts: &mut Vec, ) -> InterpretableAdjustmentResult { @@ -608,11 +674,12 @@ fn prepare_interpretable_account_resolution( let bills_coverage_in_percentage_opt = match adjusted_account_idx_opt { Some(idx) => { let adjusted_account = resulted_affordable_accounts.remove(idx); - let bill_coverage_in_percentage = u8::try_from( - (adjusted_account.balance_wei * 100) - / account_info.initially_requested_service_fee_minor, - ) - .unwrap(); + assert_eq!(adjusted_account.wallet, account_info.wallet); + let bill_coverage_in_percentage = { + let percentage = (adjusted_account.balance_wei * 100) + / account_info.initially_requested_service_fee_minor; + u8::try_from(percentage).unwrap() + }; Some(bill_coverage_in_percentage) } None => None, @@ -635,13 +702,10 @@ fn sort_interpretable_adjustments( .partition(|adjustment| adjustment.bills_coverage_in_percentage_opt.is_some()); let were_no_accounts_eliminated = eliminated.is_empty(); let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp( - &result_b.bills_coverage_in_percentage_opt.unwrap(), - &result_a.bills_coverage_in_percentage_opt.unwrap(), - ) + Ord::cmp(&result_a.initial_balance, &result_b.initial_balance) }); let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp(&result_b.initial_balance, &result_a.initial_balance) + Ord::cmp(&result_a.initial_balance, &result_b.initial_balance) }); let all_results = finished_sorted.chain(eliminated_sorted).collect(); (all_results, were_no_accounts_eliminated) @@ -663,8 +727,8 @@ fn generate_boolean(gn: &mut ThreadRng) -> bool { gn.gen() } -#[derive(Default)] struct TestOverallOutputCollector { + invalidly_generated_scenarios: usize, // First stage: entry check // ____________________________________ scenarios_denied_before_adjustment_started: usize, @@ -679,6 +743,21 @@ struct TestOverallOutputCollector { insufficient_service_fee_balance: usize, } +impl TestOverallOutputCollector { + fn new(invalidly_generated_scenarios: usize) -> Self { + Self { + invalidly_generated_scenarios, + scenarios_denied_before_adjustment_started: 0, + oks: 0, + with_no_accounts_eliminated: 0, + fulfillment_distribution_for_transaction_fee_adjustments: Default::default(), + fulfillment_distribution_for_service_fee_adjustments: Default::default(), + all_accounts_eliminated: 0, + insufficient_service_fee_balance: 0, + } + } +} + #[derive(Default)] struct PercentageFulfillmentDistribution { collected_fulfillment_percentages: Vec, @@ -759,7 +838,6 @@ struct CommonScenarioInfo { cw_service_fee_balance_minor: u128, required_adjustment: Adjustment, } - struct InterpretableAdjustmentResult { initial_balance: u128, debt_age_s: u64, diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs index c771ecc98..fde0c6a7a 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs @@ -6,7 +6,6 @@ pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount where Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, { - // fn process_findings(insufficiency_found: bool)-> fn prepare_analyzable_account( self, disqualification_arbiter: &DisqualificationArbiter, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 194c8f238..f722e1911 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -129,8 +129,8 @@ where pub struct ScannerCommon { initiated_at_opt: Option, - // TODO The thresholds probably shouldn't be in common because - // the PendingPayableScanner does not need it + // TODO The thresholds probably shouldn't be in ScannerCommon because the PendingPayableScanner + // does not need it pub payment_thresholds: Rc, } @@ -2272,9 +2272,9 @@ mod tests { unban_below_gwei: 0, }; let wallet = make_wallet("abc"); - // It is important to have a payable matching the declining part of the thresholds, - // also it will be more believable if the slope is steep because then one second can make - // the bigger difference in the intercept value, which is the value this test compare to + // It is important to have a payable matching the declining part of the thresholds, also it + // will be more believable if the slope is steep because then one second can make the bigger + // difference in the intercept value, which is the value this test compare in order to // conclude a pass let debt_age = payment_thresholds.maturity_threshold_sec + (payment_thresholds.threshold_interval_sec / 2); diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 16c445362..a9ac80a1f 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -300,6 +300,7 @@ pub mod payable_scanner_utils { .payable_threshold_gauge .is_innocent_age(debt_age, payment_thresholds.maturity_threshold_sec) { + eprintln!("age not enough"); return None; } @@ -307,6 +308,7 @@ pub mod payable_scanner_utils { payable.balance_wei, gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei), ) { + eprintln!("balance not enough"); return None; } diff --git a/node/src/sub_lib/blockchain_bridge.rs b/node/src/sub_lib/blockchain_bridge.rs index 4ebaa0390..526bf557f 100644 --- a/node/src/sub_lib/blockchain_bridge.rs +++ b/node/src/sub_lib/blockchain_bridge.rs @@ -77,12 +77,11 @@ impl SkeletonOptHolder for OutboundPaymentsInstructions { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConsumingWalletBalances { - // The supply of this currency isn't limited by our database and - // theoretically can be much bigger than of our utility currency + // The supply of this currency isn't limited by our database and theoretically can be much + // bigger than of our utility currency pub transaction_fee_balance_in_minor_units: U256, - // This supply must fit in u128 because otherwise our database would - // not be fully capable of handling math with it not threatened by - // an overflow + // This supply must fit in u128 (maybe rather i128) because otherwise our database would not be + // fully capable of handling math over it while not threatened by a fatal overflow pub service_fee_balance_in_minor_units: u128, } diff --git a/node/src/sub_lib/utils.rs b/node/src/sub_lib/utils.rs index aab46e058..1b0cd6a25 100644 --- a/node/src/sub_lib/utils.rs +++ b/node/src/sub_lib/utils.rs @@ -251,10 +251,10 @@ pub struct MessageScheduler { pub delay: Duration, } -// Yes, this refers to code from the test tree, but we pull a trick by letting the guts vanish in -// the release mode, therefore we can use this outer macro without bad effects, and we also don't -// have to add the #[cfg(test)] marks above the invocation itself, but also the related 'use' -// clause. +// Yes, this refers to code from the test tree, but we play a trick by letting the guts vanish in +// the release version of our code, therefore we can use this outer macro without adverse effects, +// but above all we don't have to add the #[cfg(test)] marks above the invocation itself, but also +// the related 'use' clause. #[macro_export] macro_rules! arbitrary_id_stamp_in_trait { () => { From 2fb03422713a993705aad4148976b388a9cb0727 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 29 Apr 2024 15:28:28 +0200 Subject: [PATCH 185/250] GH-711: operating on the loading test. it's quite neat, could be better though --- .../payment_adjuster/adjustment_runners.rs | 18 ++-- .../account_stages_conversions.rs | 25 +++-- .../miscellaneous/data_structures.rs | 26 ++--- .../miscellaneous/helper_functions.rs | 24 +++-- node/src/accountant/payment_adjuster/mod.rs | 5 + .../payment_adjuster/non_unit_tests/mod.rs | 97 ++++++++++++++----- .../payment_adjuster/service_fee_adjuster.rs | 30 +++--- 7 files changed, 151 insertions(+), 74 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index e692ed2a2..a02a0cee2 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -141,14 +141,16 @@ mod tests { assert_eq!( result, vec![ - AdjustedAccountBeforeFinalization { - original_account: payable_1.analyzed_account.qualified_as.bare_account, - proposed_adjusted_balance_minor: expected_proposed_balance_1 - }, - AdjustedAccountBeforeFinalization { - original_account: payable_2.analyzed_account.qualified_as.bare_account, - proposed_adjusted_balance_minor: expected_proposed_balance_2 - } + AdjustedAccountBeforeFinalization::new( + payable_1.analyzed_account.qualified_as.bare_account, + payable_1.weight, + expected_proposed_balance_1 + ), + AdjustedAccountBeforeFinalization::new( + payable_2.analyzed_account.qualified_as.bare_account, + payable_2.weight, + expected_proposed_balance_2 + ) ] ) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 6143d50fd..ee27101b7 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -44,13 +44,18 @@ impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { let proposed_adjusted_balance_minor = unconfirmed_adjustment.proposed_adjusted_balance_minor; + let weight = unconfirmed_adjustment.weighted_account.weight; let original_account = unconfirmed_adjustment .weighted_account .analyzed_account .qualified_as .bare_account; - AdjustedAccountBeforeFinalization::new(original_account, proposed_adjusted_balance_minor) + AdjustedAccountBeforeFinalization::new( + original_account, + weight, + proposed_adjusted_balance_minor, + ) } } @@ -64,8 +69,9 @@ impl From for AdjustedAccountBeforeFinalization { &weighted_account, limited_adjusted_balance, ); + let weight = weighted_account.weight; let original_account = weighted_account.analyzed_account.qualified_as.bare_account; - AdjustedAccountBeforeFinalization::new(original_account, limited_adjusted_balance) + AdjustedAccountBeforeFinalization::new(original_account, weight, limited_adjusted_balance) } } @@ -84,8 +90,11 @@ mod tests { fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; - let non_finalized_account = - AdjustedAccountBeforeFinalization::new(original_payable_account.clone(), 123_456_789); + let non_finalized_account = AdjustedAccountBeforeFinalization::new( + original_payable_account.clone(), + 666777, + 123_456_789, + ); let result = PayableAccount::from(non_finalized_account); @@ -121,11 +130,12 @@ mod tests { weighted_account .analyzed_account .disqualification_limit_minor = 200_000_000; + weighted_account.weight = 78910; let result = AdjustedAccountBeforeFinalization::from(weighted_account); let expected_result = - AdjustedAccountBeforeFinalization::new(original_payable_account, 200_000_000); + AdjustedAccountBeforeFinalization::new(original_payable_account, 78910, 200_000_000); assert_eq!(result, expected_result) } @@ -133,13 +143,14 @@ mod tests { fn conversion_between_unconfirmed_adjustment_and_non_finalized_account() { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; - let weighted_account = prepare_weighted_account(original_payable_account.clone()); + let mut weighted_account = prepare_weighted_account(original_payable_account.clone()); + weighted_account.weight = 321654; let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); let expected_result = - AdjustedAccountBeforeFinalization::new(original_payable_account, 111_222_333); + AdjustedAccountBeforeFinalization::new(original_payable_account, 321654, 111_222_333); assert_eq!(result, expected_result) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index d19d38a5d..1ecb7fed6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -70,13 +70,19 @@ pub enum DecidedAccounts { #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { pub original_account: PayableAccount, + pub weight: u128, pub proposed_adjusted_balance_minor: u128, } impl AdjustedAccountBeforeFinalization { - pub fn new(original_account: PayableAccount, proposed_adjusted_balance_minor: u128) -> Self { + pub fn new( + original_account: PayableAccount, + weight: u128, + proposed_adjusted_balance_minor: u128, + ) -> Self { Self { original_account, + weight, proposed_adjusted_balance_minor, } } @@ -247,18 +253,12 @@ mod tests { #[test] fn merging_results_from_recursion_works() { - let non_finalized_account_1 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(111), - proposed_adjusted_balance_minor: 1234, - }; - let non_finalized_account_2 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(222), - proposed_adjusted_balance_minor: 5555, - }; - let non_finalized_account_3 = AdjustedAccountBeforeFinalization { - original_account: make_payable_account(333), - proposed_adjusted_balance_minor: 6789, - }; + let non_finalized_account_1 = + AdjustedAccountBeforeFinalization::new(make_payable_account(111), 12345, 1234); + let non_finalized_account_2 = + AdjustedAccountBeforeFinalization::new(make_payable_account(222), 543, 5555); + let non_finalized_account_3 = + AdjustedAccountBeforeFinalization::new(make_payable_account(333), 789987, 6789); let subject = RecursionResults { here_decided_accounts: vec![non_finalized_account_1.clone()], downstream_decided_accounts: vec![ diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 9cd22bdb0..b8a3d4c27 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -114,12 +114,7 @@ pub fn exhaust_cw_balance_entirely( let init = ConsumingWalletExhaustingStatus::new(cw_reminder); approved_accounts .into_iter() - .sorted_by(|info_a, info_b| { - Ord::cmp( - &info_a.proposed_adjusted_balance_minor, - &info_b.proposed_adjusted_balance_minor, - ) - }) + .sorted_by(|info_a, info_b| Ord::cmp(&info_b.weight, &info_a.weight)) .fold( init, run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances, @@ -222,7 +217,7 @@ mod tests { #[test] fn zero_affordable_accounts_found_returns_false_for_non_finalized_accounts() { let result = zero_affordable_accounts_found(&Either::Left(vec![ - AdjustedAccountBeforeFinalization::new(make_payable_account(456), 1234), + AdjustedAccountBeforeFinalization::new(make_payable_account(456), 5678, 1234), ])); assert_eq!(result, false) @@ -321,6 +316,7 @@ mod tests { fn make_non_finalized_adjusted_account( wallet: &Wallet, original_balance: u128, + weight: u128, proposed_adjusted_balance: u128, ) -> AdjustedAccountBeforeFinalization { let garbage_last_paid_timestamp = SystemTime::now(); @@ -330,7 +326,7 @@ mod tests { last_paid_timestamp: garbage_last_paid_timestamp, pending_payable_opt: None, }; - AdjustedAccountBeforeFinalization::new(payable_account, proposed_adjusted_balance) + AdjustedAccountBeforeFinalization::new(payable_account, weight, proposed_adjusted_balance) } fn assert_payable_accounts_after_adjustment_finalization( @@ -377,12 +373,15 @@ mod tests { let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 45_000_000_000; let proposed_adjusted_balance_1 = 44_999_897_000; + let weight_1 = 2_000_000_000; let wallet_2 = make_wallet("def"); let original_requested_balance_2 = 33_500_000_000; let proposed_adjusted_balance_2 = 33_487_999_999; + let weight_2 = 6_000_000_000; let wallet_3 = make_wallet("ghi"); let original_requested_balance_3 = 41_000_000; let proposed_adjusted_balance_3 = 40_980_000; + let weight_3 = 20_000_000_000; let original_cw_balance = original_requested_balance_1 + original_requested_balance_2 + original_requested_balance_3 @@ -391,16 +390,19 @@ mod tests { make_non_finalized_adjusted_account( &wallet_1, original_requested_balance_1, + weight_1, proposed_adjusted_balance_1, ), make_non_finalized_adjusted_account( &wallet_2, original_requested_balance_2, + weight_2, proposed_adjusted_balance_2, ), make_non_finalized_adjusted_account( &wallet_3, original_requested_balance_3, + weight_3, proposed_adjusted_balance_3, ), ]; @@ -422,12 +424,15 @@ mod tests { let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 41_000_000; let proposed_adjusted_balance_1 = 39_700_000; + let weight_1 = 38_000_000_000; let wallet_2 = make_wallet("def"); let original_requested_balance_2 = 33_500_000_000; let proposed_adjusted_balance_2 = 32_487_999_999; + let weight_2 = 25_000_000_000; let wallet_3 = make_wallet("ghi"); let original_requested_balance_3 = 50_000_000_000; let proposed_adjusted_balance_3 = 43_000_000_000; + let weight_3 = 38_000_000; let original_cw_balance = original_requested_balance_1 + proposed_adjusted_balance_2 + proposed_adjusted_balance_3 @@ -436,16 +441,19 @@ mod tests { make_non_finalized_adjusted_account( &wallet_1, original_requested_balance_1, + weight_1, proposed_adjusted_balance_1, ), make_non_finalized_adjusted_account( &wallet_2, original_requested_balance_2, + weight_2, proposed_adjusted_balance_2, ), make_non_finalized_adjusted_account( &wallet_3, original_requested_balance_3, + weight_3, proposed_adjusted_balance_3, ), ]; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index ab3c953cb..03a2851e3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -60,6 +60,11 @@ use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; +// PaymentAdjuster is a very efficient recursive and scalable algorithm that inspects payments under +// the condition of an acute insolvency. You can expand the scope of the evaluation by writing your +// own CriterionCalculator, that should be specialized on a distinct parameter of a payable account, +// and sticking it inside the vector that stores them. + pub type AdjustmentAnalysisResult = Result, AdjustmentAnalysis>, PaymentAdjusterError>; diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 97e7427bb..6e5ae416d 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -33,17 +33,55 @@ use web3::types::U256; #[test] //#[ignore] fn loading_test_with_randomized_params() { - // This test needs to be understood as a generator of extensive amount of scenarios that - // the PaymentAdjuster might come to be asked to resolve while there are quite many combinations - // that a human has a hard time with to imagine, now we ought to think that some of them might - // be corner cases that there wasn't awareness of when it was being designed. Therefore, the main - // purpose of this test is to prove that out of a huge number of tries the PaymentAdjuster always - // comes along fairly well, especially, that it cannot kill the Node by an accidental panic or - // that it can live up to its original purpose and the vast majority of the attempted adjustments - // end up with reasonable results. That said, a smaller amount of these attempts are expected - // to be vain because of some chance always be there that with a given combination of payables - // the algorithm will go step by step eliminating completely all accounts. There's hardly a way - // for the adjustment procedure as it proceeds now to anticipate if this is going to happen. + // This test needs to be understood as a fuzz test, a generator of possibly an overwhelming + // amount of scenarios that the PaymentAdjuster might happen to be asked to sort them out while + // there might be many and many combinations that a human would be having a hard time imagining; + // now we ought to ponder that some of them might be corner cases of which there was no + // awareness when it was being designed. So, the main purpose of this test is to prove that even + // a huge number of goes with as-much-as-possible variable inputs will not shoot + // the PaymentAdjuster down and potentially the whole Node with it; on contrary, it should + // always give some reasonable results and live up to its original purpose with account + // adjustments. That said, a smaller amount of these attempts are expected to end up vain, + // legitimately, though, because of so defined error cases. + + // When the test finishes, it writes some key figures of each pseudo-randomly invented scenario, + // and a summary of the whole test set with some useful statistics that can help to evaluate + // the behavior to better understand this feature in the Node or consider some enhancement to be + // implemented. + + // For somebody not so familiar with the algorithms there might emerge results (they tend to be + // rare but absolutely valid and wanted - the more their observation can be interesting) that + // can cause some confusion. + + // The scenario generator is designed to provide as random and as wide variety of situations as + // possible, but it has plenty of limitations anyway. This is an example of rather un unclear + // result, where the statistical numbers might seem to have no reason and feel like something is + // broken. Let's make a hint about these percents of the initial debt coverage, that strike by + // their perhaps surprising lack of proportionality with that trend of their ascending balances. + // It cannot be that intuitive, though, because the final balance depends heavily on + // a disqualification limit. Which usually allows not to pay the entire balance but only such + // portion that will interestedly suffice for us to stay unbanned. For a huge account, + // the forgiven part is a little fraction of a whole. Small accounts, on the other hand, if it + // can be applied (and not have to be disqualified), might be missing a big portion of + // themselves. This is what the numbers 63% and 94% illustrates, despite the former account + // comes across as it should take precedence and gain at the expanse of the latter. + + // CW service fee balance: 35,592,800,367,641,272 wei + // Portion of CW balance used: 100% + // Maximal txt count due to CW txt fee balance: Unlimited + // ____________________________________________________________________________________________ + // 1,000,494,243,602,844 wei | 567,037 s | 100 % + // 1,505,465,842,696,120 wei | 540,217 s | 100 % + // 1,626,173,874,954,904 wei | 349,872 s | 100 % + // 20,688,283,645,189,616 wei | 472,661 s | 99 % # # # # # # # # + // 4,735,196,705,072,789 wei | 399,335 s | 96 % # # # # # # # # + // 6,770,347,763,782,591 wei | 245,857 s | 92 % # # # # # # # # + + // TODO In the future, consider variable PaymentThresholds. Up today, all accounts are inspected + // based on the same ones and therefore the disqualification limit is rigid and doesn't allow us + // to observe largely different scenarios or even mixed ones with something yet not really + // available: Nodes with arbitrary PaymentThresholds settings + let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); @@ -65,10 +103,8 @@ fn loading_test_with_randomized_params() { let first_stage_output = scenarios .into_iter() .fold(init, |mut output_collector, scenario| { - // We watch only the service fee balance check, transaction fee can be added, but it - // doesn't interact with the potential error 'AllAccountsEliminated' whose occurrence - // rate is interesting compared to how many times the initial check lets the adjustment - // procedure go on + // We care only about the service fee balance check, parameters for transaction fee can + // be worked into the scenarios later. let qualified_payables = scenario .adjustment_analysis .accounts @@ -182,13 +218,13 @@ fn make_payable_account( gn: &mut ThreadRng, ) -> PayableAccount { // Why is this construction so complicated? Well, I wanted to get the test showing partial - // adjustments where the final accounts can be paid enough but still not all up to their formerly - // claimed balance. It turned out it is very difficult to achieve that, I couldn't really come - // up with parameters that would certainly bring this condition. I ended up experimenting and - // looking for an algorithm that would make the parameters as random as possible because - // the generator alone is not much good at it. This isn't optimal either, but it allows to - // observe some of those partial adjustments, however, with a rate of maybe 0.5 % out of - // those all attempts to create a proper test scenario. + // adjustments where the final accounts can be paid enough but still not all up to their + // formerly claimed balance. It turned out it is very difficult to achieve that, I couldn't + // really come up with parameters that would certainly bring this condition. I ended up + // experimenting and looking for an algorithm that would make the parameters as random as + // possible because the generator alone is not much good at it. This isn't optimal either, + // but it allows to observe some of those partial adjustments, however, with a rate of maybe + // 0.5 % out of those all attempts to create a proper test scenario. let wallet = make_wallet(&format!("wallet{}", idx)); let mut generate_age_segment = || generate_non_zero_usize(gn, (guarantee_age / 2) as usize); let debt_age = generate_age_segment() + generate_age_segment() + generate_age_segment(); @@ -402,9 +438,9 @@ fn render_results_to_file_and_attempt_basic_assertions( "All handled scenarios including those invalid ones ({}) != requested scenarios count ({})", total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios ); - // The next assertions depend heavily on the setup for the scenario generator!! - // It rather indicates how well the setting is so that you can adjust it eventually, - // to see more relevant results + // The next assertions depend heavily on the setup for the scenario generator!! It rather + // indicates how well the setting is so that you can adjust it eventually, to see more relevant + // results let entry_check_pass_rate = 100 - ((test_overall_output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); @@ -702,7 +738,16 @@ fn sort_interpretable_adjustments( .partition(|adjustment| adjustment.bills_coverage_in_percentage_opt.is_some()); let were_no_accounts_eliminated = eliminated.is_empty(); let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp(&result_a.initial_balance, &result_b.initial_balance) + Ord::cmp( + &( + result_b.bills_coverage_in_percentage_opt, + result_a.initial_balance, + ), + &( + result_a.bills_coverage_in_percentage_opt, + result_b.initial_balance, + ), + ) }); let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { Ord::cmp(&result_a.initial_balance, &result_b.initial_balance) diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 26ce42b19..5954a224d 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -231,6 +231,7 @@ mod tests { #[test] fn filter_and_process_winners_limits_them_by_their_disqualification_edges() { let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); + let weight_1 = account_1.weighted_account.weight; account_1 .weighted_account .analyzed_account @@ -243,6 +244,7 @@ mod tests { .disqualification_limit_minor = multiple_by_billion(1_800_000_000); account_1.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); + let weight_2 = account_2.weighted_account.weight; account_2 .weighted_account .analyzed_account @@ -267,6 +269,7 @@ mod tests { .disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; account_3.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); + let weight_4 = account_4.weighted_account.weight; account_4 .weighted_account .analyzed_account @@ -303,30 +306,33 @@ mod tests { assert_eq!(losing_competitors, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ - AdjustedAccountBeforeFinalization { - original_account: account_1 + AdjustedAccountBeforeFinalization::new( + account_1 .weighted_account .analyzed_account .qualified_as .bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(1_800_000_000), - }, - AdjustedAccountBeforeFinalization { - original_account: account_2 + weight_1, + multiple_by_billion(1_800_000_000), + ), + AdjustedAccountBeforeFinalization::new( + account_2 .weighted_account .analyzed_account .qualified_as .bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(4_200_000_000) - 1, - }, - AdjustedAccountBeforeFinalization { - original_account: account_4 + weight_2, + multiple_by_billion(4_200_000_000) - 1, + ), + AdjustedAccountBeforeFinalization::new( + account_4 .weighted_account .analyzed_account .qualified_as .bare_account, - proposed_adjusted_balance_minor: multiple_by_billion(500_000_000), - }, + weight_4, + multiple_by_billion(500_000_000), + ), ]; assert_eq!(thriving_competitors, expected_adjusted_outweighed_accounts) } From fba4c836c19a6a6ee39bd4482a98871401227405 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 1 May 2024 00:02:54 +0200 Subject: [PATCH 186/250] GH-711: more profi simulations --- .../payment_adjuster/non_unit_tests/mod.rs | 568 ++++++++++++++---- node/src/accountant/test_utils.rs | 70 ++- 2 files changed, 498 insertions(+), 140 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 6e5ae416d..61a5e9c7c 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -12,8 +12,14 @@ use crate::accountant::payment_adjuster::{ }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; -use crate::accountant::test_utils::try_making_guaranteed_qualified_payables; -use crate::accountant::{gwei_to_wei, AnalyzedPayableAccount}; +use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ + PayableInspector, PayableThresholdsGaugeReal, +}; +use crate::accountant::test_utils::{ + make_single_qualified_payable_opt, try_making_guaranteed_qualified_payables, +}; +use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -22,8 +28,10 @@ use masq_lib::percentage::Percentage; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::convert_collection; use rand; +use rand::distributions::uniform::SampleUniform; use rand::rngs::ThreadRng; use rand::{thread_rng, Rng}; +use std::collections::HashMap; use std::fs::File; use std::io::Write; use std::time::SystemTime; @@ -49,19 +57,20 @@ fn loading_test_with_randomized_params() { // the behavior to better understand this feature in the Node or consider some enhancement to be // implemented. - // For somebody not so familiar with the algorithms there might emerge results (they tend to be - // rare but absolutely valid and wanted - the more their observation can be interesting) that - // can cause some confusion. - - // The scenario generator is designed to provide as random and as wide variety of situations as - // possible, but it has plenty of limitations anyway. This is an example of rather un unclear - // result, where the statistical numbers might seem to have no reason and feel like something is - // broken. Let's make a hint about these percents of the initial debt coverage, that strike by - // their perhaps surprising lack of proportionality with that trend of their ascending balances. - // It cannot be that intuitive, though, because the final balance depends heavily on - // a disqualification limit. Which usually allows not to pay the entire balance but only such - // portion that will interestedly suffice for us to stay unbanned. For a huge account, - // the forgiven part is a little fraction of a whole. Small accounts, on the other hand, if it + // For somebody not so familiar with the used algorithms, there might emerge results (they tend + // to be rare but absolutely valid and wanted - the more their observation can be interesting) + // that can bring one into confusion. + + // The scenario generator is designed to provide as random and wide variety of situations as + // possible, but it has plenty of limitations despite. The following is an example of tricky- + // -to-understand scenario output, where the statistics, the percents, might seem to have no + // reason and feel like something could be broken. Let's make a hint. These percents represent + // the initial debt coverage and in this case they might strike by their perhaps surprisingly + // inadequate proportionality with the trend that can be seen next to them, in the ascending + // balances. It needn't be that intuitive, though, because the final adjustment depends heavily + // on a so-called disqualification limit. That usually allows not to pay the entire balance but + // only such portion that will inherently suffice for us to stay unbanned. For a huge account, + // that forgiven part makes just a little fraction of a whole. Small accounts, however, if it // can be applied (and not have to be disqualified), might be missing a big portion of // themselves. This is what the numbers 63% and 94% illustrates, despite the former account // comes across as it should take precedence and gain at the expanse of the latter. @@ -77,15 +86,10 @@ fn loading_test_with_randomized_params() { // 4,735,196,705,072,789 wei | 399,335 s | 96 % # # # # # # # # // 6,770,347,763,782,591 wei | 245,857 s | 92 % # # # # # # # # - // TODO In the future, consider variable PaymentThresholds. Up today, all accounts are inspected - // based on the same ones and therefore the disqualification limit is rigid and doesn't allow us - // to observe largely different scenarios or even mixed ones with something yet not really - // available: Nodes with arbitrary PaymentThresholds settings - let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 5000; + let number_of_requested_scenarios = 1000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); let test_overall_output_collector = @@ -93,7 +97,7 @@ fn loading_test_with_randomized_params() { struct FirstStageOutput { test_overall_output_collector: TestOverallOutputCollector, - allowed_scenarios: Vec, + allowed_scenarios: Vec, } let init = FirstStageOutput { @@ -106,13 +110,16 @@ fn loading_test_with_randomized_params() { // We care only about the service fee balance check, parameters for transaction fee can // be worked into the scenarios later. let qualified_payables = scenario + .prepared_adjustment .adjustment_analysis .accounts .iter() .map(|account| account.qualified_as.clone()) .collect(); - let initial_check_result = - subject.search_for_indispensable_adjustment(qualified_payables, &*scenario.agent); + let initial_check_result = subject.search_for_indispensable_adjustment( + qualified_payables, + &*scenario.prepared_adjustment.agent, + ); let allowed_scenario_opt = match initial_check_result { Ok(check_factual_output) => { match check_factual_output { @@ -149,7 +156,8 @@ fn loading_test_with_randomized_params() { let test_overall_output_collector = first_stage_output.test_overall_output_collector; let scenario_adjustment_results = second_stage_scenarios .into_iter() - .map(|prepared_adjustment| { + .map(|scenario| { + let prepared_adjustment = scenario.prepared_adjustment; let account_infos = preserve_account_infos(&prepared_adjustment.adjustment_analysis.accounts, now); let required_adjustment = prepared_adjustment.adjustment_analysis.adjustment.clone(); @@ -161,6 +169,7 @@ fn loading_test_with_randomized_params() { administrate_single_scenario_result( payment_adjuster_result, account_infos, + scenario.used_thresholds, required_adjustment, cw_service_fee_balance_minor, ) @@ -178,7 +187,7 @@ fn generate_scenarios( gn: &mut ThreadRng, now: SystemTime, number_of_scenarios: usize, -) -> Vec { +) -> Vec { (0..number_of_scenarios) .flat_map(|_| try_making_single_valid_scenario(gn, now)) .collect() @@ -187,33 +196,37 @@ fn generate_scenarios( fn try_making_single_valid_scenario( gn: &mut ThreadRng, now: SystemTime, -) -> Option { - let (cw_service_fee_balance, payables) = make_payables(gn, now); - let qualified_payables = try_making_guaranteed_qualified_payables( - payables, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - false, - ); - let required_service_fee_total: u128 = - sum_as(&qualified_payables, |account| account.balance_minor()); - if required_service_fee_total <= cw_service_fee_balance { - return None; - } +) -> Option { + let accounts_count = generate_non_zero_usize(gn, 25) + 1; + let thresholds_to_be_used = choose_thresholds(gn, accounts_count); + let (cw_service_fee_balance, qualified_payables, wallet_and_thresholds_pairs) = + try_generating_qualified_payables_and_cw_balance( + gn, + &thresholds_to_be_used, + accounts_count, + now, + )?; + let used_thresholds = + thresholds_to_be_used.fix_individual_thresholds_if_needed(wallet_and_thresholds_pairs); let analyzed_accounts: Vec = convert_collection(qualified_payables); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, analyzed_accounts.len()); - Some(PreparedAdjustment::new( + let prepared_adjustment = PreparedAdjustment::new( Box::new(agent), None, AdjustmentAnalysis::new(adjustment, analyzed_accounts), - )) + ); + Some(PreparedAdjustmentAndThresholds { + prepared_adjustment, + used_thresholds, + }) } fn make_payable_account( idx: usize, - threshold_limit: u128, - guarantee_age: u64, + thresholds: &PaymentThresholds, + // threshold_limit: u128, + // guarantee_age: u64, now: SystemTime, gn: &mut ThreadRng, ) -> PayableAccount { @@ -226,8 +239,13 @@ fn make_payable_account( // but it allows to observe some of those partial adjustments, however, with a rate of maybe // 0.5 % out of those all attempts to create a proper test scenario. let wallet = make_wallet(&format!("wallet{}", idx)); - let mut generate_age_segment = || generate_non_zero_usize(gn, (guarantee_age / 2) as usize); - let debt_age = generate_age_segment() + generate_age_segment() + generate_age_segment(); + let mut generate_age_segment = || { + generate_non_zero_usize( + gn, + (thresholds.maturity_threshold_sec + thresholds.threshold_interval_sec) as usize, + ) / 2 + }; + let debt_age = generate_age_segment() + generate_age_segment(); let service_fee_balance_minor = { let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; let parameter_a = generate_u128(); @@ -255,7 +273,7 @@ fn make_payable_account( } }); - threshold_limit + addition + thresholds.permanent_debt_allowed_gwei as u128 + addition }; let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); PayableAccount { @@ -266,17 +284,28 @@ fn make_payable_account( } } -fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec) { - let accounts_count = generate_non_zero_usize(gn, 25) + 1; - let threshold_limit = - gwei_to_wei::(PRESERVED_TEST_PAYMENT_THRESHOLDS.permanent_debt_allowed_gwei); - let guarantee_age = PRESERVED_TEST_PAYMENT_THRESHOLDS.maturity_threshold_sec - + PRESERVED_TEST_PAYMENT_THRESHOLDS.threshold_interval_sec; - let accounts = (0..accounts_count) - .map(|idx| make_payable_account(idx, threshold_limit, guarantee_age, now, gn)) - .collect::>(); +fn try_generating_qualified_payables_and_cw_balance( + gn: &mut ThreadRng, + thresholds_to_be_used: &AppliedThresholds, + accounts_count: usize, + now: SystemTime, +) -> Option<( + u128, + Vec, + Vec<(Wallet, PaymentThresholds)>, +)> { + let payables = make_payables_according_to_thresholds_setup( + gn, + &thresholds_to_be_used, + accounts_count, + now, + ); + + let (qualified_payables, wallet_and_thresholds_pairs) = + try_make_qualified_payables_by_applied_thresholds(payables, &thresholds_to_be_used, now); + let balance_average = { - let sum: u128 = sum_as(&accounts, |account| account.balance_wei); + let sum: u128 = sum_as(&qualified_payables, |account| account.balance_minor()); sum / accounts_count as u128 }; let cw_service_fee_balance_minor = { @@ -285,7 +314,111 @@ fn make_payables(gn: &mut ThreadRng, now: SystemTime) -> (u128, Vec Vec { + match thresholds_to_be_used { + AppliedThresholds::Defaulted => make_payables_with_common_thresholds( + gn, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + accounts_count, + now, + ), + AppliedThresholds::SingleButRandomized { common_thresholds } => { + make_payables_with_common_thresholds(gn, common_thresholds, accounts_count, now) + } + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } => { + let vec_of_thresholds = individual_thresholds + .thresholds + .as_ref() + .left() + .expect("should be Vec at this stage"); + assert_eq!(vec_of_thresholds.len(), accounts_count); + make_payables_with_individual_thresholds(gn, vec_of_thresholds, now) + } + } +} + +fn make_payables_with_common_thresholds( + gn: &mut ThreadRng, + common_thresholds: &PaymentThresholds, + accounts_count: usize, + now: SystemTime, +) -> Vec { + (0..accounts_count) + .map(|idx| make_payable_account(idx, common_thresholds, now, gn)) + .collect::>() +} + +fn make_payables_with_individual_thresholds( + gn: &mut ThreadRng, + individual_thresholds: &[PaymentThresholds], + now: SystemTime, +) -> Vec { + individual_thresholds + .iter() + .enumerate() + .map(|(idx, thresholds)| make_payable_account(idx, thresholds, now, gn)) + .collect() +} + +fn choose_thresholds(gn: &mut ThreadRng, accounts_count: usize) -> AppliedThresholds { + let be_defaulted = generate_boolean(gn); + if be_defaulted { + AppliedThresholds::Defaulted + } else { + let be_common_for_all = generate_boolean(gn); + if be_common_for_all { + AppliedThresholds::SingleButRandomized { + common_thresholds: return_single_randomized_thresholds(gn), + } + } else { + let thresholds_set = (0..accounts_count) + .map(|_| return_single_randomized_thresholds(gn)) + .collect(); + let individual_thresholds = IndividualThresholds { + thresholds: Either::Left(thresholds_set), + }; + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } + } + } +} + +fn return_single_randomized_thresholds(gn: &mut ThreadRng) -> PaymentThresholds { + let permanent_debt_allowed_gwei = generate_range(gn, 100, 1_000_000_000); + let debt_threshold_gwei = + permanent_debt_allowed_gwei + generate_range(gn, 10_000, 10_000_000_000); + let maturity_threshold_sec = generate_range(gn, 100, 10_000); + let threshold_interval_sec = generate_range(gn, 1000, 100_000); + let unban_below_gwei = permanent_debt_allowed_gwei; + PaymentThresholds { + debt_threshold_gwei, + maturity_threshold_sec, + payment_grace_period_sec: 0, + permanent_debt_allowed_gwei, + threshold_interval_sec, + unban_below_gwei, + } } fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { @@ -319,12 +452,14 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { fn administrate_single_scenario_result( payment_adjuster_result: Result, account_infos: Vec, + used_thresholds: AppliedThresholds, required_adjustment: Adjustment, cw_service_fee_balance_minor: u128, ) -> ScenarioResult { let common = CommonScenarioInfo { cw_service_fee_balance_minor, required_adjustment, + used_thresholds, }; let reinterpreted_result = match payment_adjuster_result { Ok(outbound_payment_instructions) => { @@ -447,7 +582,7 @@ fn render_results_to_file_and_attempt_basic_assertions( let required_pass_rate = 70; assert!( entry_check_pass_rate >= required_pass_rate, - "Not at least {}% from {} the scenarios \ + "Not at least {}% from those {} scenarios \ generated for this test allows PaymentAdjuster to continue doing its job and ends too early. \ Instead only {}%. Setup of the test might be needed", required_pass_rate, @@ -575,57 +710,143 @@ fn do_final_processing_of_single_scenario( fn render_scenario_header( file: &mut File, - cw_service_fee_balance_minor: u128, + scenario_common: &CommonScenarioInfo, portion_of_cw_used_percents: u8, - required_adjustment: Adjustment, ) { + write_thick_dividing_line(file); file.write_fmt(format_args!( "CW service fee balance: {} wei\n\ Portion of CW balance used: {}%\n\ - Maximal txt count due to CW txt fee balance: {}\n", - cw_service_fee_balance_minor.separate_with_commas(), + Maximal txt count due to CW txt fee balance: {}\n\ + Used PaymentThresholds:\n", + scenario_common + .cw_service_fee_balance_minor + .separate_with_commas(), portion_of_cw_used_percents, - resolve_affordable_transaction_count(required_adjustment) + resolve_affordable_transaction_count(&scenario_common.required_adjustment) )) .unwrap(); + let _ = match &scenario_common.used_thresholds { + AppliedThresholds::Defaulted | AppliedThresholds::SingleButRandomized { .. } => { + if let AppliedThresholds::SingleButRandomized { common_thresholds } = + &scenario_common.used_thresholds + { + write_fully_described_thresholds(file, common_thresholds); + } else { + file.write(b"Defaulted\n").unwrap(); + } + } + AppliedThresholds::RandomizedForEachAccount { .. } => { + file.write(b"Individual for each account\n").unwrap(); + } + }; } + +fn write_fully_described_thresholds( + file: &mut File, + randomized_thresholds: &PaymentThresholds, +) -> usize { + file.write( + format!("{:?}\n", randomized_thresholds) + .replace(",", ",\n") + .as_bytes(), + ) + .unwrap() +} + fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { - write_thick_dividing_line(file); render_scenario_header( file, - result.common.cw_service_fee_balance_minor, + &result.common, result.portion_of_cw_cumulatively_used_percents, - result.common.required_adjustment, ); write_thin_dividing_line(file); + let adjusted_accounts = result.partially_sorted_interpretable_adjustments; - adjusted_accounts.into_iter().for_each(|account| { - single_account_output( - file, - account.initial_balance, - account.debt_age_s, - account.bills_coverage_in_percentage_opt, - ) - }) + + render_accounts( + file, + &adjusted_accounts, + &result.common.used_thresholds, + |file, account, individual_thresholds_opt| { + single_account_output( + file, + account.info.initially_requested_service_fee_minor, + account.info.debt_age_s, + individual_thresholds_opt, + account.bills_coverage_in_percentage_opt, + ) + }, + ) +} + +fn render_accounts( + file: &mut File, + accounts: &[A], + used_thresholds: &AppliedThresholds, + mut render_account: F, +) where + A: AccountWithWallet, + F: FnMut(&mut File, &A, Option<&PaymentThresholds>), +{ + let set_of_individual_thresholds_opt = if let AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } = used_thresholds + { + Some(individual_thresholds.thresholds.as_ref().right().unwrap()) + } else { + None + }; + accounts + .iter() + .map(|account| { + ( + account, + set_of_individual_thresholds_opt.map(|thresholds| { + thresholds + .get(&account.wallet()) + .expect("Original thresholds missing") + }), + ) + }) + .for_each(|(account, individual_thresholds_opt)| { + render_account(file, account, individual_thresholds_opt) + }); + file.write(b"\n").unwrap(); +} + +trait AccountWithWallet { + fn wallet(&self) -> &Wallet; } -const BALANCE_COLUMN_WIDTH: usize = 30; -const AGE_COLUMN_WIDTH: usize = 7; +const FIRST_COLUMN_WIDTH: usize = 50; +const AGE_COLUMN_WIDTH: usize = 8; + +const STARTING_GAP: usize = 6; fn single_account_output( file: &mut File, balance_minor: u128, age_s: u64, + individual_thresholds_opt: Option<&PaymentThresholds>, bill_coverage_in_percentage_opt: Option, ) { + let first_column_width = FIRST_COLUMN_WIDTH; + let age_width = AGE_COLUMN_WIDTH; + let starting_gap = STARTING_GAP; let _ = file .write_fmt(format_args!( - "{:>balance_width$} wei | {:>age_width$} s | {}\n", + "{:first_column_width$} wei | {:>age_width$} s | {}\n", + "", + individual_thresholds_opt + .map(|thresholds| format!( + "{:first_column_width$}\n", + "", thresholds + )) + .unwrap_or("".to_string()), balance_minor.separate_with_commas(), age_s.separate_with_commas(), resolve_account_ending_status_graphically(bill_coverage_in_percentage_opt), - balance_width = BALANCE_COLUMN_WIDTH, - age_width = AGE_COLUMN_WIDTH )) .unwrap(); } @@ -649,23 +870,23 @@ fn resolve_account_ending_status_graphically( } fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) { - write_thick_dividing_line(file); - render_scenario_header( + render_scenario_header(file, &negative_result.common, 0); + write_thin_dividing_line(file); + render_accounts( file, - negative_result.common.cw_service_fee_balance_minor, - 0, - negative_result.common.required_adjustment, + &negative_result.account_infos, + &negative_result.common.used_thresholds, + |file, account, individual_thresholds_opt| { + single_account_output( + file, + account.initially_requested_service_fee_minor, + account.debt_age_s, + individual_thresholds_opt, + None, + ) + }, ); write_thin_dividing_line(file); - negative_result.account_infos.iter().for_each(|account| { - single_account_output( - file, - account.initially_requested_service_fee_minor, - account.debt_age_s, - None, - ) - }); - write_thin_dividing_line(file); write_error(file, negative_result.adjuster_error) } @@ -677,7 +898,7 @@ fn write_error(file: &mut File, error: PaymentAdjusterError) { .unwrap() } -fn resolve_affordable_transaction_count(adjustment: Adjustment) -> String { +fn resolve_affordable_transaction_count(adjustment: &Adjustment) -> String { match adjustment { Adjustment::ByServiceFee => "Unlimited".to_string(), Adjustment::TransactionFeeInPriority { @@ -686,17 +907,19 @@ fn resolve_affordable_transaction_count(adjustment: Adjustment) -> String { } } -fn write_thick_dividing_line(file: &mut File) { +fn write_thick_dividing_line(file: &mut dyn Write) { write_ln_made_of(file, '=') } -fn write_thin_dividing_line(file: &mut File) { +fn write_thin_dividing_line(file: &mut dyn Write) { write_ln_made_of(file, '_') } -fn write_ln_made_of(file: &mut File, char: char) { +const PAGE_WIDTH: usize = 120; + +fn write_ln_made_of(file: &mut dyn Write, char: char) { let _ = file - .write_fmt(format_args!("{}\n", char.to_string().repeat(100))) + .write_fmt(format_args!("{}\n", char.to_string().repeat(PAGE_WIDTH))) .unwrap(); } @@ -721,8 +944,13 @@ fn prepare_interpretable_adjustment_result( None => None, }; InterpretableAdjustmentResult { - initial_balance: account_info.initially_requested_service_fee_minor, - debt_age_s: account_info.debt_age_s, + info: AccountInfo { + wallet: account_info.wallet, + debt_age_s: account_info.debt_age_s, + initially_requested_service_fee_minor: account_info + .initially_requested_service_fee_minor, + }, + bills_coverage_in_percentage_opt, } } @@ -741,31 +969,37 @@ fn sort_interpretable_adjustments( Ord::cmp( &( result_b.bills_coverage_in_percentage_opt, - result_a.initial_balance, + result_a.info.initially_requested_service_fee_minor, ), &( result_a.bills_coverage_in_percentage_opt, - result_b.initial_balance, + result_b.info.initially_requested_service_fee_minor, ), ) }); let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { - Ord::cmp(&result_a.initial_balance, &result_b.initial_balance) + Ord::cmp( + &result_a.info.initially_requested_service_fee_minor, + &result_b.info.initially_requested_service_fee_minor, + ) }); let all_results = finished_sorted.chain(eliminated_sorted).collect(); (all_results, were_no_accounts_eliminated) } -fn generate_usize_guts(gn: &mut ThreadRng, low: usize, up_to: usize) -> usize { +fn generate_range(gn: &mut ThreadRng, low: T, up_to: T) -> T +where + T: SampleUniform + PartialOrd, +{ gn.gen_range(low..up_to) } fn generate_non_zero_usize(gn: &mut ThreadRng, up_to: usize) -> usize { - generate_usize_guts(gn, 1, up_to) + generate_range(gn, 1, up_to) } fn generate_usize(gn: &mut ThreadRng, up_to: usize) -> usize { - generate_usize_guts(gn, 0, up_to) + generate_range(gn, 0, up_to) } fn generate_boolean(gn: &mut ThreadRng) -> bool { @@ -879,19 +1113,137 @@ impl PercentageFulfillmentDistribution { } } +struct PreparedAdjustmentAndThresholds { + prepared_adjustment: PreparedAdjustment, + used_thresholds: AppliedThresholds, +} + struct CommonScenarioInfo { cw_service_fee_balance_minor: u128, required_adjustment: Adjustment, + used_thresholds: AppliedThresholds, } struct InterpretableAdjustmentResult { - initial_balance: u128, - debt_age_s: u64, + info: AccountInfo, // Account was eliminated from payment if None bills_coverage_in_percentage_opt: Option, } +impl AccountWithWallet for InterpretableAdjustmentResult { + fn wallet(&self) -> &Wallet { + &self.info.wallet + } +} + struct AccountInfo { wallet: Wallet, initially_requested_service_fee_minor: u128, debt_age_s: u64, } + +impl AccountWithWallet for AccountInfo { + fn wallet(&self) -> &Wallet { + &self.wallet + } +} + +enum AppliedThresholds { + Defaulted, + SingleButRandomized { + common_thresholds: PaymentThresholds, + }, + RandomizedForEachAccount { + individual_thresholds: IndividualThresholds, + }, +} + +impl AppliedThresholds { + fn fix_individual_thresholds_if_needed( + self, + wallet_and_thresholds_pairs: Vec<(Wallet, PaymentThresholds)>, + ) -> Self { + match self { + AppliedThresholds::RandomizedForEachAccount { .. } => { + assert!( + !wallet_and_thresholds_pairs.is_empty(), + "Pairs should be missing by now" + ); + let hash_map = HashMap::from_iter(wallet_and_thresholds_pairs); + let individual_thresholds = IndividualThresholds { + thresholds: Either::Right(hash_map), + }; + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } + } + x => x, + } + } +} + +struct IndividualThresholds { + thresholds: Either, HashMap>, +} + +fn try_make_qualified_payables_by_applied_thresholds( + payable_accounts: Vec, + applied_thresholds: &AppliedThresholds, + now: SystemTime, +) -> ( + Vec, + Vec<(Wallet, PaymentThresholds)>, +) { + let payment_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); + match applied_thresholds { + AppliedThresholds::Defaulted => ( + try_making_guaranteed_qualified_payables( + payable_accounts, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + false, + ), + vec![], + ), + AppliedThresholds::SingleButRandomized { common_thresholds } => ( + try_making_guaranteed_qualified_payables( + payable_accounts, + common_thresholds, + now, + false, + ), + vec![], + ), + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } => { + let vec_of_thresholds = individual_thresholds + .thresholds + .as_ref() + .left() + .expect("should be Vec at this stage"); + assert_eq!(payable_accounts.len(), vec_of_thresholds.len(), "The number of generated \ + payables {} differs from their sets of thresholds {}, but one should've been derived \ + from the other", payable_accounts.len(), vec_of_thresholds.len()); + let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); + zipped.fold( + (vec![], vec![]), + |(mut qualified_payables, mut wallet_thresholds_pairs), + (payable, its_thresholds)| match make_single_qualified_payable_opt( + payable, + &payment_inspector, + &its_thresholds, + false, + now, + ) { + Some(qualified_payable) => { + let wallet = qualified_payable.bare_account.wallet.clone(); + qualified_payables.push(qualified_payable); + wallet_thresholds_pairs.push((wallet, *its_thresholds)); + (qualified_payables, wallet_thresholds_pairs) + } + None => (qualified_payables, wallet_thresholds_pairs), + }, + ) + } + } +} diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 3ec7e6557..92d192eb3 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1774,42 +1774,48 @@ pub fn try_making_guaranteed_qualified_payables( now: SystemTime, should_panic: bool, ) -> Vec { - fn panic( - payable: &PayableAccount, - payment_thresholds: &PaymentThresholds, - now: SystemTime, - ) -> ! { - panic!( - "You intend to create qualified payables but their parameters not always make them qualify \ - as in: {:?} where the balance needs to get over {}", - payable, - PayableThresholdsGaugeReal::default().calculate_payout_threshold_in_gwei( - payment_thresholds, - SystemTime::now().duration_since(now).unwrap().as_secs() - ) - ) - } - let payable_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); payables .into_iter() .flat_map(|payable| { - match payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) { - Some(payment_threshold_intercept) => Some(QualifiedPayableAccount::new( - payable, - payment_threshold_intercept, - CreditorThresholds::new(gwei_to_wei( - payment_thresholds.permanent_debt_allowed_gwei, - )), - )), - None => { - if should_panic { - panic(&payable, &payment_thresholds, now) - } else { - None - } - } - } + make_single_qualified_payable_opt( + payable, + &payable_inspector, + payment_thresholds, + should_panic, + now, + ) }) .collect() } + +pub fn make_single_qualified_payable_opt( + payable: PayableAccount, + payable_inspector: &PayableInspector, + payment_thresholds: &PaymentThresholds, + should_panic: bool, + now: SystemTime, +) -> Option { + match payable_inspector.payable_exceeded_threshold(&payable, payment_thresholds, now) { + Some(payment_threshold_intercept) => Some(QualifiedPayableAccount::new( + payable, + payment_threshold_intercept, + CreditorThresholds::new(gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei)), + )), + None => { + if should_panic { + panic!( + "You intend to create qualified payables but their parameters not always make \ + them qualify as in: {:?} where the balance needs to get over {}", + payable, + PayableThresholdsGaugeReal::default().calculate_payout_threshold_in_gwei( + payment_thresholds, + SystemTime::now().duration_since(now).unwrap().as_secs() + ) + ) + } else { + None + } + } + } +} From 298b91192f1628dfe48989320e181d2d40e60b26 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 1 May 2024 00:15:59 +0200 Subject: [PATCH 187/250] GH-711: cosmetics --- .../payment_adjuster/non_unit_tests/mod.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 61a5e9c7c..1f3d1f7b1 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -605,8 +605,12 @@ fn render_results_to_file_and_attempt_basic_assertions( fn introduction(file: &mut File) { write_thick_dividing_line(file); write_thick_dividing_line(file); - file.write(b"A short summary can be found at the tail\n") - .unwrap(); + let page_width = PAGE_WIDTH; + file.write_fmt(format_args!( + "{:^page_width$}", + "A short summary can be found at the tail\n" + )) + .unwrap(); write_thick_dividing_line(file); write_thick_dividing_line(file) } @@ -836,14 +840,14 @@ fn single_account_output( let starting_gap = STARTING_GAP; let _ = file .write_fmt(format_args!( - "{:first_column_width$} wei | {:>age_width$} s | {}\n", - "", + "{}{:first_column_width$} wei | {:>age_width$} s | {}\n", individual_thresholds_opt .map(|thresholds| format!( "{:first_column_width$}\n", "", thresholds )) .unwrap_or("".to_string()), + "", balance_minor.separate_with_commas(), age_s.separate_with_commas(), resolve_account_ending_status_graphically(bill_coverage_in_percentage_opt), @@ -1221,9 +1225,15 @@ fn try_make_qualified_payables_by_applied_thresholds( .as_ref() .left() .expect("should be Vec at this stage"); - assert_eq!(payable_accounts.len(), vec_of_thresholds.len(), "The number of generated \ + assert_eq!( + payable_accounts.len(), + vec_of_thresholds.len(), + "The number of generated \ payables {} differs from their sets of thresholds {}, but one should've been derived \ - from the other", payable_accounts.len(), vec_of_thresholds.len()); + from the other", + payable_accounts.len(), + vec_of_thresholds.len() + ); let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); zipped.fold( (vec![], vec![]), From 5171a5414239876e1a6f5c2dd9bc85600b88e6d4 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 1 May 2024 00:49:19 +0200 Subject: [PATCH 188/250] GH-711: more cosmetics --- .../payment_adjuster/non_unit_tests/mod.rs | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 1f3d1f7b1..c584137ad 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -607,8 +607,8 @@ fn introduction(file: &mut File) { write_thick_dividing_line(file); let page_width = PAGE_WIDTH; file.write_fmt(format_args!( - "{:^page_width$}", - "A short summary can be found at the tail\n" + "{:^page_width$}\n", + "A short summary can be found at the tail" )) .unwrap(); write_thick_dividing_line(file); @@ -722,40 +722,29 @@ fn render_scenario_header( "CW service fee balance: {} wei\n\ Portion of CW balance used: {}%\n\ Maximal txt count due to CW txt fee balance: {}\n\ - Used PaymentThresholds:\n", + Used PaymentThresholds: {}\n", scenario_common .cw_service_fee_balance_minor .separate_with_commas(), portion_of_cw_used_percents, - resolve_affordable_transaction_count(&scenario_common.required_adjustment) + resolve_affordable_transaction_count(&scenario_common.required_adjustment), + resolve_comment_on_thresholds(&scenario_common.used_thresholds) )) .unwrap(); - let _ = match &scenario_common.used_thresholds { +} + +fn resolve_comment_on_thresholds(applied_thresholds: &AppliedThresholds) -> String { + match applied_thresholds { AppliedThresholds::Defaulted | AppliedThresholds::SingleButRandomized { .. } => { - if let AppliedThresholds::SingleButRandomized { common_thresholds } = - &scenario_common.used_thresholds + if let AppliedThresholds::SingleButRandomized { common_thresholds } = applied_thresholds { - write_fully_described_thresholds(file, common_thresholds); + format!("SHARED BUT CUSTOM\n{}", common_thresholds) } else { - file.write(b"Defaulted\n").unwrap(); + format!("DEFAULTED\n{}", PRESERVED_TEST_PAYMENT_THRESHOLDS) } } - AppliedThresholds::RandomizedForEachAccount { .. } => { - file.write(b"Individual for each account\n").unwrap(); - } - }; -} - -fn write_fully_described_thresholds( - file: &mut File, - randomized_thresholds: &PaymentThresholds, -) -> usize { - file.write( - format!("{:?}\n", randomized_thresholds) - .replace(",", ",\n") - .as_bytes(), - ) - .unwrap() + AppliedThresholds::RandomizedForEachAccount { .. } => "INDIVIDUAL".to_string(), + } } fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { @@ -843,7 +832,7 @@ fn single_account_output( "{}{:first_column_width$} wei | {:>age_width$} s | {}\n", individual_thresholds_opt .map(|thresholds| format!( - "{:first_column_width$}\n", + "{:first_column_width$}\n", "", thresholds )) .unwrap_or("".to_string()), @@ -904,7 +893,7 @@ fn write_error(file: &mut File, error: PaymentAdjusterError) { fn resolve_affordable_transaction_count(adjustment: &Adjustment) -> String { match adjustment { - Adjustment::ByServiceFee => "Unlimited".to_string(), + Adjustment::ByServiceFee => "UNLIMITED".to_string(), Adjustment::TransactionFeeInPriority { affordable_transaction_count, } => affordable_transaction_count.to_string(), From 62ae19f84406e76f4f4ca0f27bfde3e15e462381 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 1 May 2024 02:33:32 +0200 Subject: [PATCH 189/250] GH-711: interim commit --- .../payment_adjuster/non_unit_tests/mod.rs | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index c584137ad..f3509ca52 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -50,46 +50,58 @@ fn loading_test_with_randomized_params() { // the PaymentAdjuster down and potentially the whole Node with it; on contrary, it should // always give some reasonable results and live up to its original purpose with account // adjustments. That said, a smaller amount of these attempts are expected to end up vain, - // legitimately, though, because of so defined error cases. + // legitimately, though, because of some error cases. - // When the test finishes, it writes some key figures of each pseudo-randomly invented scenario, - // and a summary of the whole test set with some useful statistics that can help to evaluate - // the behavior to better understand this feature in the Node or consider some enhancement to be - // implemented. + // When the test finishes, it fills in a file with some key figures for each of those exercised + // scenarios and a summary of the whole experiment with some useful statistics that can help + // with an evaluation of the thought behavior, to better understand this Node's feature or + // consider new implementations for its enhancement. - // For somebody not so familiar with the used algorithms, there might emerge results (they tend - // to be rare but absolutely valid and wanted - the more their observation can be interesting) - // that can bring one into confusion. + // For somebody not so familiar with this algorithm, there might be results (they seem rare + // but absolutely valid and wanted - the more their observation can be interesting) that can + // make one feel puzzled. // The scenario generator is designed to provide as random and wide variety of situations as - // possible, but it has plenty of limitations despite. The following is an example of tricky- - // -to-understand scenario output, where the statistics, the percents, might seem to have no - // reason and feel like something could be broken. Let's make a hint. These percents represent - // the initial debt coverage and in this case they might strike by their perhaps surprisingly - // inadequate proportionality with the trend that can be seen next to them, in the ascending - // balances. It needn't be that intuitive, though, because the final adjustment depends heavily - // on a so-called disqualification limit. That usually allows not to pay the entire balance but - // only such portion that will inherently suffice for us to stay unbanned. For a huge account, - // that forgiven part makes just a little fraction of a whole. Small accounts, however, if it - // can be applied (and not have to be disqualified), might be missing a big portion of - // themselves. This is what the numbers 63% and 94% illustrates, despite the former account - // comes across as it should take precedence and gain at the expanse of the latter. - - // CW service fee balance: 35,592,800,367,641,272 wei + // possible, but it has definitely plenty of limitations too. The following is an example of + // a tricky-to-understand scenario output, where the statistics, those percents, might seem to + // have no reason and rise a question if something could be broken. Let's clear it up. These + // percents represent the initial debt coverage. Here, they might strike by their perhaps + // surprisingly inadequate proportionality with the trend that can be seen just next to them, + // at the ascending balances. It doesn't need to be as intuitive as you think, though, as + // the final adjustment depends heavily on a so-called "disqualification limit". That usually + // allows to avoid paying the entire amount but only such portion that will inherently suffice + // for the payer to stay unbanned. + // For a huge account, this forgiven part makes just a little fraction of a whole (a few + // percents). Small accounts, however, if it can be applied (as opposed to getting disqualified), + // might be missing a big portion of themselves, reporting a loss of many percents. This is what + // the numbers like 99% and 90% illustrates, despite the letter account comes across as it + // should take precedence because of its expected larger weight, and gain at the expanse of + // the other. + + // CW service fee balance: 32,041,461,894,055,482 wei // Portion of CW balance used: 100% - // Maximal txt count due to CW txt fee balance: Unlimited - // ____________________________________________________________________________________________ - // 1,000,494,243,602,844 wei | 567,037 s | 100 % - // 1,505,465,842,696,120 wei | 540,217 s | 100 % - // 1,626,173,874,954,904 wei | 349,872 s | 100 % - // 20,688,283,645,189,616 wei | 472,661 s | 99 % # # # # # # # # - // 4,735,196,705,072,789 wei | 399,335 s | 96 % # # # # # # # # - // 6,770,347,763,782,591 wei | 245,857 s | 92 % # # # # # # # # + // Maximal txt count due to CW txt fee balance: UNLIMITED + // Used PaymentThresholds: DEFAULTED + // 2000000|1000|1000|1000000|500000|1000000 + // _____________________________________________________________________________________________ + // 1,988,742,049,305,843 wei | 236,766 s | 100 % + // 21,971,010,542,100,729 wei | 472,884 s | 99 % # # # # # # # # + // 4,726,030,753,976,563 wei | 395,377 s | 95 % # # # # # # # # + // 3,995,577,830,314,875 wei | 313,396 s | 90 % # # # # # # # # + // 129,594,971,536,673,815 wei | 343,511 s | X + + // Let's think over a possible belief that if an account is said to take higher gains it should + // be reflected in these percents. It doesn't necessarily have to be true: in the code, we first + // carefully propose a variety, as wide as possible, of partially - but enough - pre-adjusted + // accounts. The disqualification limit is where we leave each account at for that moment, and + // therefore, only if we have some more money when the maximized set of accounts to keep are + // determined we can still iterate over, with them sorted by descending weights, and give them + // some more one by one, the maximum they can absorb, until our wallet's balance meets zero. let now = SystemTime::now(); let mut gn = thread_rng(); let mut subject = PaymentAdjusterReal::new(); - let number_of_requested_scenarios = 1000; + let number_of_requested_scenarios = 2000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); let test_overall_output_collector = @@ -225,8 +237,6 @@ fn try_making_single_valid_scenario( fn make_payable_account( idx: usize, thresholds: &PaymentThresholds, - // threshold_limit: u128, - // guarantee_age: u64, now: SystemTime, gn: &mut ThreadRng, ) -> PayableAccount { @@ -252,19 +262,23 @@ fn make_payable_account( let parameter_b = generate_u128(); let parameter_c = generate_u128(); let parameter_d = generate_u128(); + let parameter_e = generate_u128(); + let parameter_f = generate_u128(); let mut use_variable_exponent = |parameter: u128, up_to: usize| { parameter.pow(generate_non_zero_usize(gn, up_to) as u32) }; - let a_b_c = use_variable_exponent(parameter_a, 3) - * use_variable_exponent(parameter_b, 4) - * use_variable_exponent(parameter_c, 5) - * parameter_d; - let addition = (0..6).fold(a_b_c, |so_far, subtrahend| { - if so_far != a_b_c { + let a_b_c_d_e = + parameter_a + * use_variable_exponent(parameter_b, 2) + * use_variable_exponent(parameter_c, 3) + * use_variable_exponent(parameter_d, 4) + * use_variable_exponent(parameter_e,5); + let addition = (0..6).fold(a_b_c_d_e, |so_far, subtrahend| { + if so_far != a_b_c_d_e { so_far } else { if let Some(num) = - a_b_c.checked_sub(use_variable_exponent(parameter_c, 6 - subtrahend)) + a_b_c_d_e.checked_sub(use_variable_exponent(parameter_f, 6 - subtrahend)) { num } else { From 1c68dabdfbf882ecacf863b66653c1a80f4c5674 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 2 May 2024 22:22:04 +0200 Subject: [PATCH 190/250] GH-711: perhaps ready for review --- .../payment_adjuster/non_unit_tests/mod.rs | 119 +++++++++--------- .../src/accountant/scanners/scanners_utils.rs | 2 - 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index f3509ca52..c90bef0a7 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -39,44 +39,43 @@ use thousands::Separable; use web3::types::U256; #[test] -//#[ignore] +// TODO If an option for "occasional tests" is added, this is a good adept +#[ignore] fn loading_test_with_randomized_params() { - // This test needs to be understood as a fuzz test, a generator of possibly an overwhelming - // amount of scenarios that the PaymentAdjuster might happen to be asked to sort them out while - // there might be many and many combinations that a human would be having a hard time imagining; - // now we ought to ponder that some of them might be corner cases of which there was no - // awareness when it was being designed. So, the main purpose of this test is to prove that even - // a huge number of goes with as-much-as-possible variable inputs will not shoot - // the PaymentAdjuster down and potentially the whole Node with it; on contrary, it should - // always give some reasonable results and live up to its original purpose with account - // adjustments. That said, a smaller amount of these attempts are expected to end up vain, - // legitimately, though, because of some error cases. - - // When the test finishes, it fills in a file with some key figures for each of those exercised - // scenarios and a summary of the whole experiment with some useful statistics that can help - // with an evaluation of the thought behavior, to better understand this Node's feature or - // consider new implementations for its enhancement. - - // For somebody not so familiar with this algorithm, there might be results (they seem rare - // but absolutely valid and wanted - the more their observation can be interesting) that can - // make one feel puzzled. - - // The scenario generator is designed to provide as random and wide variety of situations as - // possible, but it has definitely plenty of limitations too. The following is an example of - // a tricky-to-understand scenario output, where the statistics, those percents, might seem to - // have no reason and rise a question if something could be broken. Let's clear it up. These - // percents represent the initial debt coverage. Here, they might strike by their perhaps - // surprisingly inadequate proportionality with the trend that can be seen just next to them, - // at the ascending balances. It doesn't need to be as intuitive as you think, though, as - // the final adjustment depends heavily on a so-called "disqualification limit". That usually - // allows to avoid paying the entire amount but only such portion that will inherently suffice - // for the payer to stay unbanned. - // For a huge account, this forgiven part makes just a little fraction of a whole (a few - // percents). Small accounts, however, if it can be applied (as opposed to getting disqualified), - // might be missing a big portion of themselves, reporting a loss of many percents. This is what - // the numbers like 99% and 90% illustrates, despite the letter account comes across as it - // should take precedence because of its expected larger weight, and gain at the expanse of - // the other. + // This is a fuzz test, a generator of possibly an overwhelming amount of scenarios that could + // get the PaymentAdjuster to be asked to sort them out even in real situations while there + // might be many and many combinations that a human is having a hard time just imagining; of + // them some might be corner cases whose threatening wasn't known when this was being designed. + // This test is to prove that even a huge number of runs, with hopefully highly variable inputs, + // will not shoot the PaymentAdjuster down and the Node with it; on the contrary, it should + // be able to give reasonable results and live up to its original purpose of adjustments. + + // Part of the requested count is rejected before the test begins as there are generated + // scenarios with such parameters that don't fit to a variety of conditions. It's easier to keep + // it this way than setting up an algorithm with enough "tamed" randomness. Other bunch of them + // will likely be marked as legitimate errors that the PaymentAdjuster can detect. + // When the test reaches its end, a text file is filled in with some key figures of the performed + // exercises and finally also an overall summary with useful statistics that can serve to + // evaluate the actual behavior against the desired. + + // If you are new to this algorithm, there might be results (maybe rare, but absolutely valid + // and wanted, and so deserving some interest) that can have one puzzled, though. + + // The example further below presents a tricky-to-understand output belonging to one set of + // payables. See those percentages. They may not excel at explaining themselves when it comes to + // their inconsistent proportionality towards the balances. These percents represent a payment + // coverage of the initial debts. But why don't they correspond with ascending balances? There's + // a principle to equip accounts low balances with the biggest weights. True. However, it doesn't + // need to be reflected so clearly, though. The adjustment depends heavily on a so-called + // "disqualification limit". Besides other purposes, this value affects that the payment won't + // require the entire amount but only its portion. That inherently will do for the payer to stay + // unbanned. In bulky accounts, this until-some-time forgiven portion stands only as a fraction + // of a whole. Small accounts, however, if it can be applied (as opposed to the account having + // to be excluded) might get shrunk a lot, and therefore many percents are to be reported as + // missing. This is what the numbers like 99% and 90% illustrates. That said, the letter account + // comes across as it should take precedence for its expectedly larger weight, and gain at the + // expanse of the other, but the percents speak otherwise. Yet, it's correct. The interpretation + // is the key. (Caution: this test displays its output with those accounts sorted). // CW service fee balance: 32,041,461,894,055,482 wei // Portion of CW balance used: 100% @@ -90,13 +89,11 @@ fn loading_test_with_randomized_params() { // 3,995,577,830,314,875 wei | 313,396 s | 90 % # # # # # # # # // 129,594,971,536,673,815 wei | 343,511 s | X - // Let's think over a possible belief that if an account is said to take higher gains it should - // be reflected in these percents. It doesn't necessarily have to be true: in the code, we first - // carefully propose a variety, as wide as possible, of partially - but enough - pre-adjusted - // accounts. The disqualification limit is where we leave each account at for that moment, and - // therefore, only if we have some more money when the maximized set of accounts to keep are - // determined we can still iterate over, with them sorted by descending weights, and give them - // some more one by one, the maximum they can absorb, until our wallet's balance meets zero. + // In the code, we select and pale up accounts so that the picked balance isn't the full range, + // but still enough. The disqualification limit draws the cut. Only if the wallet isn't all + // dried up, after the accounts to keep are determined, while iterated over again, accounts + // sorted by descending weights are given more of it one by one, the maximum they can absorb, + // until there is still something to spend. let now = SystemTime::now(); let mut gn = thread_rng(); @@ -240,14 +237,17 @@ fn make_payable_account( now: SystemTime, gn: &mut ThreadRng, ) -> PayableAccount { - // Why is this construction so complicated? Well, I wanted to get the test showing partial - // adjustments where the final accounts can be paid enough but still not all up to their - // formerly claimed balance. It turned out it is very difficult to achieve that, I couldn't - // really come up with parameters that would certainly bring this condition. I ended up - // experimenting and looking for an algorithm that would make the parameters as random as - // possible because the generator alone is not much good at it. This isn't optimal either, - // but it allows to observe some of those partial adjustments, however, with a rate of maybe - // 0.5 % out of those all attempts to create a proper test scenario. + // Why is this construction so complicated? Well, I wanted to get the test showing partially + // fulfilling adjustments where the final accounts can be paid enough but still not all up to + // their formerly claimed balance. It turned out it is very difficult to achieve with the use of + // randomized ranges, I couldn't really come up with parameters that would promise this condition. + // I ended up experimenting and looking for an algorithm that would make the parameters as random + // as possible because the generator alone is not much good at it, using gradually, but + // individually generated parameters that I put together for better chances of randomness. Many + // produced accounts will not make it through into the actual test, filtered out when attempted + // to be converted into a proper QualifiedPayableAccount. This isn't optimal, sure, but it allows + // to observe some of those partial adjustments, however, with rather a low rate of occurrence + // among those all attempts of acceptable scenarios. let wallet = make_wallet(&format!("wallet{}", idx)); let mut generate_age_segment = || { generate_non_zero_usize( @@ -267,12 +267,11 @@ fn make_payable_account( let mut use_variable_exponent = |parameter: u128, up_to: usize| { parameter.pow(generate_non_zero_usize(gn, up_to) as u32) }; - let a_b_c_d_e = - parameter_a + let a_b_c_d_e = parameter_a * use_variable_exponent(parameter_b, 2) * use_variable_exponent(parameter_c, 3) * use_variable_exponent(parameter_d, 4) - * use_variable_exponent(parameter_e,5); + * use_variable_exponent(parameter_e, 5); let addition = (0..6).fold(a_b_c_d_e, |so_far, subtrahend| { if so_far != a_b_c_d_e { so_far @@ -587,13 +586,15 @@ fn render_results_to_file_and_attempt_basic_assertions( "All handled scenarios including those invalid ones ({}) != requested scenarios count ({})", total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios ); - // The next assertions depend heavily on the setup for the scenario generator!! It rather - // indicates how well the setting is so that you can adjust it eventually, to see more relevant - // results + // Only some of the generated scenarios are acceptable, don't be surprised by the waste. That's + // anticipated given the nature of the generator and the requirements on the payable accounts + // so that they are picked up and let in the PaymentAdjuster. We'll be better off truly faithful + // to the use case and the expected conditions. Therefore, we insist on making "guaranteed" + // QualifiedPayableAccounts out of PayableAccount which is where we take the losses. let entry_check_pass_rate = 100 - ((test_overall_output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); - let required_pass_rate = 70; + let required_pass_rate = 50; assert!( entry_check_pass_rate >= required_pass_rate, "Not at least {}% from those {} scenarios \ diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index a9ac80a1f..16c445362 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -300,7 +300,6 @@ pub mod payable_scanner_utils { .payable_threshold_gauge .is_innocent_age(debt_age, payment_thresholds.maturity_threshold_sec) { - eprintln!("age not enough"); return None; } @@ -308,7 +307,6 @@ pub mod payable_scanner_utils { payable.balance_wei, gwei_to_wei(payment_thresholds.permanent_debt_allowed_gwei), ) { - eprintln!("balance not enough"); return None; } From 69e9fecc3132f329486889fa1df60d70dfb1f201 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 6 May 2024 00:14:33 +0200 Subject: [PATCH 191/250] GH-711: better working name chosen --- ...d_age_calculator.rs => balance_calculator.rs} | 16 ++++++++-------- .../criterion_calculators/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) rename node/src/accountant/payment_adjuster/criterion_calculators/{balance_and_age_calculator.rs => balance_calculator.rs} (88%) diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs similarity index 88% rename from node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs rename to node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index d1c531588..e4aa8352a 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_and_age_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -5,9 +5,9 @@ use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::QualifiedPayableAccount; #[derive(Default)] -pub struct BalanceAndAgeCriterionCalculator {} +pub struct BalanceCriterionCalculator {} -impl CriterionCalculator for BalanceAndAgeCriterionCalculator { +impl CriterionCalculator for BalanceCriterionCalculator { fn calculate( &self, account: &QualifiedPayableAccount, @@ -24,13 +24,13 @@ impl CriterionCalculator for BalanceAndAgeCriterionCalculator { } fn parameter_name(&self) -> &'static str { - "BALANCE AND AGE" + "BALANCE" } } #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; + use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; @@ -40,15 +40,15 @@ mod tests { #[test] fn calculator_knows_its_name() { - let subject = BalanceAndAgeCriterionCalculator::default(); + let subject = BalanceCriterionCalculator::default(); let result = subject.parameter_name(); - assert_eq!(result, "BALANCE AND AGE") + assert_eq!(result, "BALANCE") } #[test] - fn balance_and_age_criterion_calculator_works() { + fn balance_criterion_calculator_works() { let now = SystemTime::now(); let analyzed_accounts = [50, 100, 2_222] .into_iter() @@ -66,7 +66,7 @@ mod tests { let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); let payment_adjuster_inner = PaymentAdjusterInnerReal::new(now, None, 123456789, largest_exceeding_balance); - let subject = BalanceAndAgeCriterionCalculator::default(); + let subject = BalanceCriterionCalculator::default(); let computed_criteria = analyzed_accounts .iter() diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index df7a4765e..4afc5c069 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -pub mod balance_and_age_calculator; +pub mod balance_calculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::QualifiedPayableAccount; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 03a2851e3..38ebebd55 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -18,7 +18,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::adjustment_runners::{ AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, }; -use crate::accountant::payment_adjuster::criterion_calculators::balance_and_age_calculator::BalanceAndAgeCriterionCalculator; +use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::{collection_diagnostics, diagnostics}; @@ -156,7 +156,7 @@ impl PaymentAdjusterReal { analyzer: PreparatoryAnalyzer::new(), disqualification_arbiter: DisqualificationArbiter::default(), service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), - calculators: vec![Box::new(BalanceAndAgeCriterionCalculator::default())], + calculators: vec![Box::new(BalanceCriterionCalculator::default())], inner: Box::new(PaymentAdjusterInnerNull::default()), logger: Logger::new("PaymentAdjuster"), } @@ -1950,7 +1950,7 @@ mod tests { let input_matrix: InputMatrixConfigurator = |(nominal_account_1, nominal_account_2, _now)| { vec![ - // First test case: BalanceAndAgeCalculator + // First test case: BalanceCalculator { let mut account_1 = nominal_account_1; account_1.bare_account.balance_wei += 123_456_789; From 4c789617902fb3525f85fa97c84441f7ada38b04 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 6 May 2024 00:23:30 +0200 Subject: [PATCH 192/250] GH-711: right copyrights --- .../criterion_calculators/balance_calculator.rs | 2 +- .../accountant/payment_adjuster/criterion_calculators/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index e4aa8352a..cc37d0d61 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index 4afc5c069..69853cde2 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod balance_calculator; From 7d5aa880ecb2422ab5f4d6eed731ddf6081c67b3 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 May 2024 00:05:30 +0200 Subject: [PATCH 193/250] GH-711: preparatory analyzer can report about insufficiecny for both balances together --- node/src/accountant/mod.rs | 24 +- .../logging_and_diagnostics/log_functions.rs | 2 +- .../miscellaneous/data_structures.rs | 169 +-------- node/src/accountant/payment_adjuster/mod.rs | 355 ++++++++++++------ .../payment_adjuster/non_unit_tests/mod.rs | 25 +- .../preparatory_analyser/mod.rs | 353 ++++++++++------- 6 files changed, 492 insertions(+), 436 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 5369d7d86..205bd8680 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1068,7 +1068,7 @@ mod tests { use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjusterError, + Adjustment, AdjustmentAnalysis, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; @@ -1744,10 +1744,13 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_from_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 1, - per_transaction_requirement_minor: 60 * 55_000, - cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 60 * 55_000, + cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), + }), + service_fee_opt: None, }, )); @@ -1760,9 +1763,9 @@ mod tests { your balances can cover neither reasonable portion of any of those payables recently \ qualified for an imminent payment. You must add more funds into your consuming wallet \ in order to stay off delinquency bans that your creditors may apply against you \ - otherwise. Details: Found transaction fee balance that is not enough for a single \ + otherwise. Details: Current transaction fee balance is not enough to pay a single \ payment. Number of canceled payments: 1. Transaction fee per payment: 3,300,000 wei, \ - while in wallet: 123,000,000,000 wei." + while the wallet contains: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); @@ -1809,10 +1812,13 @@ mod tests { let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 20, - per_transaction_requirement_minor: 40_000_000_000, - cw_transaction_fee_balance_minor: U256::from(123), + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 40_000_000_000, + cw_transaction_fee_balance_minor: U256::from(123), + }), + service_fee_opt: None, }, )); let payable_scanner = PayableScannerBuilder::new() diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index 1361989e3..a644584be 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -160,7 +160,7 @@ pub fn log_insufficient_transaction_fee_balance( ) { warning!( logger, - "Your transaction fee balance {} wei is not going to cover the anticipated fees to send {} \ + "Transaction fee balance of {} wei is not going to cover the anticipated fee to send {} \ transactions, with {} wei required per one. Maximum count is set to {}. Adjustment must be \ performed.", transaction_fee_minor.separate_with_commas(), diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 1ecb7fed6..0f3fd705f 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -1,11 +1,8 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::PaymentAdjusterError; use crate::accountant::AnalyzedPayableAccount; use crate::sub_lib::wallet::Wallet; -use masq_lib::utils::ExpectValue; use web3::types::U256; #[derive(Clone, Debug, PartialEq, Eq)] @@ -131,123 +128,11 @@ impl TransactionCountsBy16bits { } } -#[derive(Default, Clone)] -pub struct AdjustmentPossibilityErrorBuilder { - context_opt: Option, - analyzed_accounts_count: usize, - service_fee_total_required_minor: u128, - cw_service_fee_balance_minor: u128, -} - -impl AdjustmentPossibilityErrorBuilder { - pub fn context(mut self, context: TransactionFeePastCheckContext) -> Self { - if let Some(old) = self.context_opt.replace(context) { - panic!( - "Context must be supplied only once. Was {:?} and {:?} is being set", - old, - self.context_opt.expect("just put in there") - ) - } - self - } - - pub fn all_time_supplied_parameters( - mut self, - analyzed_accounts_count: usize, - service_fee_total_required_minor: u128, - cw_service_fee_balance_minor: u128, - ) -> Self { - self.analyzed_accounts_count = analyzed_accounts_count; - self.service_fee_total_required_minor = service_fee_total_required_minor; - self.cw_service_fee_balance_minor = cw_service_fee_balance_minor; - self - } - - pub fn build(self) -> PaymentAdjusterError { - let cw_service_fee_balance_minor = self.cw_service_fee_balance_minor; - let (number_of_accounts, total_service_fee_required_minor, transaction_fee_appendix_opt) = - self.derive_params(); - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts, - total_service_fee_required_minor, - cw_service_fee_balance_minor, - transaction_fee_appendix_opt, - } - } - - fn derive_params(self) -> (usize, u128, Option) { - match self.context_opt.expectv("Tx fee past check context") { - TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt } => ( - self.analyzed_accounts_count, - self.service_fee_total_required_minor, - limitation_opt, - ), - TransactionFeePastCheckContext::TransactionFeeAccountsDumped { - past_txs_count, - past_sum_of_service_fee_balances, - } => (past_txs_count, past_sum_of_service_fee_balances, None), - } - } -} - -#[derive(Debug, Clone)] -pub enum TransactionFeePastCheckContext { - TransactionFeeCheckDone { - limitation_opt: Option, - }, - TransactionFeeAccountsDumped { - past_txs_count: usize, - past_sum_of_service_fee_balances: u128, - }, -} - -impl TransactionFeePastCheckContext { - pub fn accounts_dumped(whole_set_of_analyzed_accounts: &[WeightedPayable]) -> Self { - let past_txs_count = whole_set_of_analyzed_accounts.len(); - let past_sum_of_service_fee_balances: u128 = - sum_as(whole_set_of_analyzed_accounts, |account| { - account.balance_minor() - }); - - TransactionFeePastCheckContext::TransactionFeeAccountsDumped { - past_txs_count, - past_sum_of_service_fee_balances, - } - } - - pub fn initial_check_done(limitation_opt: Option) -> Self { - TransactionFeePastCheckContext::TransactionFeeCheckDone { limitation_opt } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct TransactionFeeLimitation { - pub count_limit: u16, - pub cw_transaction_fee_balance_minor: u128, - pub per_transaction_required_fee_minor: u128, -} - -impl TransactionFeeLimitation { - pub fn new( - count_limit: u16, - cw_transaction_fee_balance_minor: u128, - per_transaction_required_fee_minor: u128, - ) -> Self { - Self { - count_limit, - cw_transaction_fee_balance_minor, - per_transaction_required_fee_minor, - } - } -} - #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AdjustmentPossibilityErrorBuilder, RecursionResults, - TransactionCountsBy16bits, TransactionFeeLimitation, TransactionFeePastCheckContext, + AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsBy16bits, }; - use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::make_payable_account; use ethereum_types::U256; @@ -324,56 +209,4 @@ mod tests { u16::MAX as usize + correction as usize } } - - #[test] - #[should_panic( - expected = "Context must be supplied only once. Was TransactionFeeCheckDone { \ - limitation_opt: None } and TransactionFeeCheckDone { limitation_opt: Some(TransactionFeeLimitation \ - { count_limit: 11, cw_transaction_fee_balance_minor: 22, per_transaction_required_fee_minor: 3 }) } \ - is being set" - )] - fn context_can_be_called_just_once() { - let mut subject = AdjustmentPossibilityErrorBuilder::default(); - subject.context_opt = Some(TransactionFeePastCheckContext::TransactionFeeCheckDone { - limitation_opt: None, - }); - - let _ = subject.context(TransactionFeePastCheckContext::TransactionFeeCheckDone { - limitation_opt: Some(TransactionFeeLimitation { - count_limit: 11, - cw_transaction_fee_balance_minor: 22, - per_transaction_required_fee_minor: 3, - }), - }); - } - - #[test] - fn construction_of_error_context_with_accounts_dumped_works() { - let mut account_1 = make_weighed_account(123); - account_1 - .analyzed_account - .qualified_as - .bare_account - .balance_wei = 1234567; - let mut account_2 = make_weighed_account(345); - account_2 - .analyzed_account - .qualified_as - .bare_account - .balance_wei = 999888777; - let weighted_accounts = vec![account_1, account_2]; - - let context = TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts); - - match context { - TransactionFeePastCheckContext::TransactionFeeAccountsDumped { - past_txs_count: txs_count, - past_sum_of_service_fee_balances: sum_of_transaction_fee_balances, - } => { - assert_eq!(txs_count, 2); - assert_eq!(sum_of_transaction_fee_balances, 1234567 + 999888777) - } - x => panic!("We expected version for accounts dump but got: {:?}", x), - } - } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 38ebebd55..878bd9721 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -34,13 +34,13 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ LowGainingAccountEliminated, SomeAccountsProcessed, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, AdjustmentPossibilityErrorBuilder, RecursionResults, TransactionFeeLimitation, TransactionFeePastCheckContext, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, sum_as, zero_affordable_accounts_found, }; -use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; +use crate::accountant::payment_adjuster::preparatory_analyser::{LateServiceFeeSingleTxErrorFactory, PreparatoryAnalyzer}; use crate::accountant::payment_adjuster::service_fee_adjuster::{ ServiceFeeAdjuster, ServiceFeeAdjusterReal, }; @@ -242,9 +242,7 @@ impl PaymentAdjusterReal { Either, Vec>, PaymentAdjusterError, > { - let error_builder = AdjustmentPossibilityErrorBuilder::default().context( - TransactionFeePastCheckContext::accounts_dumped(&weighted_accounts), - ); + let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighted_accounts); let weighted_accounts_affordable_by_transaction_fee = dump_unaffordable_accounts_by_transaction_fee( @@ -257,7 +255,7 @@ impl PaymentAdjusterReal { if self.analyzer.recheck_if_service_fee_adjustment_is_needed( &weighted_accounts_affordable_by_transaction_fee, cw_service_fee_balance_minor, - error_builder, + error_factory, &self.logger, )? { diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); @@ -444,33 +442,43 @@ impl AdjustmentAnalysis { #[derive(Debug, PartialEq, Eq, VariantCount)] pub enum PaymentAdjusterError { - NotEnoughTransactionFeeBalanceForSingleTx { + EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: usize, - per_transaction_requirement_minor: u128, - cw_transaction_fee_balance_minor: U256, + transaction_fee_opt: Option, + service_fee_opt: Option, }, - NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts: usize, number_of_accounts: usize, - total_service_fee_required_minor: u128, + original_service_fee_required_total_minor: u128, cw_service_fee_balance_minor: u128, - transaction_fee_appendix_opt: Option, }, AllAccountsEliminated, } +#[derive(Debug, PartialEq, Eq)] +pub struct TransactionFeeImmoderateInsufficiency { + pub per_transaction_requirement_minor: u128, + pub cw_transaction_fee_balance_minor: U256, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ServiceFeeImmoderateInsufficiency { + pub total_service_fee_required_minor: u128, + pub cw_service_fee_balance_minor: u128, +} + impl PaymentAdjusterError { pub fn insolvency_detected(&self) -> bool { match self { - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => true, - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - .. - } => true, + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => true, + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => true, PaymentAdjusterError::AllAccountsEliminated => true, - // So far, we don't have to worry much, but adding an error, that doesn't imply at the - // same time that an insolvency was proved before it, might become relevant in - // the future. Then, it'll be important to check for those consequences (Hint: It is - // anticipated to affect the wording of error announcements that take place back nearer - // to the Accountant's general area) + // We haven't needed to worry so yet, but adding an error not implying that + // an insolvency was found out, might become relevant in the future. Then, it'll + // be important to check for those consequences (Hint: It is anticipated to affect + // the wording of error announcements that take place back nearer to the Accountant's + // general area) } } } @@ -478,45 +486,60 @@ impl PaymentAdjusterError { impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts, - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor, - } => write!( - f, - "Found transaction fee balance that is not enough for a single payment. Number of \ - canceled payments: {}. Transaction fee per payment: {} wei, while in wallet: {} wei", - number_of_accounts, - per_transaction_requirement_minor.separate_with_commas(), - cw_transaction_fee_balance_minor.separate_with_commas() - ), - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + transaction_fee_opt, + service_fee_opt, + } => { + match (transaction_fee_opt, service_fee_opt) { + (Some(transaction_fee_check_summary), None) => + write!( + f, + "Current transaction fee balance is not enough to pay a single payment. \ + Number of canceled payments: {}. Transaction fee per payment: {} wei, while \ + the wallet contains: {} wei", + number_of_accounts, + transaction_fee_check_summary.per_transaction_requirement_minor.separate_with_commas(), + transaction_fee_check_summary.cw_transaction_fee_balance_minor.separate_with_commas() + ), + (None, Some(service_fee_check_summary)) => + write!( + f, + "Current service fee balance is not enough to pay a single payment. \ + Number of canceled payments: {}. Total amount required: {} wei, while the wallet \ + contains: {} wei", + number_of_accounts, + service_fee_check_summary.total_service_fee_required_minor.separate_with_commas(), + service_fee_check_summary.cw_service_fee_balance_minor.separate_with_commas()), + (Some(transaction_fee_check_summary), Some(service_fee_check_summary)) => + write!( + f, + "Neither transaction fee or service fee balance is enough to pay a single payment. \ + Number of payments considered: {}. Transaction fee per payment: {} wei, while in \ + wallet: {} wei. Total service fee required: {} wei, while in wallet: {} wei", + number_of_accounts, + transaction_fee_check_summary.per_transaction_requirement_minor.separate_with_commas(), + transaction_fee_check_summary.cw_transaction_fee_balance_minor.separate_with_commas(), + service_fee_check_summary.total_service_fee_required_minor.separate_with_commas(), + service_fee_check_summary.cw_service_fee_balance_minor.separate_with_commas() + ), + (None, None) => unreachable!("This error contains no specifications") + } + }, + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts, number_of_accounts, - total_service_fee_required_minor, + original_service_fee_required_total_minor, cw_service_fee_balance_minor, - transaction_fee_appendix_opt, - } => match transaction_fee_appendix_opt{ - None => write!( - f, - "Found service fee balance that is not enough for a single payment. Number of \ - canceled payments: {}. Total amount required: {} wei, while in wallet: {} wei", + } => write!(f, "The original set with {} accounts was adjusted down to {} due to \ + transaction fee. The new set was tested on service fee later again and did not \ + pass. Original required amount of service fee: {} wei, while the wallet \ + contains {} wei.", + original_number_of_accounts, number_of_accounts, - total_service_fee_required_minor.separate_with_commas(), - cw_service_fee_balance_minor.separate_with_commas()), - Some(limitation) => write!( - f, - "Both transaction fee and service fee balances are not enough. Number of payments \ - considered: {}. Current transaction fee balance can cover {} payments only. Transaction \ - fee per payment: {} wei, while in wallet: {} wei. Neither does the service fee balance \ - allow a single payment. Total amount required: {} wei, while in wallet: {} wei", - number_of_accounts, - limitation.count_limit, - limitation.per_transaction_required_fee_minor.separate_with_commas(), - limitation.cw_transaction_fee_balance_minor.separate_with_commas(), - total_service_fee_required_minor.separate_with_commas(), + original_service_fee_required_total_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() - ) - }, + ), PaymentAdjusterError::AllAccountsEliminated => write!( f, "The adjustment algorithm had to eliminate each payable from the recently urged \ @@ -535,7 +558,7 @@ mod tests { use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::SomeAccountsProcessed; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, TransactionFeeLimitation, WeightedPayable, + AdjustmentIterationResult, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; @@ -547,6 +570,7 @@ mod tests { }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -729,8 +753,8 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Your transaction fee balance 16,499,999,000,000,000 wei is not \ - going to cover the anticipated fees to send 3 transactions, with 6,325,000,000,000,000 \ + "WARN: {test_name}: Transaction fee balance of 16,499,999,000,000,000 wei is not \ + going to cover the anticipated fee to send 3 transactions, with 6,325,000,000,000,000 \ wei required per one. Maximum count is set to 2. Adjustment must be performed." )); log_handler.exists_log_containing(&format!( @@ -779,6 +803,43 @@ mod tests { )); } + #[test] + fn transaction_fee_balance_is_unbearably_low_but_service_fee_balance_is_fine() { + let subject = PaymentAdjusterReal::new(); + let number_of_accounts = 3; + let (qualified_payables, agent) = make_input_for_initial_check_tests( + Some(TestConfigForServiceFeeBalances { + account_balances: Either::Left(vec![123]), + cw_balance_minor: gwei_to_wei::(444), + }), + Some(TestConfigForTransactionFees { + agreed_transaction_fee_per_computed_unit_major: 100, + number_of_accounts, + estimated_transaction_fee_units_per_transaction: 55_000, + cw_transaction_fee_balance_major: 54_000 * 100, + }), + ); + + let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + + let per_transaction_requirement_minor = + TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(100)); + let cw_transaction_fee_balance_minor = U256::from(54_000 * gwei_to_wei::(100)); + assert_eq!( + result, + Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor, + }), + service_fee_opt: None + } + ) + ); + } + #[test] fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low() { @@ -798,44 +859,52 @@ mod tests { assert_eq!( result, Err( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 3, - total_service_fee_required_minor: 920_000_000_000, - cw_service_fee_balance_minor, - transaction_fee_appendix_opt: None, + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 920_000_000_000, + cw_service_fee_balance_minor + }) } ) ); } #[test] - fn not_enough_transaction_fee_balance_for_even_a_single_transaction() { + fn both_balances_are_unbearably_low() { let subject = PaymentAdjusterReal::new(); - let number_of_accounts = 3; + let number_of_accounts = 2; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![123]), - cw_balance_minor: gwei_to_wei::(444), + account_balances: Either::Left(vec![200, 300]), + cw_balance_minor: 0, }), Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 100, + agreed_transaction_fee_per_computed_unit_major: 123, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: 54_000 * 100, + cw_transaction_fee_balance_major: 0, }), ); let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let per_transaction_requirement_minor = + TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(123)); assert_eq!( result, Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts, - per_transaction_requirement_minor: TRANSACTION_FEE_MARGIN - .add_percent_to(55_000 * gwei_to_wei::(100)), - cw_transaction_fee_balance_minor: U256::from(54_000) - * gwei_to_wei::(100) + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor: U256::zero(), + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: multiple_by_billion(500), + cw_service_fee_balance_minor: 0 + }) } ) ); @@ -845,43 +914,59 @@ mod tests { fn payment_adjuster_error_implements_display() { let inputs = vec![ ( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 4, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ + per_transaction_requirement_minor: 70_000_000_000_000, + cw_transaction_fee_balance_minor: U256::from(90_000), + }), + service_fee_opt: None + }, + "Current transaction fee balance is not enough to pay a single payment. Number of \ + canceled payments: 4. Transaction fee per payment: 70,000,000,000,000 wei, while \ + the wallet contains: 90,000 wei", + ), + ( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 5, - total_service_fee_required_minor: 6_000_000_000, - cw_service_fee_balance_minor: 333_000_000, - transaction_fee_appendix_opt: None, + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency{ + total_service_fee_required_minor: 6_000_000_000, + cw_service_fee_balance_minor: 333_000_000, + }) }, - "Found service fee balance that is not enough for a single payment. Number of \ - canceled payments: 5. Total amount required: 6,000,000,000 wei, while in wallet: \ - 333,000,000 wei", + "Current service fee balance is not enough to pay a single payment. Number of \ + canceled payments: 5. Total amount required: 6,000,000,000 wei, while the wallet \ + contains: 333,000,000 wei", ), ( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 5, - total_service_fee_required_minor: 7_000_000_000, - cw_service_fee_balance_minor: 100_000_000, - transaction_fee_appendix_opt: Some(TransactionFeeLimitation { - count_limit: 3, - cw_transaction_fee_balance_minor: 3_000_000_000, - per_transaction_required_fee_minor: 5_000_000_000, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ + per_transaction_requirement_minor: 5_000_000_000, + cw_transaction_fee_balance_minor: U256::from(3_000_000_000_u64) }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency{ + total_service_fee_required_minor: 7_000_000_000, + cw_service_fee_balance_minor: 100_000_000 + }) }, - "Both transaction fee and service fee balances are not enough. Number of payments \ - considered: 5. Current transaction fee balance can cover 3 payments only. \ - Transaction fee per payment: 5,000,000,000 wei, while in wallet: 3,000,000,000 \ - wei. Neither does the service fee balance allow a single payment. Total amount \ - required: 7,000,000,000 wei, while in wallet: 100,000,000 wei", + "Neither transaction fee or service fee balance is enough to pay a single payment. \ + Number of payments considered: 5. Transaction fee per payment: 5,000,000,000 wei, \ + while in wallet: 3,000,000,000 wei. Total service fee required: 7,000,000,000 wei, \ + while in wallet: 100,000,000 wei", ), ( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: 4, - per_transaction_requirement_minor: 70_000_000_000_000, - cw_transaction_fee_balance_minor: U256::from(90_000), + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts: 6, + number_of_accounts: 3, + original_service_fee_required_total_minor: 1234567891011, + cw_service_fee_balance_minor: 333333, }, - "Found transaction fee balance that is not enough for a single payment. Number of \ - canceled payments: 4. Transaction fee per payment: 70,000,000,000,000 wei, while in \ - wallet: 90,000 wei", - ), + "The original set with 6 accounts was adjusted down to 3 due to transaction fee. \ + The new set was tested on service fee later again and did not pass. Original \ + required amount of service fee: 1,234,567,891,011 wei, while the wallet contains \ + 333,333 wei."), ( PaymentAdjusterError::AllAccountsEliminated, "The adjustment algorithm had to eliminate each payable from the recently urged \ @@ -892,23 +977,59 @@ mod tests { inputs .into_iter() .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)); - assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 1) + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 2) + } + + #[test] + #[should_panic( + expected = "internal error: entered unreachable code: This error contains no \ + specifications" + )] + fn error_message_for_input_referring_to_no_issues_cannot_be_made() { + let _ = PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 0, + transaction_fee_opt: None, + service_fee_opt: None, + } + .to_string(); } #[test] fn we_can_say_if_error_occurred_after_insolvency_was_detected() { let inputs = vec![ PaymentAdjusterError::AllAccountsEliminated, - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 0, - total_service_fee_required_minor: 0, - cw_service_fee_balance_minor: 0, - transaction_fee_appendix_opt: None, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 0, + cw_transaction_fee_balance_minor: Default::default(), + }), + service_fee_opt: None, + }, + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 0, + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + }), + }, + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 0, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 0, + cw_transaction_fee_balance_minor: Default::default(), + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + }), }, - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts: 0, number_of_accounts: 0, - per_transaction_requirement_minor: 0, - cw_transaction_fee_balance_minor: Default::default(), + original_service_fee_required_total_minor: 0, + cw_service_fee_balance_minor: 0, }, ]; let inputs_count = inputs.len(); @@ -916,8 +1037,8 @@ mod tests { .into_iter() .map(|err| err.insolvency_detected()) .collect::>(); - assert_eq!(results, vec![true, true, true]); - assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT) + assert_eq!(results, vec![true, true, true, true, true]); + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 2) } #[test] @@ -1701,11 +1822,11 @@ mod tests { let disqualification_limit_2 = disqualification_arbiter.calculate_disqualification_edge(&account_2); // This is exactly the amount which will provoke an error - let service_fee_balance_in_minor_units = disqualification_limit_2 - 1; + let cw_service_fee_balance_minor = disqualification_limit_2 - 1; let qualified_payables = vec![account_1, account_2, account_3]; let analyzed_payables = convert_collection(qualified_payables); let agent = BlockchainAgentMock::default() - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(cw_service_fee_balance_minor); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysis::new( @@ -1725,11 +1846,11 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: 3, - total_service_fee_required_minor: balance_1 + balance_2 + balance_3, - cw_service_fee_balance_minor: service_fee_balance_in_minor_units, - transaction_fee_appendix_opt: None, + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts: 3, + number_of_accounts: 2, + original_service_fee_required_total_minor: balance_1 + balance_2 + balance_3, + cw_service_fee_balance_minor } ); TestLogHandler::new().assert_logs_contain_in_order(vec![ diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index c90bef0a7..118e085e4 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -140,17 +140,12 @@ fn loading_test_with_randomized_params() { }; Some(scenario) } - Err( - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - .. - }, - ) => { + Err(_) => { output_collector .test_overall_output_collector .scenarios_denied_before_adjustment_started += 1; None } - _e => Some(scenario), }; match allowed_scenario_opt { @@ -572,7 +567,7 @@ fn render_results_to_file_and_attempt_basic_assertions( .scenarios_denied_before_adjustment_started + test_overall_output_collector.oks + test_overall_output_collector.all_accounts_eliminated - + test_overall_output_collector.insufficient_service_fee_balance; + + test_overall_output_collector.late_immoderately_insufficient_service_fee_balance; write_brief_test_summary_into_file( &mut file, &test_overall_output_collector, @@ -674,7 +669,7 @@ fn write_brief_test_summary_into_file( .render_in_two_lines(), overall_output_collector.scenarios_denied_before_adjustment_started, overall_output_collector.all_accounts_eliminated, - overall_output_collector.insufficient_service_fee_balance, + overall_output_collector.late_immoderately_insufficient_service_fee_balance, NON_EXHAUSTED_ACCOUNT_MARKER )) .unwrap() @@ -711,12 +706,12 @@ fn do_final_processing_of_single_scenario( } Err(negative) => { match negative.adjuster_error { - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { .. } => { - panic!("impossible in this kind of test without the tx fee initial check") + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => { + panic!("Such errors should be already filtered out") + } + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => { + test_overall_output.late_immoderately_insufficient_service_fee_balance += 1 } - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - .. - } => test_overall_output.insufficient_service_fee_balance += 1, PaymentAdjusterError::AllAccountsEliminated => { test_overall_output.all_accounts_eliminated += 1 } @@ -1027,7 +1022,7 @@ struct TestOverallOutputCollector { fulfillment_distribution_for_service_fee_adjustments: PercentageFulfillmentDistribution, // Errors all_accounts_eliminated: usize, - insufficient_service_fee_balance: usize, + late_immoderately_insufficient_service_fee_balance: usize, } impl TestOverallOutputCollector { @@ -1040,7 +1035,7 @@ impl TestOverallOutputCollector { fulfillment_distribution_for_transaction_fee_adjustments: Default::default(), fulfillment_distribution_for_service_fee_adjustments: Default::default(), all_accounts_eliminated: 0, - insufficient_service_fee_balance: 0, + late_immoderately_insufficient_service_fee_balance: 0, } } } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 87f0e60cd..1b139a1e1 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -8,8 +8,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentPossibilityErrorBuilder, TransactionCountsBy16bits, TransactionFeeLimitation, - TransactionFeePastCheckContext, WeightedPayable, + TransactionCountsBy16bits, WeightedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_smallest_u128, sum_as, @@ -17,7 +16,10 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; -use crate::accountant::payment_adjuster::{Adjustment, AdjustmentAnalysis, PaymentAdjusterError}; +use crate::accountant::payment_adjuster::{ + Adjustment, AdjustmentAnalysis, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, + TransactionFeeImmoderateInsufficiency, +}; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; @@ -40,20 +42,20 @@ impl PreparatoryAnalyzer { logger: &Logger, ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> { - let number_of_counts = qualified_payables.len(); + let number_of_accounts = qualified_payables.len(); let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction_minor(); let agreed_transaction_fee_margin = agent.agreed_transaction_fee_margin(); - let transaction_fee_limitation_opt = self + let transaction_fee_check_result = self .determine_transaction_count_limit_by_transaction_fee( cw_transaction_fee_balance_minor, agreed_transaction_fee_margin, per_transaction_requirement_minor, - number_of_counts, + number_of_accounts, logger, - )?; + ); let cw_service_fee_balance_minor = agent.service_fee_balance_minor(); let is_service_fee_adjustment_needed = Self::is_service_fee_adjustment_needed( @@ -62,39 +64,76 @@ impl PreparatoryAnalyzer { logger, ); - if transaction_fee_limitation_opt.is_none() && !is_service_fee_adjustment_needed { - Ok(Either::Left(qualified_payables)) + if matches!(transaction_fee_check_result, Ok(None)) && !is_service_fee_adjustment_needed { + return Ok(Either::Left(qualified_payables)); + } + + let prepared_accounts = Self::pre_process_accounts_for_adjustments( + qualified_payables, + disqualification_arbiter, + ); + + let service_fee_check_result = if is_service_fee_adjustment_needed { + let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); + + Self::check_adjustment_possibility( + &prepared_accounts, + cw_service_fee_balance_minor, + error_factory, + ) } else { - let prepared_accounts = Self::pre_process_accounts_for_adjustments( - qualified_payables, - disqualification_arbiter, - ); - if is_service_fee_adjustment_needed { - let error_builder = AdjustmentPossibilityErrorBuilder::default().context( - TransactionFeePastCheckContext::initial_check_done( - transaction_fee_limitation_opt, - ), - ); - - Self::check_adjustment_possibility( - &prepared_accounts, - cw_service_fee_balance_minor, - error_builder, - )? - }; - let adjustment = match transaction_fee_limitation_opt { - None => Adjustment::ByServiceFee, - Some(limitation) => { - let affordable_transaction_count = limitation.count_limit; - Adjustment::TransactionFeeInPriority { - affordable_transaction_count, - } - } - }; - Ok(Either::Right(AdjustmentAnalysis::new( - adjustment, - prepared_accounts, - ))) + Ok(()) + }; + + let transaction_fee_limitation_opt = Self::handle_errors_if_present( + number_of_accounts, + transaction_fee_check_result, + service_fee_check_result, + )?; + + let adjustment = match transaction_fee_limitation_opt { + None => Adjustment::ByServiceFee, + Some(affordable_transaction_count) => Adjustment::TransactionFeeInPriority { + affordable_transaction_count, + }, + }; + + Ok(Either::Right(AdjustmentAnalysis::new( + adjustment, + prepared_accounts, + ))) + } + + fn handle_errors_if_present( + number_of_accounts: usize, + transaction_fee_check_result: Result, TransactionFeeImmoderateInsufficiency>, + service_fee_check_result: Result<(), ServiceFeeImmoderateInsufficiency>, + ) -> Result, PaymentAdjusterError> { + match (transaction_fee_check_result, service_fee_check_result) { + (Err(transaction_fee_check_error), Ok(_)) => Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts, + transaction_fee_opt: Some(transaction_fee_check_error), + service_fee_opt: None, + }, + ), + (Err(transaction_fee_check_error), Err(service_fee_check_error)) => Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts, + transaction_fee_opt: Some(transaction_fee_check_error), + service_fee_opt: Some(service_fee_check_error), + }, + ), + (Ok(_), Err(service_fee_check_error)) => Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts, + transaction_fee_opt: None, + service_fee_opt: Some(service_fee_check_error), + }, + ), + (Ok(transaction_fee_limiting_count_opt), Ok(())) => { + Ok(transaction_fee_limiting_count_opt) + } } } @@ -102,7 +141,7 @@ impl PreparatoryAnalyzer { &self, weighted_accounts: &[WeightedPayable], cw_service_fee_balance_minor: u128, - error_builder: AdjustmentPossibilityErrorBuilder, + error_factory: LateServiceFeeSingleTxErrorFactory, logger: &Logger, ) -> Result { if Self::is_service_fee_adjustment_needed( @@ -113,7 +152,7 @@ impl PreparatoryAnalyzer { if let Err(e) = Self::check_adjustment_possibility( weighted_accounts, cw_service_fee_balance_minor, - error_builder, + error_factory, ) { log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger); Err(e) @@ -132,13 +171,13 @@ impl PreparatoryAnalyzer { per_transaction_requirement_minor: u128, number_of_qualified_accounts: usize, logger: &Logger, - ) -> Result, PaymentAdjusterError> { - let txn_fee_required_per_txn_minor_with_margin = + ) -> Result, TransactionFeeImmoderateInsufficiency> { + let per_txn_requirement_minor_with_margin = agreed_transaction_fee_margin.add_percent_to(per_transaction_requirement_minor); let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, - txn_fee_required_per_txn_minor_with_margin, + per_txn_requirement_minor_with_margin, number_of_qualified_accounts, ); @@ -146,29 +185,22 @@ impl PreparatoryAnalyzer { let required_tx_count: u16 = verified_tx_counts.required; if max_tx_count_we_can_afford == 0 { - Err( - PaymentAdjusterError::NotEnoughTransactionFeeBalanceForSingleTx { - number_of_accounts: number_of_qualified_accounts, - per_transaction_requirement_minor: txn_fee_required_per_txn_minor_with_margin, - cw_transaction_fee_balance_minor, - }, - ) + Err(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: per_txn_requirement_minor_with_margin, + cw_transaction_fee_balance_minor, + }) } else if max_tx_count_we_can_afford >= required_tx_count { Ok(None) } else { log_insufficient_transaction_fee_balance( logger, required_tx_count, - txn_fee_required_per_txn_minor_with_margin, + per_txn_requirement_minor_with_margin, cw_transaction_fee_balance_minor, max_tx_count_we_can_afford, ); - let transaction_fee_limitation_opt = TransactionFeeLimitation::new( - max_tx_count_we_can_afford, - cw_transaction_fee_balance_minor.as_u128(), - txn_fee_required_per_txn_minor_with_margin, - ); - Ok(Some(transaction_fee_limitation_opt)) + + Ok(Some(max_tx_count_we_can_afford)) } } @@ -183,13 +215,14 @@ impl PreparatoryAnalyzer { TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) } - fn check_adjustment_possibility( + fn check_adjustment_possibility( prepared_accounts: &[AnalyzableAccounts], cw_service_fee_balance_minor: u128, - error_builder: AdjustmentPossibilityErrorBuilder, - ) -> Result<(), PaymentAdjusterError> + service_fee_error_factory: ErrorFactory, + ) -> Result<(), Error> where AnalyzableAccounts: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, + ErrorFactory: ServiceFeeSingleTXErrorFactory, { let lowest_disqualification_limit = Self::find_lowest_disqualification_limit(prepared_accounts); @@ -204,13 +237,11 @@ impl PreparatoryAnalyzer { let analyzed_accounts_count = prepared_accounts.len(); let required_service_fee_total = Self::compute_total_of_service_fee_required(prepared_accounts); - let err = error_builder - .all_time_supplied_parameters( - analyzed_accounts_count, - required_service_fee_total, - cw_service_fee_balance_minor, - ) - .build(); + let err = service_fee_error_factory.make( + analyzed_accounts_count, + required_service_fee_total, + cw_service_fee_balance_minor, + ); Err(err) } } @@ -270,21 +301,84 @@ impl PreparatoryAnalyzer { } } +pub trait ServiceFeeSingleTXErrorFactory { + fn make( + &self, + number_of_accounts: usize, + required_service_fee_total: u128, + cw_service_fee_balance_minor: u128, + ) -> E; +} + +#[derive(Default)] +pub struct EarlyServiceFeeSingleTXErrorFactory {} + +impl ServiceFeeSingleTXErrorFactory + for EarlyServiceFeeSingleTXErrorFactory +{ + fn make( + &self, + _number_of_accounts: usize, + total_service_fee_required_minor: u128, + cw_service_fee_balance_minor: u128, + ) -> ServiceFeeImmoderateInsufficiency { + ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor, + cw_service_fee_balance_minor, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct LateServiceFeeSingleTxErrorFactory { + original_number_of_accounts: usize, + original_service_fee_required_total_minor: u128, +} + +impl LateServiceFeeSingleTxErrorFactory { + pub fn new(unadjusted_accounts: &[WeightedPayable]) -> Self { + let original_number_of_accounts = unadjusted_accounts.len(); + let original_service_fee_required_total_minor = + sum_as(unadjusted_accounts, |account| account.balance_minor()); + Self { + original_number_of_accounts, + original_service_fee_required_total_minor, + } + } +} + +impl ServiceFeeSingleTXErrorFactory for LateServiceFeeSingleTxErrorFactory { + fn make( + &self, + number_of_accounts: usize, + _required_service_fee_total: u128, + cw_service_fee_balance_minor: u128, + ) -> PaymentAdjusterError { + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts: self.original_number_of_accounts, + number_of_accounts, + original_service_fee_required_total_minor: self + .original_service_fee_required_total_minor, + cw_service_fee_balance_minor, + } + } +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentPossibilityErrorBuilder, TransactionFeeLimitation, TransactionFeePastCheckContext, - }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; - use crate::accountant::payment_adjuster::preparatory_analyser::PreparatoryAnalyzer; + use crate::accountant::payment_adjuster::preparatory_analyser::{ + EarlyServiceFeeSingleTXErrorFactory, LateServiceFeeSingleTxErrorFactory, + PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, + }; use crate::accountant::payment_adjuster::test_utils::{ make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjusterError, + Adjustment, AdjustmentAnalysis, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::test_utils::{ @@ -295,6 +389,7 @@ mod tests { use itertools::Either; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use std::fmt::Debug; use std::sync::{Arc, Mutex}; use thousands::Separable; use web3::types::U256; @@ -400,11 +495,13 @@ mod tests { ) } - fn test_not_enough_for_even_the_least_demanding_account_causes_error( - error_builder: AdjustmentPossibilityErrorBuilder, + fn test_not_enough_for_even_the_least_demanding_account_causes_error( + error_factory: ErrorFactory, expected_error_preparer: F, ) where - F: FnOnce(u128, u128) -> PaymentAdjusterError, + F: FnOnce(usize, u128, u128) -> Error, + ErrorFactory: ServiceFeeSingleTXErrorFactory, + Error: Debug + PartialEq, { let mut account_1 = make_analyzed_account(111); account_1.qualified_as.bare_account.balance_wei = 2_000_000_000; @@ -416,89 +513,64 @@ mod tests { account_3.qualified_as.bare_account.balance_wei = 1_000_111_111; account_3.disqualification_limit_minor = 1_000_000_222; let cw_service_fee_balance = 1_000_000_100; - let original_accounts = vec![account_1, account_2, account_3]; + let supplied_accounts = vec![account_1, account_2, account_3]; + let supplied_accounts_count = supplied_accounts.len(); let service_fee_total_of_the_known_set = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; let result = PreparatoryAnalyzer::check_adjustment_possibility( - &original_accounts, + &supplied_accounts, cw_service_fee_balance, - error_builder, + error_factory, ); - let expected_error = - expected_error_preparer(service_fee_total_of_the_known_set, cw_service_fee_balance); + let expected_error = expected_error_preparer( + supplied_accounts_count, + service_fee_total_of_the_known_set, + cw_service_fee_balance, + ); assert_eq!(result, Err(expected_error)) } #[test] fn not_enough_for_even_the_least_demanding_account_error_right_after_positive_tx_fee_check() { - let transaction_fee_limitation = TransactionFeeLimitation { - count_limit: 2, - cw_transaction_fee_balance_minor: 200_000_000, - per_transaction_required_fee_minor: 300_000_000, - }; - let error_builder = AdjustmentPossibilityErrorBuilder::default().context( - TransactionFeePastCheckContext::initial_check_done(Some(transaction_fee_limitation)), - ); - let expected_error_preparer = - |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: 3, - total_service_fee_required_minor: total_amount_demanded_in_accounts_in_place, - cw_service_fee_balance_minor, - transaction_fee_appendix_opt: Some(transaction_fee_limitation), - } - }; - - test_not_enough_for_even_the_least_demanding_account_causes_error( - error_builder, - expected_error_preparer, - ) - } + let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); - #[test] - fn not_enough_for_even_the_least_demanding_account_error_right_after_negative_tx_fee_check() { - let error_builder = AdjustmentPossibilityErrorBuilder::default() - .context(TransactionFeePastCheckContext::initial_check_done(None)); let expected_error_preparer = - |total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: 3, + |_, total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { + ServiceFeeImmoderateInsufficiency { total_service_fee_required_minor: total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor, - transaction_fee_appendix_opt: None, } }; test_not_enough_for_even_the_least_demanding_account_causes_error( - error_builder, + error_factory, expected_error_preparer, ) } #[test] fn not_enough_for_even_the_least_demanding_account_error_right_after_tx_fee_accounts_dump() { - let accounts = vec![ + let original_accounts = vec![ make_weighed_account(123), make_weighed_account(456), make_weighed_account(789), make_weighed_account(1011), ]; - let initial_sum = sum_as(&accounts, |account| account.balance_minor()); - let initial_count = accounts.len(); - let error_builder = AdjustmentPossibilityErrorBuilder::default() - .context(TransactionFeePastCheckContext::accounts_dumped(&accounts)); - let expected_error_preparer = |_, cw_service_fee_balance_minor| { - PaymentAdjusterError::NotEnoughServiceFeeBalanceEvenForTheSmallestTransaction { - number_of_accounts: initial_count, - total_service_fee_required_minor: initial_sum, + let original_number_of_accounts = original_accounts.len(); + let initial_sum = sum_as(&original_accounts, |account| account.balance_minor()); + let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); + let expected_error_preparer = |number_of_accounts, _, cw_service_fee_balance_minor| { + PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + original_number_of_accounts, + number_of_accounts, + original_service_fee_required_total_minor: initial_sum, cw_service_fee_balance_minor, - transaction_fee_appendix_opt: None, } }; test_not_enough_for_even_the_least_demanding_account_causes_error( - error_builder, + error_factory, expected_error_preparer, ) } @@ -524,7 +596,7 @@ mod tests { let accounts = vec![weighted_account_1, weighted_account_2]; let service_fee_totally_required_minor = balance_1 + balance_2; let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1; - let error_builder = AdjustmentPossibilityErrorBuilder::default(); + let error_factory = LateServiceFeeSingleTxErrorFactory::new(&accounts); let logger = Logger::new(test_name); let subject = PreparatoryAnalyzer::new(); @@ -535,7 +607,7 @@ mod tests { .recheck_if_service_fee_adjustment_is_needed( &accounts, service_fee_balance, - error_builder.clone(), + error_factory.clone(), &logger, ) .unwrap(); @@ -549,6 +621,35 @@ mod tests { )); } + #[test] + fn construction_of_error_context_with_accounts_dumped_works() { + let balance_1 = 1234567; + let mut account_1 = make_weighed_account(123); + account_1 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = balance_1; + let balance_2 = 999888777; + let mut account_2 = make_weighed_account(345); + account_2 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = balance_2; + let weighted_accounts = vec![account_1, account_2]; + + let result = LateServiceFeeSingleTxErrorFactory::new(&weighted_accounts); + + assert_eq!( + result, + LateServiceFeeSingleTxErrorFactory { + original_number_of_accounts: 2, + original_service_fee_required_total_minor: balance_1 + balance_2 + } + ) + } + fn double_mock_results_queue(mock: DisqualificationGaugeMock) -> DisqualificationGaugeMock { let originally_prepared_results = (0..2) .map(|_| mock.determine_limit(0, 0, 0)) From dc4d63b18742bac6675488e2885fc070314231b8 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 13 Sep 2024 21:58:58 +0200 Subject: [PATCH 194/250] GH-711: first fixes from the review...finalizing 'Percentage' --- dns_utility/dns_utility.iml | 2 + masq_lib/src/percentage.rs | 647 ++++++++++++++++++++++++++++-------- node/Cargo.lock | 62 ++-- 3 files changed, 544 insertions(+), 167 deletions(-) diff --git a/dns_utility/dns_utility.iml b/dns_utility/dns_utility.iml index 7bcd61c17..7c3466207 100644 --- a/dns_utility/dns_utility.iml +++ b/dns_utility/dns_utility.iml @@ -5,8 +5,10 @@ + + diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 20c03f14a..29a43a38f 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -1,76 +1,227 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use num::integer::mod_floor; +use nix::libc::sigaction; use num::CheckedAdd; use num::CheckedSub; use num::{CheckedDiv, CheckedMul, Integer}; use std::any::type_name; use std::fmt::Debug; -use std::ops::Div; use std::ops::Mul; +use std::ops::{Div, Rem}; +// Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage +// operations over a wide variety of integer types. It is also to look after the least significant +// digit on the resulted number in order to avoid the effect of a loss on precision that genuinely +// comes with division on integers if a remainder is left over. -// It's designed for a storage of values from 0 to 100, after which it can be used to compute -// the corresponding portion of many integer types. It should also take care of the least significant -// digit in order to diminish the effect of a precision loss genuinly implied by this kind of math -// operations done on integers. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PurePercentage { + degree: u8, +} #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Percentage { - per_cent: u8, +pub struct LoosePercentage { + multiples_of_100_percent: u32, + degrees_from_remainder: PurePercentage, +} + +pub trait PercentageInteger: + TryFrom + + CheckedMul + + CheckedAdd + + CheckedSub + + CheckedDiv + + PartialOrd + + Rem + + Integer + + Debug + + Copy +{ } -impl Percentage { - pub fn new(num: u8) -> Self { - match num { - 0..=100 => Self { per_cent: num }, - x => panic!("Accepts only range from 0 to 100 but {} was supplied", x), +macro_rules! impl_percentage_integer { + ($($num_type: ty),+) => { + $(impl PercentageInteger for $num_type {})+ + } +} + +impl_percentage_integer!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +impl LoosePercentage { + pub fn new(percents: u32) -> Self { + let multiples_of_100_percent = percents / 100; + let remainder = (percents % 100) as u8; + let degrees_from_remainder = + PurePercentage::try_from(remainder).expect("should never happen"); + Self { + multiples_of_100_percent, + degrees_from_remainder, } } + // If this overflows you probably want to precede the operation by converting your bas number + // to a larger integer type + pub fn of(&self, num: N) -> Result + where + N: PercentageInteger, + >::Error: Debug, + N: TryFrom, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, + { + let multiples = match N::try_from(self.multiples_of_100_percent) { + Ok(num) => num, + Err(e) => todo!(), + }; + + let by_wholes = match num.checked_mul(&multiples) { + Some(num) => num, + None => todo!(), + }; + + let by_remainder = self.degrees_from_remainder.of(num); + + match by_wholes.checked_add(&by_remainder) { + Some(res) => Ok(res), + None => todo!(), + } + } +} + +#[derive(Debug)] +pub enum OverflowError {} + +impl TryFrom for PurePercentage { + type Error = String; + + fn try_from(degree: u8) -> Result { + match degree { + 0..=100 => Ok(Self { degree }), + x => Err(format!( + "Accepts only range from 0 to 100 but {} was supplied", + x + )), + } + } +} + +impl PurePercentage { pub fn of(&self, num: N) -> N where - N: From + CheckedMul + CheckedAdd + CheckedDiv + PartialOrd + Integer + Debug + Copy, + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, { - let zero = N::from(0); - if num == zero || N::from(self.per_cent) == zero { + if let Some(zero) = self.return_zero(num) { return zero; } - let a = match N::from(self.per_cent).checked_mul(&num) { + let product_before_final_div = match N::try_from(self.degree as i8) + .expect("Each type has 100") + .checked_mul(&num) + { Some(num) => num, None => return self.handle_upper_overflow(num), }; - if a < N::from(10) { - return N::from(0); + Self::div_by_100_and_round(product_before_final_div) + } + + fn return_zero(&self, num: N) -> Option + where + N: PercentageInteger, + >::Error: Debug, + { + let zero = N::try_from(0).expect("Each type has 0"); + if num == zero || N::try_from(self.degree as i8).expect("Each type has 100") == zero { + Some(zero) + } else { + None + } + } + + fn div_by_100_and_round(num: N) -> N + where + N: PercentageInteger, + >::Error: Debug, + { + let divisor = N::try_from(100).expect("Each type has 100"); + let rounded_rule = Self::should_be_rounded_as(num, divisor); + let significant_digits_only = num.checked_div(&divisor).expect("Division failed"); + + macro_rules! adjust_num { + ($significant_digits: expr, $method_add_or_sub: ident, $msg_in_expect: literal) => { + $significant_digits + .$method_add_or_sub(&N::try_from(1).expect("Each type has 1")) + .expect($msg_in_expect) + }; + } + + match rounded_rule { + RoundingRule::ToBiggerPositive => { + adjust_num!(significant_digits_only, checked_add, "Addition failed") + } + RoundingRule::ToBiggerNegative => { + adjust_num!(significant_digits_only, checked_sub, "Subtraction failed") + } + RoundingRule::ToSmallerNegative | RoundingRule::ToSmallerPositive => { + significant_digits_only + } } + } - let rounding = if Percentage::should_be_rounded_down(a) { - N::from(0) + fn should_be_rounded_as(num: N, divisor: N) -> RoundingRule + where + N: PercentageInteger, + >::Error: Debug, + { + let least_significant_digits: N = num % divisor; + let is_signed = num < N::try_from(0).expect("Each type has 0"); + let divider = N::try_from(50).expect("Each type has 50"); + let abs_of_significant_digits = + Self::abs_of_least_significant_digits(least_significant_digits, is_signed); + let is_minor: bool = if abs_of_significant_digits == divider { + false + } else if abs_of_significant_digits > divider { + false } else { - N::from(1) + true }; + match (is_signed, is_minor) { + (false, true) => RoundingRule::ToSmallerPositive, + (false, false) => RoundingRule::ToBiggerPositive, + (true, true) => RoundingRule::ToSmallerNegative, + (true, false) => RoundingRule::ToBiggerNegative, + } + } - let hundred = N::from(100); - - if a < hundred { - rounding + fn abs_of_least_significant_digits(least_significant_digits: N, is_signed: bool) -> N + where + N: TryFrom + CheckedMul, + >::Error: Debug, + { + if is_signed { + N::try_from(-1) + .expect("Negative 1 must be possible for a confirmed signed integer") + .checked_mul(&least_significant_digits) + .expect("Must be possible in these low values") } else { - a.checked_div(&hundred) - .expect("div failed") - .checked_add(&rounding) - .expect("rounding failed") + least_significant_digits } } pub fn add_percent_to(&self, num: N) -> N where - N: From + CheckedMul + CheckedAdd + CheckedDiv + PartialOrd + Integer + Debug + Copy, + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, { self.of(num).checked_add(&num).unwrap_or_else(|| { panic!( - "Overflowed during addition of {} per cent, that is {:?}, to {:?} of type {}.", - self.per_cent, + "Overflowed during addition of {} percent, that is {:?}, to {:?} of type {}.", + self.degree, self.of(num), num, type_name::() @@ -80,178 +231,402 @@ impl Percentage { pub fn subtract_percent_from(&self, num: N) -> N where - N: From - + CheckedMul - + CheckedAdd - + CheckedSub - + CheckedDiv - + PartialOrd - + Integer - + Debug - + Copy, + N: PercentageInteger + CheckedSub, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, { num.checked_sub(&self.of(num)) .expect("should never happen by its principle") } - fn should_be_rounded_down(num: N) -> bool + fn handle_upper_overflow(&self, num: N) -> N where - N: From + PartialEq + PartialOrd + Mul + CheckedMul + Integer + Copy, + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, { - let ten = N::from(10); - let upper_limit = ten * ten; - let enough_limit = ten; - if num == upper_limit { - true - } else if num >= enough_limit { - let modulo = mod_floor(num, upper_limit); - modulo - < N::from(5) - .checked_mul(&ten) - .expect("Couldn't create limit to compare with") - } else { - unreachable!("Check to prevent numbers with fewer than two digits failed") - } + let hundred = N::try_from(100).expect("Each type has 100"); + let modulo = num % hundred; + let percent = N::try_from(self.degree as i8).expect("Each type has 100"); + + let without_treated_remainder = (num / hundred) * percent; + let final_remainder_treatment = Self::treat_remainder(modulo, percent); + without_treated_remainder + final_remainder_treatment } - fn handle_upper_overflow(&self, num: N) -> N + fn treat_remainder(modulo: N, percent: N) -> N where - N: From + Div + Mul, + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, { - num / From::from(100) * N::from(self.per_cent) + let extended_remainder_prepared_for_rounding = i16::try_from(modulo) + .unwrap_or_else(|_| panic!("u16 from -100..=100 failed at modulo {:?}", modulo)) + * i16::try_from(percent).expect("i16 from within 0..=100 failed at multiplier"); + let rounded = Self::div_by_100_and_round(extended_remainder_prepared_for_rounding); + N::try_from(rounded as i8).expect("Each type has 0 up to 100") } } +#[derive(Debug, PartialEq, Eq)] +enum RoundingRule { + ToBiggerPositive, + ToBiggerNegative, + ToSmallerPositive, + ToSmallerNegative, +} + #[cfg(test)] mod tests { - use crate::percentage::Percentage; - use std::panic::catch_unwind; + use crate::percentage::{LoosePercentage, PercentageInteger, PurePercentage, RoundingRule}; + use std::fmt::Debug; + use std::ops::RangeInclusive; + + #[test] + fn percentage_is_implemented_for_all_rust_integers() { + let subject = PurePercentage::try_from(50).unwrap(); + + assert_integer_compatibility(&subject, u8::MAX, 128); + assert_integer_compatibility(&subject, u16::MAX, 32768); + assert_integer_compatibility(&subject, u32::MAX, 2147483648); + assert_integer_compatibility(&subject, u64::MAX, 9223372036854775808); + assert_integer_compatibility(&subject, u128::MAX, 170141183460469231731687303715884105728); + assert_integer_compatibility(&subject, i8::MIN, -64); + assert_integer_compatibility(&subject, i16::MIN, -16384); + assert_integer_compatibility(&subject, i32::MIN, -1073741824); + assert_integer_compatibility(&subject, i64::MIN, -4611686018427387904); + assert_integer_compatibility(&subject, i128::MIN, -85070591730234615865843651857942052864); + } + + fn assert_integer_compatibility(subject: &PurePercentage, num: N, expected: N) + where + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, + { + assert_eq!(subject.of(num), expected); + let half = num / N::try_from(2).unwrap(); + let one = N::try_from(1).unwrap(); + assert!((half - one) <= half && half <= (half + one)) + } #[test] - fn zero() { - assert_eq!(Percentage::new(45).of(0), 0); - assert_eq!(Percentage::new(0).of(33), 0) + fn zeros_for_pure_percentage() { + assert_eq!(PurePercentage::try_from(45).unwrap().of(0), 0); + assert_eq!(PurePercentage::try_from(0).unwrap().of(33), 0) } #[test] - fn end_to_end_test() { - let range = 0..=100; + fn pure_percentage_end_to_end_test_for_unsigned() { + let expected_values = (0..=100).collect::>(); + + test_end_to_end(100, expected_values, |percent, base| { + PurePercentage::try_from(percent).unwrap().of(base) + }) + } + + #[test] + fn pure_percentage_end_to_end_test_for_signed() { + let expected_values = (-100..=0).rev().collect::>(); + + test_end_to_end(-100, expected_values, |percent, base| { + PurePercentage::try_from(percent).unwrap().of(base) + }) + } + + fn test_end_to_end( + base: i8, + expected_values: Vec, + create_percentage_and_apply_it_on_number: F, + ) where + F: Fn(u8, i8) -> i8, + { + let range = 0_u8..=100; let round_returned_range = range - .clone() .into_iter() - .map(|per_cent| Percentage::new(per_cent).of(100_u64)) - .collect::>(); + .map(|percent| create_percentage_and_apply_it_on_number(percent, base)) + .collect::>(); - let expected = range - .into_iter() - .map(|num| num as u64) - .collect::>(); - assert_eq!(round_returned_range, expected) + assert_eq!(round_returned_range, expected_values) } #[test] fn only_numbers_up_to_100_are_accepted() { (101..=u8::MAX) - .map(|num| { - ( - catch_unwind(|| Percentage::new(num)).expect_err("expected panic"), - num, - ) - }) - .map(|(panic, num)| { - ( - panic - .downcast_ref::() - .expect("couldn't downcast to String") - .to_owned(), - num, - ) - }) - .for_each(|(panic_msg, num)| { + .map(|num| (PurePercentage::try_from(num), num)) + .for_each(|(res, num)| { assert_eq!( - panic_msg, - format!("Accepts only range from 0 to 100 but {} was supplied", num) + res, + Err(format!( + "Accepts only range from 0 to 100 but {} was supplied", + num + )) ) }); } + struct Case { + requested_percent: u32, + examined_base_number: i64, + expected_result: i64, + } + #[test] fn too_low_values() { - vec![((10, 1), 0), ((9, 1), 0), ((5, 14), 1), ((55, 40), 22)] - .into_iter() - .for_each(|((per_cent, examined_number), expected_result)| { - let result = Percentage::new(per_cent).of(examined_number); - assert_eq!( - result, expected_result, - "For {} per cent and number {} the expected result was {} but we got {}", - per_cent, examined_number, expected_result, result - ) - }) + vec![ + Case { + requested_percent: 49, + examined_base_number: 1, + expected_result: 0, + }, + Case { + requested_percent: 9, + examined_base_number: 1, + expected_result: 0, + }, + Case { + requested_percent: 5, + examined_base_number: 14, + expected_result: 1, + }, + Case { + requested_percent: 55, + examined_base_number: 41, + expected_result: 23, + }, + Case { + requested_percent: 55, + examined_base_number: 40, + expected_result: 22, + }, + ] + .into_iter() + .for_each(|case| { + let result = PurePercentage::try_from(u8::try_from(case.requested_percent).unwrap()) + .unwrap() + .of(case.examined_base_number); + assert_eq!( + result, case.expected_result, + "For {} percent and number {} the expected result was {} but we got {}", + case.requested_percent, case.examined_base_number, case.expected_result, result + ) + }) } #[test] - fn should_be_rounded_down_works_for_last_but_one_digit() { + fn should_be_rounded_as_works_for_last_but_one_digit() { [ - (787879, false), - (1114545, true), - (100, true), - (49, true), - (50, false), + ( + 49, + RoundingRule::ToSmallerPositive, + RoundingRule::ToSmallerNegative, + ), + ( + 50, + RoundingRule::ToBiggerPositive, + RoundingRule::ToBiggerNegative, + ), + ( + 51, + RoundingRule::ToBiggerPositive, + RoundingRule::ToBiggerNegative, + ), + ( + 5, + RoundingRule::ToSmallerPositive, + RoundingRule::ToSmallerNegative, + ), + ( + 100, + RoundingRule::ToSmallerPositive, + RoundingRule::ToSmallerNegative, + ), + ( + 787879, + RoundingRule::ToBiggerPositive, + RoundingRule::ToBiggerNegative, + ), + ( + 898784545, + RoundingRule::ToSmallerPositive, + RoundingRule::ToSmallerNegative, + ), ] .into_iter() - .for_each(|(num, expected_result)| { - assert_eq!(Percentage::should_be_rounded_down(num), expected_result) - }) + .for_each( + |(num, expected_result_for_unsigned_base, expected_result_for_signed_base)| { + let result = PurePercentage::should_be_rounded_as(num, 100); + assert_eq!( + result, + expected_result_for_unsigned_base, + "Unsigned number {} was identified for rounding as {:?} but it should've been {:?}", + num, + result, + expected_result_for_unsigned_base + ); + let signed = num as i64 * -1; + let result = PurePercentage::should_be_rounded_as(signed, 100); + assert_eq!( + result, + expected_result_for_signed_base, + "Signed number {} was identified for rounding as {:?} but it should've been {:?}", + signed, + result, + expected_result_for_signed_base + ) + }, + ) } #[test] fn add_percent_to_works() { - let percentage = Percentage::new(13); + let subject = PurePercentage::try_from(13).unwrap(); - let result = percentage.add_percent_to(100); + let unsigned = subject.add_percent_to(100); + let signed = subject.add_percent_to(-100); - assert_eq!(result, 113) + assert_eq!(unsigned, 113); + assert_eq!(signed, -113) } #[test] - #[should_panic(expected = "Overflowed during addition of 1 per cent, that is \ + #[should_panic(expected = "Overflowed during addition of 1 percent, that is \ 184467440737095516, to 18446744073709551615 of type u64.")] fn add_percent_to_hits_overflow() { - let _ = Percentage::new(1).add_percent_to(u64::MAX); + let _ = PurePercentage::try_from(1) + .unwrap() + .add_percent_to(u64::MAX); } #[test] fn subtract_percent_from_works() { - let percentage = Percentage::new(55); + let subject = PurePercentage::try_from(55).unwrap(); - let result = percentage.subtract_percent_from(100); + let unsigned = subject.subtract_percent_from(100); + let signed = subject.subtract_percent_from(-100); - assert_eq!(result, 45) + assert_eq!(unsigned, 45); + assert_eq!(signed, -45) } #[test] fn preventing_early_upper_overflow() { - let quite_large_value = u64::MAX / 60; - // The standard algorythm begins by a multiplication with this 61, which would cause + // The standard algorithm begins by a multiplication with this 61, which would cause // an overflow, so for such large numbers like this one we switch the order of operations. - // We're gonna devide it by 100 first and multiple after it. (However, we'd lose some + // We're going to divide it by 100 first and multiple after it. (However, we'd lose some // precision in smaller numbers that same way). Why that much effort? I don't want to see - // an overflow happen where most people would't anticipate it: when going for a percentage - // from their number, implaying a request to receive another number, but always smaller - // than that passed in. - let result = Percentage::new(61).of(quite_large_value); + // an overflow happen where most people wouldn't anticipate it: when going for + // a PurePercentage from their number, implying a request to receive another number, but + // always smaller than that passed in. + let case_one = PurePercentage::try_from(61).unwrap().of(u64::MAX / 60); + // There is more going on under the hood, which shows better on the following example: + // if we divide 255 by 100, we get 2. Then multiplied by 30, it amounts to 60. The right + // result, though, is 77 (with an extra 1 from rounding). Therefor there is another + // piece of code whose charge is to treat the remainder of modulo 100 that is pushed off + // the scoped, and if ignored, it would cause the result to be undervalued. This remainder + // is again treated the by the primary (reversed) methodology with num * percents done + // first, followed by the final division, keeping just one hundredth. + let case_two = PurePercentage::try_from(30).unwrap().of(u8::MAX); + // We apply the rounding even here. That's why we'll see the result drop by one compared to + // the previous case. As 254 * 30 is 7620, the two least significant digits come rounded + // by 100 as 0 which means 7620 divided by 100 makes 76. + let case_three = PurePercentage::try_from(30).unwrap().of(u8::MAX - 1); + + assert_eq!(case_one, 187541898082713775); + assert_eq!(case_two, 77); + assert_eq!(case_three, 76) + //Note: Interestingly, this isn't a threat on the negative numbers, even the extremes. + } + + #[test] + fn zeroes_for_loose_percentage() { + assert_eq!(LoosePercentage::new(45).of(0).unwrap(), 0); + assert_eq!(LoosePercentage::new(0).of(33).unwrap(), 0) + } + + #[test] + fn loose_percentage_end_to_end_test_for_standard_values_unsigned() { + let expected_values = (0..=100).collect::>(); + + test_end_to_end(100, expected_values, |percent, base| { + LoosePercentage::new(percent as u32).of(base).unwrap() + }) + } - let expected_result = (quite_large_value / 100) * 61; - assert_eq!(result, expected_result); + #[test] + fn loose_percentage_end_to_end_test_for_standard_values_signed() { + let expected_values = (-100..=0).rev().collect::>(); + + test_end_to_end(-100, expected_values, |percent, base| { + LoosePercentage::new(percent as u32).of(base).unwrap() + }) + } + + const TEST_SET: [Case; 5] = [ + Case { + requested_percent: 101, + examined_base_number: 10000, + expected_result: 10100, + }, + Case { + requested_percent: 150, + examined_base_number: 900, + expected_result: 1350, + }, + Case { + requested_percent: 999, + examined_base_number: 10, + expected_result: 100, + }, + Case { + requested_percent: 1234567, + examined_base_number: 20, + expected_result: 12345 * 20 + (67 * 20 / 100), + }, + Case { + requested_percent: u32::MAX, + examined_base_number: 1, + expected_result: (u32::MAX / 100) as i64 + 1, + }, + ]; + + #[test] + fn loose_percentage_for_large_values_unsigned() { + TEST_SET.into_iter().for_each(|case| { + let result = LoosePercentage::new(case.requested_percent) + .of(case.examined_base_number) + .unwrap(); + assert_eq!( + result, case.expected_result, + "Expected {} does not match actual {}. Percents {} of base {}.", + case.expected_result, result, case.requested_percent, case.examined_base_number + ) + }) } #[test] - #[should_panic( - expected = "internal error: entered unreachable code: Check to prevent numbers with fewer \ - than two digits failed" - )] - fn broken_code_for_violation_of_already_checked_range() { - let _ = Percentage::should_be_rounded_down(2); + fn loose_percentage_end_to_end_test_for_large_values_signed() { + TEST_SET + .into_iter() + .map(|mut case| { + case.examined_base_number *= -1; + case.expected_result *= -1; + case + }) + .for_each(|case| { + let result = LoosePercentage::new(case.requested_percent) + .of(case.examined_base_number) + .unwrap(); + assert_eq!( + result, case.expected_result, + "Expected {} does not match actual {}. Percents {} of base {}.", + case.expected_result, result, case.requested_percent, case.examined_base_number + ) + }) } } diff --git a/node/Cargo.lock b/node/Cargo.lock index 3ed00beaf..16cddec4f 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -430,8 +430,8 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-xid 0.2.1", ] @@ -689,8 +689,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" dependencies = [ "convert_case", - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version 0.3.3", "syn 1.0.85", ] @@ -880,8 +880,8 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", "synstructure", ] @@ -2540,8 +2540,8 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95af56fee93df76d721d356ac1ca41fccf168bc448eb14049234df764ba3e76" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", ] @@ -2630,9 +2630,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2654,11 +2654,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.59", + "proc-macro2 1.0.86", ] [[package]] @@ -3270,8 +3270,8 @@ version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", ] @@ -3315,8 +3315,8 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", ] @@ -3533,8 +3533,8 @@ version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-xid 0.2.1", ] @@ -3544,8 +3544,8 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", "unicode-xid 0.2.1", ] @@ -3637,8 +3637,8 @@ version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", ] @@ -4335,7 +4335,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "quote 1.0.28", + "quote 1.0.37", "syn 1.0.85", ] @@ -4415,8 +4415,8 @@ dependencies = [ "bumpalo", "lazy_static", "log 0.4.18", - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", "wasm-bindgen-shared", ] @@ -4439,7 +4439,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ - "quote 1.0.28", + "quote 1.0.37", "wasm-bindgen-macro-support", ] @@ -4449,8 +4449,8 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -4730,8 +4730,8 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" dependencies = [ - "proc-macro2 1.0.59", - "quote 1.0.28", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.85", "synstructure", ] From ea7380ea0604ae9a9c768cbd3ebf935f1c2679a8 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 14 Sep 2024 00:36:38 +0200 Subject: [PATCH 195/250] GH-711: Pecentage looks done --- masq_lib/src/percentage.rs | 67 ++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 29a43a38f..b046ff592 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -1,6 +1,5 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use nix::libc::sigaction; use num::CheckedAdd; use num::CheckedSub; use num::{CheckedDiv, CheckedMul, Integer}; @@ -11,13 +10,20 @@ use std::ops::{Div, Rem}; // Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage // operations over a wide variety of integer types. It is also to look after the least significant // digit on the resulted number in order to avoid the effect of a loss on precision that genuinely -// comes with division on integers if a remainder is left over. +// comes with division on integers if a remainder is left over. The percents are always represented +// by an unsigned integer. On the contrary, the numbers that it is applied on can take on both +// positive and negative values. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PurePercentage { degree: u8, } +// This is a wider type that allows to specify cumulative percents of more than only 100. +// The expected use of this would look like requesting percents meaning possibly multiples of 100%, +// roughly, of a certain base number. Similarly to the PurePercentage type, also signed numbers +// would be accepted. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LoosePercentage { multiples_of_100_percent: u32, @@ -58,9 +64,9 @@ impl LoosePercentage { } } - // If this overflows you probably want to precede the operation by converting your bas number + // If this overflows you probably want to precede the operation by converting your base number // to a larger integer type - pub fn of(&self, num: N) -> Result + pub fn of(&self, num: N) -> Result where N: PercentageInteger, >::Error: Debug, @@ -71,25 +77,25 @@ impl LoosePercentage { { let multiples = match N::try_from(self.multiples_of_100_percent) { Ok(num) => num, - Err(e) => todo!(), + Err(e) => return Err(BaseTypeOverflow {}), }; let by_wholes = match num.checked_mul(&multiples) { Some(num) => num, - None => todo!(), + None => return Err(BaseTypeOverflow {}), }; let by_remainder = self.degrees_from_remainder.of(num); match by_wholes.checked_add(&by_remainder) { Some(res) => Ok(res), - None => todo!(), + None => Err(BaseTypeOverflow {}), } } } -#[derive(Debug)] -pub enum OverflowError {} +#[derive(Debug, PartialEq, Eq)] +pub struct BaseTypeOverflow {} impl TryFrom for PurePercentage { type Error = String; @@ -218,11 +224,12 @@ impl PurePercentage { i16: TryFrom, >::Error: Debug, { - self.of(num).checked_add(&num).unwrap_or_else(|| { + let to_add = self.of(num); + num.checked_add(&to_add).unwrap_or_else(|| { panic!( "Overflowed during addition of {} percent, that is {:?}, to {:?} of type {}.", self.degree, - self.of(num), + to_add, num, type_name::() ) @@ -236,7 +243,8 @@ impl PurePercentage { i16: TryFrom, >::Error: Debug, { - num.checked_sub(&self.of(num)) + let to_subtract = self.of(num); + num.checked_sub(&to_subtract) .expect("should never happen by its principle") } @@ -281,9 +289,10 @@ enum RoundingRule { #[cfg(test)] mod tests { - use crate::percentage::{LoosePercentage, PercentageInteger, PurePercentage, RoundingRule}; + use crate::percentage::{ + BaseTypeOverflow, LoosePercentage, PercentageInteger, PurePercentage, RoundingRule, + }; use std::fmt::Debug; - use std::ops::RangeInclusive; #[test] fn percentage_is_implemented_for_all_rust_integers() { @@ -629,4 +638,34 @@ mod tests { ) }) } + + #[test] + fn loose_percentage_multiple_of_percent_hits_limit() { + let percents = ((u8::MAX as u32 + 1) * 100); + let subject = LoosePercentage::new(percents); + + let result: Result = subject.of(1); + + assert_eq!(result, Err(BaseTypeOverflow {})) + } + + #[test] + fn loose_percentage_multiplying_input_number_hits_limit() { + let percents = 200; + let subject = LoosePercentage::new(percents); + + let result: Result = subject.of(u8::MAX); + + assert_eq!(result, Err(BaseTypeOverflow {})) + } + + #[test] + fn loose_percentage_adding_portion_from_remainder_hits_limit() { + let percents = 101; + let subject = LoosePercentage::new(percents); + + let result: Result = subject.of(u8::MAX); + + assert_eq!(result, Err(BaseTypeOverflow {})) + } } From d540732a21f2fadc6ca960a11c24ee4b1b96c709 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 16 Sep 2024 18:12:04 +0200 Subject: [PATCH 196/250] GH-711: First handful of changes for verify_bill_payment --- .../blockchain/amd64_linux/entrypoint.sh | 1 + .../docker/blockchain/arm64_linux/Dockerfile | 2 +- .../blockchain/arm64_linux/entrypoint.sh | 1 + .../tests/verify_bill_payment.rs | 449 +++++++++--------- 4 files changed, 231 insertions(+), 222 deletions(-) diff --git a/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh index 013f9a68d..28d960392 100755 --- a/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh +++ b/multinode_integration_tests/docker/blockchain/amd64_linux/entrypoint.sh @@ -1,4 +1,5 @@ #!/bin/sh +# Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. # All wallets begin with null balances. The only exception is the contract owner wallet whose means are to be # redistributed from there to every account that would need it. (Notice the argument --account '(payment_thresholds.debt_threshold_gwei) + 456_789; let owed_to_serving_node_3_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 789_012; - let cons_node_initial_service_fee_balance_minor = + let consuming_node_initial_service_fee_balance_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei) * 4; - let exp_final_cons_node_service_fee_balance_minor = cons_node_initial_service_fee_balance_minor - - (owed_to_serving_node_1_minor - + owed_to_serving_node_2_minor - + owed_to_serving_node_3_minor); - let test_global_config = TestInputsOutputsConfig { + let final_consuming_node_service_fee_balance_minor = + consuming_node_initial_service_fee_balance_minor + - (owed_to_serving_node_1_minor + + owed_to_serving_node_2_minor + + owed_to_serving_node_3_minor); + let test_inputs = TestInputs { ui_ports_opt: None, - cons_node_initial_transaction_fee_balance_minor_opt: None, - cons_node_initial_service_fee_balance_minor, + consuming_node_initial_transaction_fee_balance_minor_opt: None, + consuming_node_initial_service_fee_balance_minor, debts_config: Either::Left(SimpleSimulatedDebts { owed_to_serving_node_1_minor, owed_to_serving_node_2_minor, owed_to_serving_node_3_minor, }), payment_thresholds_all_nodes: payment_thresholds, - cons_node_transaction_fee_agreed_unit_price_opt: None, - exp_final_cons_node_transaction_fee_balance_minor: 999_842_470_000_000_000, - exp_final_cons_node_service_fee_balance_minor, - exp_final_service_fee_balance_serv_node_1_minor: owed_to_serving_node_1_minor, - exp_final_service_fee_balance_serv_node_2_minor: owed_to_serving_node_2_minor, - exp_final_service_fee_balance_serv_node_3_minor: owed_to_serving_node_3_minor, + consuming_node_gas_price_opt: None, + }; + let assertions_values = AssertionsValues { + final_consuming_node_transaction_fee_balance_minor: 999_842_470_000_000_000, + final_consuming_node_service_fee_balance_minor, + final_service_fee_balance_serv_node_1_minor: owed_to_serving_node_1_minor, + final_service_fee_balance_serv_node_2_minor: owed_to_serving_node_2_minor, + final_service_fee_balance_serv_node_3_minor: owed_to_serving_node_3_minor, }; - let stimulate_payments = - |cluster: &mut MASQNodeCluster, - real_consuming_node: &MASQRealNode, - _global_test_config: &TestInputsOutputsConfig| { - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(real_consuming_node.node_reference()) - .build(), - ); - } - }; + let stimulate_payments = |cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + _global_test_config: &TestInputs| { + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(real_consuming_node.node_reference()) + .build(), + ); + } + }; - let start_serving_nodes_and_run_check = + let start_serving_nodes_and_activate_their_accountancy = |cluster: &mut MASQNodeCluster, - serving_node_1_attributes: ServingNodeAttributes, - serving_node_2_attributes: ServingNodeAttributes, - serving_node_3_attributes: ServingNodeAttributes, - _test_global_config: &TestInputsOutputsConfig| { - let serving_node_1 = cluster.start_named_real_node( - &serving_node_1_attributes.name, - serving_node_1_attributes.index, - serving_node_1_attributes.config, - ); - let serving_node_2 = cluster.start_named_real_node( - &serving_node_2_attributes.name, - serving_node_2_attributes.index, - serving_node_2_attributes.config, - ); - let serving_node_3 = cluster.start_named_real_node( - &serving_node_3_attributes.name, - serving_node_3_attributes.index, - serving_node_3_attributes.config, - ); - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(serving_node_1.node_reference()) - .neighbor(serving_node_2.node_reference()) - .neighbor(serving_node_3.node_reference()) - .build(), - ); + serving_nodes: [ServingNodeAttributes; 3], + _test_global_config: &TestInputs| { + let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes + .into_iter() + .map(|attributes| { + cluster.start_named_real_node( + &attributes.name, + attributes.index, + attributes.config, + ) + }) + .map(|node| (node.node_reference(), node)) + .unzip(); + let auxiliary_node_config_builder = + NodeStartupConfigBuilder::standard().chain(Chain::Dev); + let auxiliary_node_config = node_references + .into_iter() + .fold( + auxiliary_node_config_builder, + |builder, serving_node_reference| builder.neighbor(serving_node_reference), + ) + .build(); + + for _ in 0..3 { + let _ = cluster.start_real_node(auxiliary_node_config.clone()); } - (serving_node_1, serving_node_2, serving_node_3) + serving_nodes.try_into().unwrap() }; test_body( - test_global_config, + test_inputs, + assertions_values, stimulate_payments, - start_serving_nodes_and_run_check, + start_serving_nodes_and_activate_their_accountancy, ); } @@ -172,7 +172,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { let owed_to_serv_node_3_minor = gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 60_000_000); // Assuming all Nodes rely on the same set of payment thresholds - let cons_node_initial_service_fee_balance_minor = (owed_to_serv_node_1_minor + let consuming_node_initial_service_fee_balance_minor = (owed_to_serv_node_1_minor + owed_to_serv_node_2_minor) - gwei_to_wei::(2_345_678); let agreed_transaction_fee_unit_price_major = 60; @@ -195,9 +195,9 @@ fn payments_were_adjusted_due_to_insufficient_balances() { "Computed transaction fee: {}", transaction_fee_needed_to_pay_for_one_payment_major ); - let cons_node_transaction_fee_balance_minor = + let consuming_node_transaction_fee_balance_minor = 2 * gwei_to_wei::(transaction_fee_needed_to_pay_for_one_payment_major); - let test_global_config = TestInputsOutputsConfig { + let test_inputs = TestInputs { ui_ports_opt: Some(Ports { consuming_node: consuming_node_ui_port, serving_node_1: serving_node_1_ui_port, @@ -205,10 +205,10 @@ fn payments_were_adjusted_due_to_insufficient_balances() { serving_node_3: serving_node_3_ui_port, }), // Should be enough only for two payments, the least significant one will fall out - cons_node_initial_transaction_fee_balance_minor_opt: Some( - cons_node_transaction_fee_balance_minor + 1, + consuming_node_initial_transaction_fee_balance_minor_opt: Some( + consuming_node_transaction_fee_balance_minor + 1, ), - cons_node_initial_service_fee_balance_minor, + consuming_node_initial_service_fee_balance_minor, debts_config: Either::Right(FullySpecifiedSimulatedDebts { // This account will be the most significant and will deserve the full balance owed_to_serving_node_1: AccountedDebt { @@ -228,20 +228,21 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }, }), payment_thresholds_all_nodes: payment_thresholds, - cons_node_transaction_fee_agreed_unit_price_opt: Some( - agreed_transaction_fee_unit_price_major, - ), + consuming_node_gas_price_opt: Some(agreed_transaction_fee_unit_price_major), + }; + + let assertions_values = AssertionsValues { // It seems like the ganache server sucked up quite less than those 55_000 units of gas?? - exp_final_cons_node_transaction_fee_balance_minor: 2_828_352_000_000_001, + final_consuming_node_transaction_fee_balance_minor: 2_828_352_000_000_001, // Zero reached, because the algorithm is designed to exhaust the wallet completely - exp_final_cons_node_service_fee_balance_minor: 0, + final_consuming_node_service_fee_balance_minor: 0, // This account was granted with the full size as its lowest balance from the set makes // it weight the most - exp_final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, - exp_final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor + final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, + final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor - gwei_to_wei::(2_345_678), // This account dropped out from the payment, so received no money - exp_final_service_fee_balance_serv_node_3_minor: 0, + final_service_fee_balance_serv_node_3_minor: 0, }; let process_scan_request_to_node = @@ -252,50 +253,53 @@ fn payments_were_adjusted_due_to_insufficient_balances() { UiScanResponse::fmb(response).unwrap(); }; - let stimulate_payments = - |_cluster: &mut MASQNodeCluster, - real_consuming_node: &MASQRealNode, - _global_test_config: &TestInputsOutputsConfig| { - process_scan_request_to_node( - &real_consuming_node, - consuming_node_ui_port, - ScanType::Payables, - 1111, - ) - }; + let stimulate_payments = |_cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + _global_test_config: &TestInputs| { + process_scan_request_to_node( + &real_consuming_node, + consuming_node_ui_port, + ScanType::Payables, + 1111, + ) + }; - let start_serving_nodes_and_run_check = |cluster: &mut MASQNodeCluster, - serving_node_1_attributes: ServingNodeAttributes, - serving_node_2_attributes: ServingNodeAttributes, - serving_node_3_attributes: ServingNodeAttributes, - test_global_config: &TestInputsOutputsConfig| - -> (MASQRealNode, MASQRealNode, MASQRealNode) { - let ports = test_global_config.ui_ports_opt.as_ref().unwrap(); - let mut vec: Vec = vec![ - (serving_node_1_attributes, ports.serving_node_1, 2222), - (serving_node_2_attributes, ports.serving_node_2, 3333), - (serving_node_3_attributes, ports.serving_node_3, 4444), - ] - .into_iter() - .map(|(serving_node_attributes, ui_port, context_id)| { - let serving_node = cluster.start_named_real_node( - &serving_node_attributes.name, - serving_node_attributes.index, - serving_node_attributes.config, - ); + let start_serving_nodes_and_activate_their_accountancy = |cluster: &mut MASQNodeCluster, + serving_nodes_attributes: [ServingNodeAttributes; + 3], + test_inputs: &TestInputs| + -> [MASQRealNode; 3] { + let real_nodes: Vec<_> = serving_nodes_attributes + .into_iter() + .enumerate() + .map(|(idx, serving_node_attributes)| { + let ui_port = test_inputs + .port(serving_node_attributes.node_by_role) + .expect("ui port missing"); + let serving_node = cluster.start_named_real_node( + &serving_node_attributes.name, + serving_node_attributes.index, + serving_node_attributes.config, + ); - process_scan_request_to_node(&serving_node, ui_port, ScanType::Receivables, context_id); + process_scan_request_to_node( + &serving_node, + ui_port, + ScanType::Receivables, + (idx * 111) as u64, + ); - serving_node - }) - .collect(); - (vec.remove(0), vec.remove(0), vec.remove(0)) + serving_node + }) + .collect(); + real_nodes.try_into().unwrap() }; test_body( - test_global_config, + test_inputs, + assertions_values, stimulate_payments, - start_serving_nodes_and_run_check, + start_serving_nodes_and_activate_their_accountancy, ); } @@ -493,11 +497,11 @@ fn make_seed() -> Seed { fn build_config( server_url_holder: &dyn UrlHolder, seed: &Seed, - payment_thresholds: PaymentThresholds, - transaction_fee_agreed_price_per_unit_opt: Option, - wallet_derivation_path: String, - port_opt: Option, + node_by_role: NodeByRole, + test_inputs: &TestInputs, ) -> (NodeStartupConfig, Wallet) { + let wallet_derivation_path = node_by_role.derivation_path(); + let payment_thresholds = test_inputs.payment_thresholds_all_nodes; let (node_wallet, node_secret) = make_node_wallet(seed, wallet_derivation_path.as_str()); let cfg_to_build = NodeStartupConfigBuilder::standard() .blockchain_service_url(server_url_holder.url()) @@ -508,18 +512,18 @@ fn build_config( "{}", node_wallet.clone() ))); - let cfg_to_build = if let Some(port) = port_opt { + let cfg_to_build = if let Some(port) = test_inputs.port(node_by_role) { cfg_to_build.ui_port(port) } else { cfg_to_build }; - let cfg_to_build = if let Some(price) = transaction_fee_agreed_price_per_unit_opt { + let cfg_to_build = if let Some(price) = test_inputs.consuming_node_gas_price_opt { cfg_to_build.gas_price(price) } else { cfg_to_build }; - let config = cfg_to_build.build(); - (config, node_wallet) + + (cfg_to_build.build(), node_wallet) } fn expire_payables( @@ -588,7 +592,7 @@ fn expire_receivables(path: PathBuf) { config_stmt.execute([]).unwrap(); } -struct TestInputsOutputsConfig { +struct TestInputs { ui_ports_opt: Option, // The contract owner wallet is populated with 100 ETH as defined in the set of commands // with which we start up the Ganache server. @@ -596,24 +600,27 @@ struct TestInputsOutputsConfig { // Specify number of wei this account should possess at its initialisation. // The consuming node gets the full balance of the contract owner if left as None. // Cannot ever get more than what the "owner" has. - cons_node_initial_transaction_fee_balance_minor_opt: Option, - cons_node_initial_service_fee_balance_minor: u128, + consuming_node_initial_transaction_fee_balance_minor_opt: Option, + consuming_node_initial_service_fee_balance_minor: u128, debts_config: Either, payment_thresholds_all_nodes: PaymentThresholds, - cons_node_transaction_fee_agreed_unit_price_opt: Option, + consuming_node_gas_price_opt: Option, +} - exp_final_cons_node_transaction_fee_balance_minor: u128, - exp_final_cons_node_service_fee_balance_minor: u128, - exp_final_service_fee_balance_serv_node_1_minor: u128, - exp_final_service_fee_balance_serv_node_2_minor: u128, - exp_final_service_fee_balance_serv_node_3_minor: u128, +struct AssertionsValues { + final_consuming_node_transaction_fee_balance_minor: u128, + final_consuming_node_service_fee_balance_minor: u128, + final_service_fee_balance_serv_node_1_minor: u128, + final_service_fee_balance_serv_node_2_minor: u128, + final_service_fee_balance_serv_node_3_minor: u128, } +#[derive(Clone, Copy)] enum NodeByRole { - ConsNode, - ServNode1, - ServNode2, - ServNode3, + ConsumingNode = 1, + ServingNode1 = 2, + ServingNode2 = 3, + ServingNode3 = 4, } struct SimpleSimulatedDebts { @@ -633,32 +640,32 @@ struct AccountedDebt { age_s: u64, } -impl TestInputsOutputsConfig { +impl TestInputs { fn port(&self, requested: NodeByRole) -> Option { self.ui_ports_opt.as_ref().map(|ports| match requested { - NodeByRole::ConsNode => ports.consuming_node, - NodeByRole::ServNode1 => ports.serving_node_1, - NodeByRole::ServNode2 => ports.serving_node_2, - NodeByRole::ServNode3 => ports.serving_node_3, + NodeByRole::ConsumingNode => ports.consuming_node, + NodeByRole::ServingNode1 => ports.serving_node_1, + NodeByRole::ServingNode2 => ports.serving_node_2, + NodeByRole::ServingNode3 => ports.serving_node_3, }) } fn debt_size(&self, requested: NodeByRole) -> u128 { match self.debts_config.as_ref() { Either::Left(simple_config) => match requested { - NodeByRole::ServNode1 => simple_config.owed_to_serving_node_1_minor, - NodeByRole::ServNode2 => simple_config.owed_to_serving_node_2_minor, - NodeByRole::ServNode3 => simple_config.owed_to_serving_node_3_minor, - NodeByRole::ConsNode => panic!( + NodeByRole::ServingNode1 => simple_config.owed_to_serving_node_1_minor, + NodeByRole::ServingNode2 => simple_config.owed_to_serving_node_2_minor, + NodeByRole::ServingNode3 => simple_config.owed_to_serving_node_3_minor, + NodeByRole::ConsumingNode => panic!( "Version simple: These configs are \ serve to set owed to the consuming node, while that one should not be here." ), }, Either::Right(fully_specified) => match requested { - NodeByRole::ServNode1 => fully_specified.owed_to_serving_node_1.balance_minor, - NodeByRole::ServNode2 => fully_specified.owed_to_serving_node_2.balance_minor, - NodeByRole::ServNode3 => fully_specified.owed_to_serving_node_3.balance_minor, - NodeByRole::ConsNode => panic!( + NodeByRole::ServingNode1 => fully_specified.owed_to_serving_node_1.balance_minor, + NodeByRole::ServingNode2 => fully_specified.owed_to_serving_node_2.balance_minor, + NodeByRole::ServingNode3 => fully_specified.owed_to_serving_node_3.balance_minor, + NodeByRole::ConsumingNode => panic!( "Version fully specified: These configs \ are serve to set owed to the consuming node, while that one should not \ be here." @@ -668,6 +675,14 @@ impl TestInputsOutputsConfig { } } +impl NodeByRole { + fn derivation_path(self) -> String { + derivation_path(0, self as usize as u8) + } +} + +const ONE_ETH_IN_WEI: u128 = 1_000_000_000_000_000_000; + struct Ports { consuming_node: u16, serving_node_1: u16, @@ -676,26 +691,21 @@ struct Ports { } struct ServingNodeAttributes { + node_by_role: NodeByRole, name: String, index: usize, config: NodeStartupConfig, } fn test_body( - global_config: TestInputsOutputsConfig, + test_inputs: TestInputs, + assertions_values: AssertionsValues, stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, - start_serving_nodes_and_run_check: StartServingNodesAndLetThemPerformReceivablesCheck, + start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, ) where - StimulateConsumingNodePayments: - FnOnce(&mut MASQNodeCluster, &MASQRealNode, &TestInputsOutputsConfig), + StimulateConsumingNodePayments: FnOnce(&mut MASQNodeCluster, &MASQRealNode, &TestInputs), StartServingNodesAndLetThemPerformReceivablesCheck: - FnOnce( - &mut MASQNodeCluster, - ServingNodeAttributes, - ServingNodeAttributes, - ServingNodeAttributes, - &TestInputsOutputsConfig, - ) -> (MASQRealNode, MASQRealNode, MASQRealNode), + FnOnce(&mut MASQNodeCluster, [ServingNodeAttributes; 3], &TestInputs) -> [MASQRealNode; 3], { let mut cluster = match MASQNodeCluster::start() { Ok(cluster) => cluster, @@ -715,25 +725,25 @@ fn test_body Date: Thu, 19 Sep 2024 15:14:39 +0200 Subject: [PATCH 197/250] GH-711: verify_bill_payments significant improvement in arrangement --- .../src/masq_node_cluster.rs | 16 +- .../src/masq_real_node.rs | 15 +- multinode_integration_tests/src/utils.rs | 4 +- .../tests/blockchain_interaction_test.rs | 21 +- .../tests/verify_bill_payment.rs | 1114 +++++++++-------- 5 files changed, 615 insertions(+), 555 deletions(-) diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index 86a94af54..c8c7d0f11 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -5,8 +5,9 @@ use crate::masq_mock_node::{ MutableMASQMockNodeStarter, }; use crate::masq_node::{MASQNode, MASQNodeUtils}; -use crate::masq_real_node::MASQRealNode; use crate::masq_real_node::NodeStartupConfig; +use crate::masq_real_node::{MASQRealNode, NodeNamingAndDir}; +use crate::utils::{node_chain_specific_data_directory, open_all_file_permissions}; use masq_lib::blockchains::chains::Chain; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use node_lib::sub_lib::cryptde::PublicKey; @@ -14,6 +15,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::env; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs}; +use std::path::PathBuf; pub struct MASQNodeCluster { startup_configs: HashMap<(String, usize), NodeStartupConfig>, @@ -50,15 +52,21 @@ impl MASQNodeCluster { self.next_index } - pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> (String, usize) { + pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> NodeNamingAndDir { let index = self.startup_configs.len() + 1; let name = MASQRealNode::make_name(index); self.next_index = index + 1; self.startup_configs .insert((name.clone(), index), config.clone()); - MASQRealNode::prepare(&name); + MASQRealNode::prepare_node_directories_for_docker(&name); + let db_path: PathBuf = node_chain_specific_data_directory(&name).into(); + open_all_file_permissions(&db_path); - (name, index) + NodeNamingAndDir { + node_name: name, + index, + db_path, + } } pub fn start_real_node(&mut self, config: NodeStartupConfig) -> MASQRealNode { diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 5170bc763..55cf44543 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -30,7 +30,7 @@ use std::fmt::Display; use std::net::IpAddr; use std::net::Ipv4Addr; use std::net::SocketAddr; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::str::FromStr; use std::string::ToString; @@ -630,8 +630,8 @@ impl NodeStartupConfigBuilder { self } - pub fn blockchain_service_url(mut self, blockchain_service_url: String) -> Self { - self.blockchain_service_url = Some(blockchain_service_url); + pub fn blockchain_service_url(mut self, blockchain_service_url: &str) -> Self { + self.blockchain_service_url = Some(blockchain_service_url.to_string()); self } @@ -778,7 +778,7 @@ impl MASQNode for MASQRealNode { } impl MASQRealNode { - pub fn prepare(name: &str) { + pub fn prepare_node_directories_for_docker(name: &str) { Self::do_prepare_for_docker_run(name).unwrap(); } @@ -1221,6 +1221,13 @@ impl MASQRealNode { } } +#[derive(Debug)] +pub struct NodeNamingAndDir { + pub node_name: String, + pub index: usize, + pub db_path: PathBuf, +} + #[derive(Debug, Clone)] struct CryptDENullPair { main: CryptDENull, diff --git a/multinode_integration_tests/src/utils.rs b/multinode_integration_tests/src/utils.rs index 12c63f1ec..1d4fa92f7 100644 --- a/multinode_integration_tests/src/utils.rs +++ b/multinode_integration_tests/src/utils.rs @@ -18,7 +18,7 @@ use node_lib::sub_lib::cryptde::{CryptData, PlainData}; use std::collections::BTreeSet; use std::io::{ErrorKind, Read, Write}; use std::net::TcpStream; -use std::path::PathBuf; +use std::path::Path; use std::time::{Duration, Instant}; use std::{io, thread}; @@ -111,7 +111,7 @@ pub fn wait_for_shutdown(stream: &mut TcpStream, timeout: &Duration) -> Result<( } } -pub fn open_all_file_permissions(dir: PathBuf) { +pub fn open_all_file_permissions(dir: &Path) { match Command::new( "chmod", Command::strings(vec!["-R", "777", dir.to_str().unwrap()]), diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 1eb6a3ca7..507036630 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -68,20 +68,18 @@ fn debtors_are_credited_once_but_not_twice() { let node_config = NodeStartupConfigBuilder::standard() .log_level(Level::Debug) .scans(false) - .blockchain_service_url(blockchain_client_server.url()) + .blockchain_service_url(&blockchain_client_server.url()) .ui_port(ui_port) .build(); - let (node_name, node_index) = cluster.prepare_real_node(&node_config); - let chain_specific_dir = node_chain_specific_data_directory(&node_name); - open_all_file_permissions(PathBuf::from(chain_specific_dir)); + let node_namings = cluster.prepare_real_node(&node_config); { - let config_dao = config_dao(&node_name); + let config_dao = config_dao(&node_namings.node_name); config_dao .set("start_block", Some("1000".to_string())) .unwrap(); } { - let receivable_dao = receivable_dao(&node_name); + let receivable_dao = receivable_dao(&node_namings.node_name); receivable_dao .more_money_receivable( SystemTime::UNIX_EPOCH.add(Duration::from_secs(15_000_000)), @@ -92,7 +90,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the receivable DAO to verify that the receivable's balance has been initialized { - let receivable_dao = receivable_dao(&node_name); + let receivable_dao = receivable_dao(&node_namings.node_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -107,13 +105,14 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been set to 1000 { - let config_dao = config_dao(&node_name); + let config_dao = config_dao(&node_namings.node_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "1000" ); } - let node = cluster.start_named_real_node(&node_name, node_index, node_config); + let node = + cluster.start_named_real_node(&node_namings.node_name, node_namings.index, node_config); let ui_client = node.make_ui(ui_port); // Command a scan log ui_client.send_request( @@ -129,7 +128,7 @@ fn debtors_are_credited_once_but_not_twice() { node.kill_node(); // Use the receivable DAO to verify that the receivable's balance has been adjusted { - let receivable_dao = receivable_dao(&node_name); + let receivable_dao = receivable_dao(&node_namings.node_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -144,7 +143,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been advanced to 2001 { - let config_dao = config_dao(&node_name); + let config_dao = config_dao(&node_namings.node_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "2001" diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index d98b4d899..59d5b9fc5 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use bip39::{Language, Mnemonic, Seed}; use futures::Future; -use itertools::Either; use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; @@ -12,15 +11,12 @@ use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeStartupConfig, + ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeNamingAndDir, NodeStartupConfig, NodeStartupConfigBuilder, }; -use multinode_integration_tests_lib::utils::{ - node_chain_specific_data_directory, open_all_file_permissions, UrlHolder, -}; +use multinode_integration_tests_lib::utils::UrlHolder; use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; -use node_lib::accountant::db_access_objects::utils::to_time_t; use node_lib::accountant::gwei_to_wei; use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ @@ -36,11 +32,10 @@ use node_lib::sub_lib::blockchain_interface_web3::{ }; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; -use rusqlite::ToSql; use rustc_hex::{FromHex, ToHex}; use std::convert::TryFrom; -use std::path::{Path, PathBuf}; -use std::time::{Duration, Instant, SystemTime}; +use std::path::Path; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::{thread, u128}; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; @@ -65,63 +60,72 @@ fn verify_bill_payment() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; - let owed_to_serving_node_1_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 123_456; - let owed_to_serving_node_2_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 456_789; - let owed_to_serving_node_3_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei) + 789_012; - let consuming_node_initial_service_fee_balance_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei) * 4; - let final_consuming_node_service_fee_balance_minor = - consuming_node_initial_service_fee_balance_minor - - (owed_to_serving_node_1_minor - + owed_to_serving_node_2_minor - + owed_to_serving_node_3_minor); + let debt_threshold_wei = to_wei(payment_thresholds.debt_threshold_gwei); + let owed_to_serving_node_1_minor = debt_threshold_wei + 123_456; + let owed_to_serving_node_2_minor = debt_threshold_wei + 456_789; + let owed_to_serving_node_3_minor = debt_threshold_wei + 789_012; + let consuming_node_initial_service_fee_balance_minor = debt_threshold_wei * 4; + let long_ago = UNIX_EPOCH.elapsed().unwrap().as_secs(); let test_inputs = TestInputs { ui_ports_opt: None, consuming_node_initial_transaction_fee_balance_minor_opt: None, consuming_node_initial_service_fee_balance_minor, - debts_config: Either::Left(SimpleSimulatedDebts { - owed_to_serving_node_1_minor, - owed_to_serving_node_2_minor, - owed_to_serving_node_3_minor, - }), + debts_config: DebtsSpecs { + serving_node_1: AccountedDebt { + balance_minor: owed_to_serving_node_1_minor, + age_s: long_ago, + }, + serving_node_2: AccountedDebt { + balance_minor: owed_to_serving_node_2_minor, + age_s: long_ago, + }, + serving_node_3: AccountedDebt { + balance_minor: owed_to_serving_node_3_minor, + age_s: long_ago, + }, + }, payment_thresholds_all_nodes: payment_thresholds, consuming_node_gas_price_opt: None, }; + let final_consuming_node_service_fee_balance_minor = + consuming_node_initial_service_fee_balance_minor + - (owed_to_serving_node_1_minor + + owed_to_serving_node_2_minor + + owed_to_serving_node_3_minor); let assertions_values = AssertionsValues { final_consuming_node_transaction_fee_balance_minor: 999_842_470_000_000_000, final_consuming_node_service_fee_balance_minor, - final_service_fee_balance_serv_node_1_minor: owed_to_serving_node_1_minor, - final_service_fee_balance_serv_node_2_minor: owed_to_serving_node_2_minor, - final_service_fee_balance_serv_node_3_minor: owed_to_serving_node_3_minor, + final_service_fee_balances: FinalServiceFeeBalancesByNode { + node_1_minor: owed_to_serving_node_1_minor, + node_2_minor: owed_to_serving_node_2_minor, + node_3_minor: owed_to_serving_node_3_minor, + }, }; - let stimulate_payments = |cluster: &mut MASQNodeCluster, - real_consuming_node: &MASQRealNode, - _global_test_config: &TestInputs| { - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(real_consuming_node.node_reference()) - .build(), - ); - } - }; + let stimulate_payments: StimulateConsumingNodePayments = + Box::new(|cluster, real_consuming_node, _test_inputs| { + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(real_consuming_node.node_reference()) + .build(), + ); + } + }); - let start_serving_nodes_and_activate_their_accountancy = - |cluster: &mut MASQNodeCluster, - serving_nodes: [ServingNodeAttributes; 3], - _test_global_config: &TestInputs| { + let start_serving_nodes_and_activate_their_accountancy : StartServingNodesAndLetThemPerformReceivablesCheck = Box::new( + |cluster, + serving_nodes, + _wholesome_config| { let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes .into_iter() .map(|attributes| { + let namings = &attributes.common.namings; cluster.start_named_real_node( - &attributes.name, - attributes.index, - attributes.config, + &namings.node_name, + namings.index, + attributes.common.config_opt.take().unwrap(), ) }) .map(|node| (node.node_reference(), node)) @@ -141,7 +145,7 @@ fn verify_bill_payment() { } serving_nodes.try_into().unwrap() - }; + }); test_body( test_inputs, @@ -153,10 +157,6 @@ fn verify_bill_payment() { #[test] fn payments_were_adjusted_due_to_insufficient_balances() { - let consuming_node_ui_port = find_free_port(); - let serving_node_1_ui_port = find_free_port(); - let serving_node_2_ui_port = find_free_port(); - let serving_node_3_ui_port = find_free_port(); let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_500_000, debt_threshold_gwei: 100_000_000, @@ -165,12 +165,12 @@ fn payments_were_adjusted_due_to_insufficient_balances() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 1_000_000, }; - let owed_to_serv_node_1_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 5_000_000); + + let owed_to_serv_node_1_minor = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); let owed_to_serv_node_2_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 20_000_000); + gwei_to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); let owed_to_serv_node_3_minor = - gwei_to_wei::(payment_thresholds.debt_threshold_gwei + 60_000_000); + gwei_to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); // Assuming all Nodes rely on the same set of payment thresholds let consuming_node_initial_service_fee_balance_minor = (owed_to_serv_node_1_minor + owed_to_serv_node_2_minor) @@ -191,42 +191,38 @@ fn payments_were_adjusted_due_to_insufficient_balances() { transaction_fee_margin .add_percent_to(gas_limit_dev_chain * agreed_transaction_fee_unit_price_major) }; - eprintln!( - "Computed transaction fee: {}", - transaction_fee_needed_to_pay_for_one_payment_major - ); let consuming_node_transaction_fee_balance_minor = 2 * gwei_to_wei::(transaction_fee_needed_to_pay_for_one_payment_major); let test_inputs = TestInputs { ui_ports_opt: Some(Ports { - consuming_node: consuming_node_ui_port, - serving_node_1: serving_node_1_ui_port, - serving_node_2: serving_node_2_ui_port, - serving_node_3: serving_node_3_ui_port, + consuming_node: find_free_port(), + serving_node_1: find_free_port(), + serving_node_2: find_free_port(), + serving_node_3: find_free_port(), }), // Should be enough only for two payments, the least significant one will fall out consuming_node_initial_transaction_fee_balance_minor_opt: Some( consuming_node_transaction_fee_balance_minor + 1, ), consuming_node_initial_service_fee_balance_minor, - debts_config: Either::Right(FullySpecifiedSimulatedDebts { + debts_config: DebtsSpecs { // This account will be the most significant and will deserve the full balance - owed_to_serving_node_1: AccountedDebt { + serving_node_1: AccountedDebt { balance_minor: owed_to_serv_node_1_minor, age_s: payment_thresholds.maturity_threshold_sec + 1000, }, // This balance is of a middle size it will be reduced as there won't be enough // after the first one is filled up. - owed_to_serving_node_2: AccountedDebt { + serving_node_2: AccountedDebt { balance_minor: owed_to_serv_node_2_minor, age_s: payment_thresholds.maturity_threshold_sec + 100_000, }, // This account will be the least significant and therefore eliminated - owed_to_serving_node_3: AccountedDebt { + serving_node_3: AccountedDebt { balance_minor: owed_to_serv_node_3_minor, age_s: payment_thresholds.maturity_threshold_sec + 30_000, }, - }), + }, payment_thresholds_all_nodes: payment_thresholds, consuming_node_gas_price_opt: Some(agreed_transaction_fee_unit_price_major), }; @@ -238,11 +234,12 @@ fn payments_were_adjusted_due_to_insufficient_balances() { final_consuming_node_service_fee_balance_minor: 0, // This account was granted with the full size as its lowest balance from the set makes // it weight the most - final_service_fee_balance_serv_node_1_minor: owed_to_serv_node_1_minor, - final_service_fee_balance_serv_node_2_minor: owed_to_serv_node_2_minor - - gwei_to_wei::(2_345_678), - // This account dropped out from the payment, so received no money - final_service_fee_balance_serv_node_3_minor: 0, + final_service_fee_balances: FinalServiceFeeBalancesByNode { + node_1_minor: owed_to_serv_node_1_minor, + node_2_minor: owed_to_serv_node_2_minor - gwei_to_wei::(2_345_678), + // This account dropped out from the payment, so received no money + node_3_minor: 0, + }, }; let process_scan_request_to_node = @@ -253,34 +250,35 @@ fn payments_were_adjusted_due_to_insufficient_balances() { UiScanResponse::fmb(response).unwrap(); }; - let stimulate_payments = |_cluster: &mut MASQNodeCluster, - real_consuming_node: &MASQRealNode, - _global_test_config: &TestInputs| { - process_scan_request_to_node( - &real_consuming_node, - consuming_node_ui_port, - ScanType::Payables, - 1111, - ) - }; + let stimulate_payments: StimulateConsumingNodePayments = + Box::new(|_cluster, real_consuming_node, global_values| { + process_scan_request_to_node( + &real_consuming_node, + global_values + .test_inputs + .port(NodeByRole::ConsumingNode) + .unwrap(), + ScanType::Payables, + 1111, + ) + }); - let start_serving_nodes_and_activate_their_accountancy = |cluster: &mut MASQNodeCluster, - serving_nodes_attributes: [ServingNodeAttributes; - 3], - test_inputs: &TestInputs| - -> [MASQRealNode; 3] { + let start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck = Box::new(|cluster, + serving_nodes_attributes, global_values| { let real_nodes: Vec<_> = serving_nodes_attributes - .into_iter() + .iter_mut() .enumerate() .map(|(idx, serving_node_attributes)| { - let ui_port = test_inputs - .port(serving_node_attributes.node_by_role) - .expect("ui port missing"); + let node_config = serving_node_attributes.common.config_opt.take().unwrap(); + let common = &serving_node_attributes.common; let serving_node = cluster.start_named_real_node( - &serving_node_attributes.name, - serving_node_attributes.index, - serving_node_attributes.config, + &common.namings.node_name, + common.namings.index, + node_config, ); + let ui_port = global_values.test_inputs + .port(common.node_by_role) + .expect("ui port missing"); process_scan_request_to_node( &serving_node, @@ -293,7 +291,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }) .collect(); real_nodes.try_into().unwrap() - }; + }); test_body( test_inputs, @@ -303,6 +301,61 @@ fn payments_were_adjusted_due_to_insufficient_balances() { ); } +fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, GlobalValues) { + let now = SystemTime::now(); + let cluster = match MASQNodeCluster::start() { + Ok(cluster) => cluster, + Err(e) => panic!("{}", e), + }; + let blockchain_server = BlockchainServer { + name: "ganache-cli", + }; + blockchain_server.start(); + blockchain_server.wait_until_ready(); + let server_url = blockchain_server.url().to_string(); + let (event_loop_handle, http) = + Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); + let web3 = Web3::new(http.clone()); + let seed = make_seed(); + let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); + let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); + let blockchain_interface = Box::new(BlockchainInterfaceWeb3::new( + http, + event_loop_handle, + cluster.chain, + )); + let blockchain_params = BlockchainParams { + blockchain_interfaces: BlockchainInterfaces { + blockchain_interface, + web3, + }, + chain: cluster.chain, + server_url, + contract_owner_addr, + contract_owner_wallet, + seed, + }; + let global_values = GlobalValues { + test_inputs, + blockchain_params, + now_in_common: now, + }; + assert_eq!( + contract_owner_addr, + cluster.chain.rec().contract, + "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ + Resulted contact addr {:?} doesn't much what's expected: {:?}", + contract_owner_addr, + cluster.chain.rec().contract + ); + + (cluster, global_values) +} + +fn to_wei(gwei: u64) -> u128 { + gwei_to_wei(gwei) +} + fn make_init_config(chain: Chain) -> DbInitializationConfig { DbInitializationConfig::create_or_migrate(ExternalData::new( chain, @@ -313,7 +366,7 @@ fn make_init_config(chain: Chain) -> DbInitializationConfig { fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { let data = "608060405234801561001057600080fd5b5060038054600160a060020a031916331790819055604051600160a060020a0391909116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3610080336b01866de34549d620d8000000640100000000610b9461008582021704565b610156565b600160a060020a038216151561009a57600080fd5b6002546100b490826401000000006109a461013d82021704565b600255600160a060020a0382166000908152602081905260409020546100e790826401000000006109a461013d82021704565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60008282018381101561014f57600080fd5b9392505050565b610c6a806101656000396000f3006080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610100578063095ea7b31461018a57806318160ddd146101c257806323b872dd146101e95780632ff2e9dc14610213578063313ce56714610228578063395093511461025357806342966c681461027757806370a0823114610291578063715018a6146102b257806379cc6790146102c75780638da5cb5b146102eb5780638f32d59b1461031c57806395d89b4114610331578063a457c2d714610346578063a9059cbb1461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561014f578181015183820152602001610137565b50505050905090810190601f16801561017c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019657600080fd5b506101ae600160a060020a0360043516602435610436565b604080519115158252519081900360200190f35b3480156101ce57600080fd5b506101d7610516565b60408051918252519081900360200190f35b3480156101f557600080fd5b506101ae600160a060020a036004358116906024351660443561051c565b34801561021f57600080fd5b506101d76105b9565b34801561023457600080fd5b5061023d6105c9565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b506101ae600160a060020a03600435166024356105ce565b34801561028357600080fd5b5061028f60043561067e565b005b34801561029d57600080fd5b506101d7600160a060020a036004351661068b565b3480156102be57600080fd5b5061028f6106a6565b3480156102d357600080fd5b5061028f600160a060020a0360043516602435610710565b3480156102f757600080fd5b5061030061071e565b60408051600160a060020a039092168252519081900360200190f35b34801561032857600080fd5b506101ae61072d565b34801561033d57600080fd5b5061011561073e565b34801561035257600080fd5b506101ae600160a060020a0360043516602435610775565b34801561037657600080fd5b506101ae600160a060020a03600435166024356107c0565b34801561039a57600080fd5b506101d7600160a060020a03600435811690602435166107d6565b3480156103c157600080fd5b5061028f600160a060020a0360043516610801565b606060405190810160405280602481526020017f486f7420746865206e657720746f6b656e20796f75277265206c6f6f6b696e6781526020017f20666f720000000000000000000000000000000000000000000000000000000081525081565b600081158061044c575061044a33846107d6565b155b151561050557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f55736520696e637265617365417070726f76616c206f7220646563726561736560448201527f417070726f76616c20746f2070726576656e7420646f75626c652d7370656e6460648201527f2e00000000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b61050f838361081d565b9392505050565b60025490565b600160a060020a038316600090815260016020908152604080832033845290915281205482111561054c57600080fd5b600160a060020a0384166000908152600160209081526040808320338452909152902054610580908363ffffffff61089b16565b600160a060020a03851660009081526001602090815260408083203384529091529020556105af8484846108b2565b5060019392505050565b6b01866de34549d620d800000081565b601281565b6000600160a060020a03831615156105e557600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff6109a416565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b61068833826109b6565b50565b600160a060020a031660009081526020819052604090205490565b6106ae61072d565b15156106b957600080fd5b600354604051600091600160a060020a0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36003805473ffffffffffffffffffffffffffffffffffffffff19169055565b61071a8282610a84565b5050565b600354600160a060020a031690565b600354600160a060020a0316331490565b60408051808201909152600381527f484f540000000000000000000000000000000000000000000000000000000000602082015281565b6000600160a060020a038316151561078c57600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff61089b16565b60006107cd3384846108b2565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b61080961072d565b151561081457600080fd5b61068881610b16565b6000600160a060020a038316151561083457600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b600080838311156108ab57600080fd5b5050900390565b600160a060020a0383166000908152602081905260409020548111156108d757600080fd5b600160a060020a03821615156108ec57600080fd5b600160a060020a038316600090815260208190526040902054610915908263ffffffff61089b16565b600160a060020a03808516600090815260208190526040808220939093559084168152205461094a908263ffffffff6109a416565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282018381101561050f57600080fd5b600160a060020a03821615156109cb57600080fd5b600160a060020a0382166000908152602081905260409020548111156109f057600080fd5b600254610a03908263ffffffff61089b16565b600255600160a060020a038216600090815260208190526040902054610a2f908263ffffffff61089b16565b600160a060020a038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600160a060020a0382166000908152600160209081526040808320338452909152902054811115610ab457600080fd5b600160a060020a0382166000908152600160209081526040808320338452909152902054610ae8908263ffffffff61089b16565b600160a060020a038316600090815260016020908152604080832033845290915290205561071a82826109b6565b600160a060020a0381161515610b2b57600080fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600160a060020a0382161515610ba957600080fd5b600254610bbc908263ffffffff6109a416565b600255600160a060020a038216600090815260208190526040902054610be8908263ffffffff6109a416565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350505600a165627a7a72305820d4ad56dfe541fec48c3ecb02cebad565a998dfca7774c0c4f4b1f4a8e2363a590029".from_hex::>().unwrap(); - let gas_price = 50_000_000_000_u64; + let gas_price = to_wei(50); let gas_limit = 1_000_000_u64; let tx = TransactionParameters { nonce: Some(ethereum_types::U256::try_from(0).expect("Internal error")), @@ -351,13 +404,13 @@ fn transfer_service_fee_amount_to_address( chain: Chain, ) { let data = transaction_data_web3(to_wallet, amount_minor); - let gas_price = 150_000_000_000_u64; + let gas_price_wei = to_wei(150); let gas_limit = 1_000_000_u64; let tx = TransactionParameters { nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), to: Some(contract_addr), gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), - gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), + gas_price: Some(ethereum_types::U256::try_from(gas_price_wei).expect("Internal error")), value: ethereum_types::U256::zero(), data: Bytes(data.to_vec()), chain_id: Some(chain.rec().num_chain_id), @@ -401,13 +454,13 @@ fn transfer_transaction_fee_amount_to_address( transaction_nonce: u64, web3: &Web3, ) { - let gas_price = 150_000_000_000_u64; + let gas_price_wei = to_wei(150); let gas_limit = 1_000_000_u64; let tx = TransactionRequest { from: from_wallet.address(), to: Some(to_wallet.address()), gas: Some(ethereum_types::U256::try_from(gas_limit).expect("Internal error")), - gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), + gas_price: Some(ethereum_types::U256::try_from(gas_price_wei).expect("Internal error")), value: Some(ethereum_types::U256::from(amount_minor)), data: None, nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), @@ -447,7 +500,7 @@ fn transfer_transaction_fee_amount_to_address( fn assert_balances( wallet: &Wallet, - blockchain_interface: &BlockchainInterfaceWeb3, + blockchain_interface: &dyn BlockchainInterface, expected_eth_balance: u128, expected_token_balance: u128, ) { @@ -488,110 +541,15 @@ fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { } fn make_seed() -> Seed { - let phrase = "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle lamp absent write kind term toddler sphere ripple idle dragon curious hold"; + let phrase = "\ + timber cage wide hawk phone shaft pattern movie \ + army dizzy hen tackle lamp absent write kind \ + term toddler sphere ripple idle dragon curious hold"; let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); let seed = Seed::new(&mnemonic, ""); seed } -fn build_config( - server_url_holder: &dyn UrlHolder, - seed: &Seed, - node_by_role: NodeByRole, - test_inputs: &TestInputs, -) -> (NodeStartupConfig, Wallet) { - let wallet_derivation_path = node_by_role.derivation_path(); - let payment_thresholds = test_inputs.payment_thresholds_all_nodes; - let (node_wallet, node_secret) = make_node_wallet(seed, wallet_derivation_path.as_str()); - let cfg_to_build = NodeStartupConfigBuilder::standard() - .blockchain_service_url(server_url_holder.url()) - .chain(Chain::Dev) - .payment_thresholds(payment_thresholds) - .consuming_wallet_info(ConsumingWalletInfo::PrivateKey(node_secret)) - .earning_wallet_info(EarningWalletInfo::Address(format!( - "{}", - node_wallet.clone() - ))); - let cfg_to_build = if let Some(port) = test_inputs.port(node_by_role) { - cfg_to_build.ui_port(port) - } else { - cfg_to_build - }; - let cfg_to_build = if let Some(price) = test_inputs.consuming_node_gas_price_opt { - cfg_to_build.gas_price(price) - } else { - cfg_to_build - }; - - (cfg_to_build.build(), node_wallet) -} - -fn expire_payables( - path: PathBuf, - debts_config: &Either, - now: SystemTime, - serving_node_1_wallet: &Wallet, - serving_node_2_wallet: &Wallet, - serving_node_3_wallet: &Wallet, -) { - let conn = DbInitializerReal::default() - .initialize(&path, DbInitializationConfig::panic_on_migration()) - .unwrap(); - match debts_config { - Either::Left(_) => { - let _ = conn - .prepare( - "update payable set last_paid_timestamp = 0 where pending_payable_rowid is null", - ) - .unwrap() - .execute([]) - .unwrap(); - } - Either::Right(fully_specified_config) => vec![ - ( - serving_node_1_wallet, - fully_specified_config.owed_to_serving_node_1.age_s, - ), - ( - serving_node_2_wallet, - fully_specified_config.owed_to_serving_node_2.age_s, - ), - ( - serving_node_3_wallet, - fully_specified_config.owed_to_serving_node_3.age_s, - ), - ] - .iter() - .for_each(|(wallet, age_s)| { - let time_t = to_time_t(now.checked_sub(Duration::from_secs(*age_s)).unwrap()); - conn.prepare("update payable set last_paid_timestamp = ? where wallet_address = ?") - .unwrap() - .execute(&[&time_t as &dyn ToSql, &(wallet.to_string())]) - .unwrap(); - }), - } - - let mut config_stmt = conn - .prepare("update config set value = '0' where name = 'start_block'") - .unwrap(); - config_stmt.execute([]).unwrap(); -} - -fn expire_receivables(path: PathBuf) { - let conn = DbInitializerReal::default() - .initialize(&path, DbInitializationConfig::panic_on_migration()) - .unwrap(); - let mut statement = conn - .prepare("update receivable set last_received_timestamp = 0") - .unwrap(); - statement.execute([]).unwrap(); - - let mut config_stmt = conn - .prepare("update config set value = '0' where name = 'start_block'") - .unwrap(); - config_stmt.execute([]).unwrap(); -} - struct TestInputs { ui_ports_opt: Option, // The contract owner wallet is populated with 100 ETH as defined in the set of commands @@ -602,7 +560,7 @@ struct TestInputs { // Cannot ever get more than what the "owner" has. consuming_node_initial_transaction_fee_balance_minor_opt: Option, consuming_node_initial_service_fee_balance_minor: u128, - debts_config: Either, + debts_config: DebtsSpecs, payment_thresholds_all_nodes: PaymentThresholds, consuming_node_gas_price_opt: Option, } @@ -610,12 +568,16 @@ struct TestInputs { struct AssertionsValues { final_consuming_node_transaction_fee_balance_minor: u128, final_consuming_node_service_fee_balance_minor: u128, - final_service_fee_balance_serv_node_1_minor: u128, - final_service_fee_balance_serv_node_2_minor: u128, - final_service_fee_balance_serv_node_3_minor: u128, + final_service_fee_balances: FinalServiceFeeBalancesByNode, +} + +struct FinalServiceFeeBalancesByNode { + node_1_minor: u128, + node_2_minor: u128, + node_3_minor: u128, } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum NodeByRole { ConsumingNode = 1, ServingNode1 = 2, @@ -623,23 +585,50 @@ enum NodeByRole { ServingNode3 = 4, } -struct SimpleSimulatedDebts { - owed_to_serving_node_1_minor: u128, - owed_to_serving_node_2_minor: u128, - owed_to_serving_node_3_minor: u128, +struct BlockchainParams { + blockchain_interfaces: BlockchainInterfaces, + chain: Chain, + server_url: String, + contract_owner_addr: Address, + contract_owner_wallet: Wallet, + seed: Seed, +} + +struct BlockchainInterfaces { + blockchain_interface: Box, + web3: Web3, +} + +struct GlobalValues { + test_inputs: TestInputs, + blockchain_params: BlockchainParams, + now_in_common: SystemTime, +} + +struct WholesomeConfig { + global_values: GlobalValues, + consuming_node: ConsumingNodeAttributes, + serving_nodes: [ServingNodeAttributes; 3], } -struct FullySpecifiedSimulatedDebts { - owed_to_serving_node_1: AccountedDebt, - owed_to_serving_node_2: AccountedDebt, - owed_to_serving_node_3: AccountedDebt, +struct DebtsSpecs { + serving_node_1: AccountedDebt, + serving_node_2: AccountedDebt, + serving_node_3: AccountedDebt, } +#[derive(Copy, Clone)] struct AccountedDebt { balance_minor: u128, age_s: u64, } +impl AccountedDebt { + fn proper_timestamp(&self, now: SystemTime) -> SystemTime { + now.checked_sub(Duration::from_secs(self.age_s)).unwrap() + } +} + impl TestInputs { fn port(&self, requested: NodeByRole) -> Option { self.ui_ports_opt.as_ref().map(|ports| match requested { @@ -650,29 +639,317 @@ impl TestInputs { }) } - fn debt_size(&self, requested: NodeByRole) -> u128 { - match self.debts_config.as_ref() { - Either::Left(simple_config) => match requested { - NodeByRole::ServingNode1 => simple_config.owed_to_serving_node_1_minor, - NodeByRole::ServingNode2 => simple_config.owed_to_serving_node_2_minor, - NodeByRole::ServingNode3 => simple_config.owed_to_serving_node_3_minor, - NodeByRole::ConsumingNode => panic!( - "Version simple: These configs are \ - serve to set owed to the consuming node, while that one should not be here." - ), - }, - Either::Right(fully_specified) => match requested { - NodeByRole::ServingNode1 => fully_specified.owed_to_serving_node_1.balance_minor, - NodeByRole::ServingNode2 => fully_specified.owed_to_serving_node_2.balance_minor, - NodeByRole::ServingNode3 => fully_specified.owed_to_serving_node_3.balance_minor, - NodeByRole::ConsumingNode => panic!( - "Version fully specified: These configs \ - are serve to set owed to the consuming node, while that one should not \ + fn debt_specs(&self, requested: NodeByRole) -> AccountedDebt { + match requested { + NodeByRole::ServingNode1 => self.debts_config.serving_node_1, + NodeByRole::ServingNode2 => self.debts_config.serving_node_2, + NodeByRole::ServingNode3 => self.debts_config.serving_node_3, + NodeByRole::ConsumingNode => panic!( + "Version fully specified: These configs \ + describe debts owed to the consuming node, while that one should not \ be here." - ), + ), + } + } +} + +impl GlobalValues { + fn get_node_config_and_wallet(&self, node_by_role: NodeByRole) -> (NodeStartupConfig, Wallet) { + let wallet_derivation_path = node_by_role.derivation_path(); + let payment_thresholds = self.test_inputs.payment_thresholds_all_nodes; + let (node_wallet, node_secret) = make_node_wallet( + &self.blockchain_params.seed, + wallet_derivation_path.as_str(), + ); + let cfg_to_build = NodeStartupConfigBuilder::standard() + .blockchain_service_url(&self.blockchain_params.server_url) + .chain(Chain::Dev) + .payment_thresholds(payment_thresholds) + .consuming_wallet_info(ConsumingWalletInfo::PrivateKey(node_secret)) + .earning_wallet_info(EarningWalletInfo::Address(format!( + "{}", + node_wallet.clone() + ))); + let cfg_to_build = if let Some(port) = self.test_inputs.port(node_by_role) { + cfg_to_build.ui_port(port) + } else { + cfg_to_build + }; + let cfg_to_build = if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { + cfg_to_build.gas_price(price) + } else { + cfg_to_build + }; + + eprintln!("{:?} wallet established: {}\n", node_by_role, node_wallet,); + + (cfg_to_build.build(), node_wallet) + } + + fn prepare_consuming_node( + &self, + cluster: &mut MASQNodeCluster, + blockchain_interfaces: &BlockchainInterfaces, + ) -> ConsumingNodeAttributes { + let (consuming_node_config, consuming_node_wallet) = + self.get_node_config_and_wallet(NodeByRole::ConsumingNode); + let initial_transaction_fee_balance = self + .test_inputs + .consuming_node_initial_transaction_fee_balance_minor_opt + .unwrap_or(ONE_ETH_IN_WEI); + transfer_transaction_fee_amount_to_address( + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + initial_transaction_fee_balance, + 1, + &blockchain_interfaces.web3, + ); + transfer_service_fee_amount_to_address( + self.blockchain_params.contract_owner_addr, + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + self.test_inputs + .consuming_node_initial_service_fee_balance_minor, + 2, + &blockchain_interfaces.web3, + self.blockchain_params.chain, + ); + assert_balances( + &consuming_node_wallet, + blockchain_interfaces.blockchain_interface.as_ref(), + initial_transaction_fee_balance, + self.test_inputs + .consuming_node_initial_service_fee_balance_minor, + ); + let consuming_node_namings = cluster.prepare_real_node(&consuming_node_config); + let consuming_node_connection = DbInitializerReal::default() + .initialize( + Path::new(&consuming_node_namings.db_path), + make_init_config(cluster.chain), + ) + .unwrap(); + let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); + ConsumingNodeAttributes { + common: NodeAttributesCommon { + node_by_role: NodeByRole::ConsumingNode, + namings: consuming_node_namings, + config_opt: Some(consuming_node_config), }, + consuming_wallet: consuming_node_wallet, + payable_dao: consuming_node_payable_dao, + } + } + + fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { + [ + NodeByRole::ServingNode1, + NodeByRole::ServingNode2, + NodeByRole::ServingNode3, + ] + .into_iter() + .map(|node_by_role: NodeByRole| { + let (serving_node_config, serving_node_earning_wallet) = + self.get_node_config_and_wallet(node_by_role); + let serving_node_namings = cluster.prepare_real_node(&serving_node_config); + let serving_node_1_connection = DbInitializerReal::default() + .initialize( + &serving_node_namings.db_path, + make_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); + ServingNodeAttributes { + common: NodeAttributesCommon { + node_by_role, + namings: serving_node_namings, + config_opt: Some(serving_node_config), + }, + earning_wallet: serving_node_earning_wallet, + receivable_dao: serving_node_receivable_dao, + } + }) + .collect::>() + .try_into() + .expect("failed to make [T;3] of provided Vec") + } + + fn set_start_block_to_zero(path: &Path) { + //TODO do we want to do this?? + DbInitializerReal::default() + .initialize(path, DbInitializationConfig::panic_on_migration()) + .unwrap() + .prepare("update config set value = '0' where name = 'start_block'") + .unwrap() + .execute([]) + .unwrap(); + } + + fn serving_node_debt_balance_and_timestamp( + &self, + attributes: &ServingNodeAttributes, + ) -> (u128, SystemTime) { + let node_role = attributes.common.node_by_role; + let debt_specs = self.test_inputs.debt_specs(node_role); + ( + debt_specs.balance_minor, + debt_specs.proper_timestamp(self.now_in_common), + ) + } + + fn set_up_serving_nodes_databases( + &self, + serving_nodes_matrix: &[ServingNodeAttributes; 3], + consuming_node_attributes: &ConsumingNodeAttributes, + ) { + serving_nodes_matrix.iter().for_each(|node_attributes| { + let (balance, timestamp) = + self.serving_node_debt_balance_and_timestamp(node_attributes); + node_attributes + .receivable_dao + .more_money_receivable( + timestamp, + &consuming_node_attributes.consuming_wallet, + balance, + ) + .unwrap(); + assert_balances( + &node_attributes.earning_wallet, + self.blockchain_params + .blockchain_interfaces + .blockchain_interface + .as_ref(), + 0, + 0, + ); + Self::set_start_block_to_zero(&node_attributes.common.namings.db_path) + }) + } + + fn set_up_consuming_node_db( + &self, + serving_nodes_array: &[ServingNodeAttributes; 3], + consuming_node_attributes: &ConsumingNodeAttributes, + ) { + serving_nodes_array.iter().for_each(|node_attributes| { + let (balance, timestamp) = + self.serving_node_debt_balance_and_timestamp(node_attributes); + consuming_node_attributes + .payable_dao + .more_money_payable(timestamp, &node_attributes.earning_wallet, balance) + .unwrap(); + }); + Self::set_start_block_to_zero(&consuming_node_attributes.common.namings.db_path) + } +} + +impl WholesomeConfig { + fn new( + global_values: GlobalValues, + consuming_node: ConsumingNodeAttributes, + serving_nodes: [ServingNodeAttributes; 3], + ) -> Self { + WholesomeConfig { + global_values, + consuming_node, + serving_nodes, } } + + fn assert_expected_wallet_addresses(&self) { + let consuming_node_actual = self.consuming_node.consuming_wallet.to_string(); + let consuming_node_expected = "0x7a3cf474962646b18666b5a5be597bb0af013d81"; + assert_eq!( + &consuming_node_actual, consuming_node_expected, + "Consuming Node's wallet {} mismatched with expected {}", + consuming_node_actual, consuming_node_expected + ); + vec![ + "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6", + "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924", + "0xb45a33ef3e3097f34c826369b74141ed268cdb5a", + ] + .iter() + .zip(self.serving_nodes.iter()) + .for_each(|(expected_wallet_addr, serving_node_attributes)| { + let serving_node_actual = serving_node_attributes.earning_wallet.to_string(); + assert_eq!( + &serving_node_actual, + expected_wallet_addr, + "{:?} wallet {} mismatched with expected {}", + serving_node_attributes.common.node_by_role, + serving_node_actual, + expected_wallet_addr + ); + }) + } + + fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { + let blockchain_interface = self + .global_values + .blockchain_params + .blockchain_interfaces + .blockchain_interface + .as_ref(); + assert_balances( + &self.consuming_node.consuming_wallet, + blockchain_interface, + assertions_values.final_consuming_node_transaction_fee_balance_minor, + assertions_values.final_consuming_node_service_fee_balance_minor, + ); + assertions_values + .serving_nodes_final_values() + .into_iter() + .zip(self.serving_nodes.iter()) + .for_each(|(expected_remaining_owed_value, serving_node)| { + assert_balances( + &serving_node.earning_wallet, + blockchain_interface, + 0, + expected_remaining_owed_value, + ); + }) + } + + fn assert_serving_nodes_addressed_received_payments( + &self, + assertions_values: &AssertionsValues, + ) { + let final_values = assertions_values.serving_nodes_final_values(); + let consuming_node_wallet = &self.consuming_node.consuming_wallet; + self.serving_nodes + .iter() + .zip(final_values.into_iter()) + .for_each(|(serving_node, final_value)| { + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = serving_node + .receivable_dao + .account_status(&consuming_node_wallet) + { + status.balance_wei + == i128::try_from( + self.global_values + .test_inputs + .debt_specs(serving_node.common.node_by_role) + .balance_minor + - final_value, + ) + .unwrap() + } else { + false + } + }); + }) + } +} + +impl AssertionsValues { + fn serving_nodes_final_values(&self) -> [u128; 3] { + [ + self.final_service_fee_balances.node_1_minor, + self.final_service_fee_balances.node_2_minor, + self.final_service_fee_balances.node_3_minor, + ] + } } impl NodeByRole { @@ -690,324 +967,93 @@ struct Ports { serving_node_3: u16, } -struct ServingNodeAttributes { +#[derive(Debug)] +struct NodeAttributesCommon { node_by_role: NodeByRole, - name: String, - index: usize, - config: NodeStartupConfig, + namings: NodeNamingAndDir, + config_opt: Option, } -fn test_body( +#[derive(Debug)] +struct ConsumingNodeAttributes { + common: NodeAttributesCommon, + consuming_wallet: Wallet, + payable_dao: PayableDaoReal, +} + +#[derive(Debug)] +struct ServingNodeAttributes { + common: NodeAttributesCommon, + earning_wallet: Wallet, + receivable_dao: ReceivableDaoReal, +} + +type StimulateConsumingNodePayments<'a> = + Box; +type StartServingNodesAndLetThemPerformReceivablesCheck<'a> = Box< + dyn FnOnce( + &mut MASQNodeCluster, + &mut [ServingNodeAttributes; 3], + &GlobalValues, + ) -> [MASQRealNode; 3] + + 'a, +>; + +fn test_body( test_inputs: TestInputs, assertions_values: AssertionsValues, stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, -) where - StimulateConsumingNodePayments: FnOnce(&mut MASQNodeCluster, &MASQRealNode, &TestInputs), - StartServingNodesAndLetThemPerformReceivablesCheck: - FnOnce(&mut MASQNodeCluster, [ServingNodeAttributes; 3], &TestInputs) -> [MASQRealNode; 3], -{ - let mut cluster = match MASQNodeCluster::start() { - Ok(cluster) => cluster, - Err(e) => panic!("{}", e), - }; - let blockchain_server = BlockchainServer { - name: "ganache-cli", - }; - blockchain_server.start(); - blockchain_server.wait_until_ready(); - let url = blockchain_server.url().to_string(); - let (event_loop_handle, http) = Http::with_max_parallel(&url, REQUESTS_IN_PARALLEL).unwrap(); - let web3 = Web3::new(http.clone()); - let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); - let contract_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - assert_eq!( - contract_addr, - cluster.chain.rec().contract, - "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ - Resulted contact addr {:?} doesn't much what's expected: {:?}", - contract_addr, - cluster.chain.rec().contract - ); - let blockchain_interface = BlockchainInterfaceWeb3::new(http, event_loop_handle, cluster.chain); - let (consuming_config, consuming_node_wallet) = build_config( - &blockchain_server, - &seed, - NodeByRole::ConsumingNode, - &test_inputs, - ); - eprintln!( - "Consuming node wallet established: {}\n", - consuming_node_wallet - ); - let initial_transaction_fee_balance = test_inputs - .consuming_node_initial_transaction_fee_balance_minor_opt - .unwrap_or(ONE_ETH_IN_WEI); - transfer_transaction_fee_amount_to_address( - &contract_owner_wallet, - &consuming_node_wallet, - initial_transaction_fee_balance, - 1, - &web3, - ); - transfer_service_fee_amount_to_address( - contract_addr, - &contract_owner_wallet, - &consuming_node_wallet, - test_inputs.consuming_node_initial_service_fee_balance_minor, - 2, - &web3, - cluster.chain, - ); - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - initial_transaction_fee_balance, - test_inputs.consuming_node_initial_service_fee_balance_minor, - ); - let (serving_node_1_config, serving_node_1_wallet) = build_config( - &blockchain_server, - &seed, - NodeByRole::ServingNode1, - &test_inputs, - ); - eprintln!( - "First serving node wallet established: {}\n", - serving_node_1_wallet - ); - let (serving_node_2_config, serving_node_2_wallet) = build_config( - &blockchain_server, - &seed, - NodeByRole::ServingNode2, - &test_inputs, - ); - eprintln!( - "Second serving node wallet established: {}\n", - serving_node_2_wallet - ); - let (serving_node_3_config, serving_node_3_wallet) = build_config( - &blockchain_server, - &seed, - NodeByRole::ServingNode3, - &test_inputs, - ); - eprintln!( - "Third serving node wallet established: {}\n", - serving_node_3_wallet - ); - let (consuming_node_name, consuming_node_index) = cluster.prepare_real_node(&consuming_config); - let consuming_node_path = node_chain_specific_data_directory(&consuming_node_name); - let consuming_node_connection = DbInitializerReal::default() - .initialize( - Path::new(&consuming_node_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); - open_all_file_permissions(consuming_node_path.clone().into()); - assert_eq!( - format!("{}", &consuming_node_wallet), - "0x7a3cf474962646b18666b5a5be597bb0af013d81" - ); - assert_eq!( - format!("{}", &serving_node_1_wallet), - "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6" - ); - assert_eq!( - format!("{}", &serving_node_2_wallet), - "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924" +) { + let (mut cluster, global_values) = establish_test_frame(test_inputs); + let consuming_node_attributes = global_values.prepare_consuming_node( + &mut cluster, + &global_values.blockchain_params.blockchain_interfaces, ); - assert_eq!( - format!("{}", &serving_node_3_wallet), - "0xb45a33ef3e3097f34c826369b74141ed268cdb5a" + let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); + global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node_attributes); + global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node_attributes); + let mut wholesome_config = WholesomeConfig::new( + global_values, + consuming_node_attributes, + serving_nodes_array, ); - let now = SystemTime::now(); - consuming_node_payable_dao - .more_money_payable( - now, - &serving_node_1_wallet, - test_inputs.debt_size(NodeByRole::ServingNode1), - ) - .unwrap(); - consuming_node_payable_dao - .more_money_payable( - now, - &serving_node_2_wallet, - test_inputs.debt_size(NodeByRole::ServingNode2), - ) - .unwrap(); - consuming_node_payable_dao - .more_money_payable( - now, - &serving_node_3_wallet, - test_inputs.debt_size(NodeByRole::ServingNode3), - ) - .unwrap(); - let (serving_node_1_name, serving_node_1_index) = - cluster.prepare_real_node(&serving_node_1_config); - let serving_node_1_path = node_chain_specific_data_directory(&serving_node_1_name); - let serving_node_1_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_1_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_1_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); - serving_node_1_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - test_inputs.debt_size(NodeByRole::ServingNode1), - ) - .unwrap(); - open_all_file_permissions(serving_node_1_path.clone().into()); - let (serving_node_2_name, serving_node_2_index) = - cluster.prepare_real_node(&serving_node_2_config); - let serving_node_2_path = node_chain_specific_data_directory(&serving_node_2_name); - let serving_node_2_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_2_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_2_receivable_dao = ReceivableDaoReal::new(serving_node_2_connection); - serving_node_2_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - test_inputs.debt_size(NodeByRole::ServingNode2), - ) - .unwrap(); - open_all_file_permissions(serving_node_2_path.clone().into()); - let (serving_node_3_name, serving_node_3_index) = - cluster.prepare_real_node(&serving_node_3_config); - let serving_node_3_path = node_chain_specific_data_directory(&serving_node_3_name); - let serving_node_3_connection = DbInitializerReal::default() - .initialize( - Path::new(&serving_node_3_path), - make_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_3_receivable_dao = ReceivableDaoReal::new(serving_node_3_connection); - serving_node_3_receivable_dao - .more_money_receivable( - SystemTime::now(), - &consuming_node_wallet, - test_inputs.debt_size(NodeByRole::ServingNode3), - ) - .unwrap(); - open_all_file_permissions(serving_node_3_path.clone().into()); - expire_payables( - consuming_node_path.into(), - &test_inputs.debts_config, - now, - &serving_node_1_wallet, - &serving_node_2_wallet, - &serving_node_3_wallet, + wholesome_config.assert_expected_wallet_addresses(); + let real_consuming_node = cluster.start_named_real_node( + &wholesome_config.consuming_node.common.namings.node_name, + wholesome_config.consuming_node.common.namings.index, + wholesome_config + .consuming_node + .common + .config_opt + .take() + .unwrap(), ); - expire_receivables(serving_node_1_path.into()); - expire_receivables(serving_node_2_path.into()); - expire_receivables(serving_node_3_path.into()); - assert_balances(&serving_node_1_wallet, &blockchain_interface, 0, 0); - assert_balances(&serving_node_2_wallet, &blockchain_interface, 0, 0); - assert_balances(&serving_node_3_wallet, &blockchain_interface, 0, 0); - let real_consuming_node = - cluster.start_named_real_node(&consuming_node_name, consuming_node_index, consuming_config); - stimulate_consuming_node_to_pay(&mut cluster, &real_consuming_node, &test_inputs); + stimulate_consuming_node_to_pay( + &mut cluster, + &real_consuming_node, + &wholesome_config.global_values, + ); let now = Instant::now(); - while !consuming_node_payable_dao.non_pending_payables().is_empty() + while !wholesome_config + .consuming_node + .payable_dao + .non_pending_payables() + .is_empty() && now.elapsed() < Duration::from_secs(10) { thread::sleep(Duration::from_millis(400)); } - assert_balances( - &consuming_node_wallet, - &blockchain_interface, - assertions_values.final_consuming_node_transaction_fee_balance_minor, - assertions_values.final_consuming_node_service_fee_balance_minor, - ); - assert_balances( - &serving_node_1_wallet, - &blockchain_interface, - 0, - assertions_values.final_service_fee_balance_serv_node_1_minor, - ); - assert_balances( - &serving_node_2_wallet, - &blockchain_interface, - 0, - assertions_values.final_service_fee_balance_serv_node_2_minor, - ); - assert_balances( - &serving_node_3_wallet, - &blockchain_interface, - 0, - assertions_values.final_service_fee_balance_serv_node_3_minor, - ); - let serving_nodes_attributes = [ - ServingNodeAttributes { - node_by_role: NodeByRole::ServingNode1, - name: serving_node_1_name.to_string(), - index: serving_node_1_index, - config: serving_node_1_config, - }, - ServingNodeAttributes { - node_by_role: NodeByRole::ServingNode2, - name: serving_node_2_name.to_string(), - index: serving_node_2_index, - config: serving_node_2_config, - }, - ServingNodeAttributes { - node_by_role: NodeByRole::ServingNode3, - name: serving_node_3_name.to_string(), - index: serving_node_3_index, - config: serving_node_3_config, - }, - ]; + wholesome_config.assert_payments_via_direct_blockchain_scanning(&assertions_values); let _ = start_serving_nodes_and_activate_their_accountancy( &mut cluster, - serving_nodes_attributes, - &test_inputs, + // So that individual Configs can be pulled out and used + &mut wholesome_config.serving_nodes, + &wholesome_config.global_values, ); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_1_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei - == i128::try_from( - test_inputs.debt_size(NodeByRole::ServingNode1) - - assertions_values.final_service_fee_balance_serv_node_1_minor, - ) - .unwrap() - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_2_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei - == i128::try_from( - test_inputs.debt_size(NodeByRole::ServingNode2) - - assertions_values.final_service_fee_balance_serv_node_2_minor, - ) - .unwrap() - } else { - false - } - }); - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node_3_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei - == i128::try_from( - test_inputs.debt_size(NodeByRole::ServingNode3) - - assertions_values.final_service_fee_balance_serv_node_3_minor, - ) - .unwrap() - } else { - false - } - }); + wholesome_config.assert_serving_nodes_addressed_received_payments(&assertions_values) } From d4c15bd78c755df7ce762ea75858d58c5874f0d5 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 19 Sep 2024 15:35:57 +0200 Subject: [PATCH 198/250] GH-711-review-one: adding directives to ignore files --- automap/.gitignore | 5 +++++ dns_utility/.gitignore | 5 +++++ masq/.gitignore | 5 +++++ multinode_integration_tests/.gitignore | 5 +++++ port_exposer/.gitignore | 5 +++++ 5 files changed, 25 insertions(+) create mode 100644 masq/.gitignore create mode 100644 multinode_integration_tests/.gitignore create mode 100644 port_exposer/.gitignore diff --git a/automap/.gitignore b/automap/.gitignore index b41078a47..0f6e0549c 100644 --- a/automap/.gitignore +++ b/automap/.gitignore @@ -1 +1,6 @@ automap.log + +## File-based project format: +*.iws +*.iml +*.ipr \ No newline at end of file diff --git a/dns_utility/.gitignore b/dns_utility/.gitignore index 9ab870da8..67cedea71 100644 --- a/dns_utility/.gitignore +++ b/dns_utility/.gitignore @@ -1 +1,6 @@ generated/ + +## File-based project format: +*.iws +*.iml +*.ipr diff --git a/masq/.gitignore b/masq/.gitignore new file mode 100644 index 000000000..e0264b089 --- /dev/null +++ b/masq/.gitignore @@ -0,0 +1,5 @@ + +## File-based project format: +*.iws +*.iml +*.ipr \ No newline at end of file diff --git a/multinode_integration_tests/.gitignore b/multinode_integration_tests/.gitignore new file mode 100644 index 000000000..e0264b089 --- /dev/null +++ b/multinode_integration_tests/.gitignore @@ -0,0 +1,5 @@ + +## File-based project format: +*.iws +*.iml +*.ipr \ No newline at end of file diff --git a/port_exposer/.gitignore b/port_exposer/.gitignore new file mode 100644 index 000000000..e0264b089 --- /dev/null +++ b/port_exposer/.gitignore @@ -0,0 +1,5 @@ + +## File-based project format: +*.iws +*.iml +*.ipr \ No newline at end of file From f1924243e757631c590c38042e76ec452589ba9a Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 21 Sep 2024 23:31:51 +0200 Subject: [PATCH 199/250] GH-711-review-one: more work done on verify_bill_payments; savepoint --- .../tests/verify_bill_payment.rs | 515 ++++++++++-------- .../preparatory_analyser/mod.rs | 4 +- .../payable_scanner/agent_null.rs | 6 +- .../payable_scanner/agent_web3.rs | 6 +- .../payable_scanner/blockchain_agent.rs | 4 +- .../blockchain_interface_web3/mod.rs | 4 +- node/src/test_utils/database_utils.rs | 2 +- node/src/test_utils/mod.rs | 24 +- .../database_version_0_sqls.txt | 0 .../smart_contract_for_on_blockchain_test | 60 ++ 10 files changed, 379 insertions(+), 246 deletions(-) rename node/src/test_utils/{input_data => test_input_data}/database_version_0_sqls.txt (100%) create mode 100644 node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 59d5b9fc5..8e1636628 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -5,7 +5,7 @@ use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node::MASQNode; @@ -32,8 +32,11 @@ use node_lib::sub_lib::blockchain_interface_web3::{ }; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; +use node_lib::test_utils::standard_dir_for_test_input_data; use rustc_hex::{FromHex, ToHex}; use std::convert::TryFrom; +use std::fs::File; +use std::io::Read; use std::path::Path; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::{thread, u128}; @@ -43,15 +46,16 @@ use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, Tran use web3::Web3; #[test] -fn verify_bill_payment() { +fn full_payments_were_processed_for_sufficient_balances() { // Note: besides the main objectives of this test, it relies on (and so it proves) the premise - // that each Node, after it reaches its full connectivity and becomes able to make a route, - // activates its accountancy module whereas it also unleashes the first cycle of the scanners - // immediately. That's why some consideration has been made not to take out the passage with - // the intense startups of a bunch of Nodes, with that particular reason to fulfill the above - // depicted scenario, even though this test could be written more simply with the use of - // the `scans` command emitted from a UI, with a smaller CPU burden. (You may want to know that - // such an approach is implemented for another test in this file.) + // that each Node, after it achieves an effective connectivity as making a route is enabled, + // activates the accountancy module whereas the first cycle of scanners is unleashed. That's + // an excuse hopefully good enough not to take out the passage in this test with the intense + // startup of a bunch of real Nodes, with the only purpose of fulfilling the conditions required + // for going through that above depicted sequence of events. That said, this test could've been + // written simpler with an emulated UI and its `scans` command, lowering the CPU burden. + // (You may be pleased to know that such an approach is implemented for another test in this + // file.) let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_500_000, debt_threshold_gwei: 1_000_000_000, @@ -71,15 +75,15 @@ fn verify_bill_payment() { consuming_node_initial_transaction_fee_balance_minor_opt: None, consuming_node_initial_service_fee_balance_minor, debts_config: DebtsSpecs { - serving_node_1: AccountedDebt { + serving_node_1: Debt { balance_minor: owed_to_serving_node_1_minor, age_s: long_ago, }, - serving_node_2: AccountedDebt { + serving_node_2: Debt { balance_minor: owed_to_serving_node_2_minor, age_s: long_ago, }, - serving_node_3: AccountedDebt { + serving_node_3: Debt { balance_minor: owed_to_serving_node_3_minor, age_s: long_ago, }, @@ -102,59 +106,62 @@ fn verify_bill_payment() { }, }; - let stimulate_payments: StimulateConsumingNodePayments = - Box::new(|cluster, real_consuming_node, _test_inputs| { - for _ in 0..6 { - cluster.start_real_node( - NodeStartupConfigBuilder::standard() - .chain(Chain::Dev) - .neighbor(real_consuming_node.node_reference()) - .build(), - ); - } - }); - - let start_serving_nodes_and_activate_their_accountancy : StartServingNodesAndLetThemPerformReceivablesCheck = Box::new( - |cluster, - serving_nodes, - _wholesome_config| { - let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes - .into_iter() - .map(|attributes| { - let namings = &attributes.common.namings; - cluster.start_named_real_node( - &namings.node_name, - namings.index, - attributes.common.config_opt.take().unwrap(), - ) - }) - .map(|node| (node.node_reference(), node)) - .unzip(); - let auxiliary_node_config_builder = - NodeStartupConfigBuilder::standard().chain(Chain::Dev); - let auxiliary_node_config = node_references - .into_iter() - .fold( - auxiliary_node_config_builder, - |builder, serving_node_reference| builder.neighbor(serving_node_reference), - ) - .build(); - - for _ in 0..3 { - let _ = cluster.start_real_node(auxiliary_node_config.clone()); - } - - serving_nodes.try_into().unwrap() - }); - test_body( test_inputs, assertions_values, - stimulate_payments, - start_serving_nodes_and_activate_their_accountancy, + stimulate_consuming_node_to_pay_for_test_with_sufficient_funds, + activating_serving_nodes_for_test_with_sufficient_funds, ); } +fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds<'a>( + cluster: &'a mut MASQNodeCluster, + real_consuming_node: &'a MASQRealNode, + _global_values: &'a GlobalValues, +) { + for _ in 0..6 { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .chain(Chain::Dev) + .neighbor(real_consuming_node.node_reference()) + .build(), + ); + } +} + +fn activating_serving_nodes_for_test_with_sufficient_funds<'a>( + cluster: &'a mut MASQNodeCluster, + serving_nodes: &'a mut [ServingNodeAttributes; 3], + _global_values: &'a GlobalValues, +) -> [MASQRealNode; 3] { + let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes + .into_iter() + .map(|attributes| { + let namings = &attributes.common.namings; + cluster.start_named_real_node( + &namings.node_name, + namings.index, + attributes.common.config_opt.take().unwrap(), + ) + }) + .map(|node| (node.node_reference(), node)) + .unzip(); + let auxiliary_node_config = node_references + .into_iter() + .fold( + NodeStartupConfigBuilder::standard().chain(Chain::Dev), + |builder, serving_node_reference| builder.neighbor(serving_node_reference), + ) + .build(); + + // Should be enough additional Nodes to provide the full connectivity + for _ in 0..3 { + let _ = cluster.start_real_node(auxiliary_node_config.clone()); + } + + serving_nodes.try_into().unwrap() +} + #[test] fn payments_were_adjusted_due_to_insufficient_balances() { let payment_thresholds = PaymentThresholds { @@ -165,34 +172,41 @@ fn payments_were_adjusted_due_to_insufficient_balances() { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 1_000_000, }; - + // Assuming all Nodes rely on the same set of payment thresholds let owed_to_serv_node_1_minor = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); let owed_to_serv_node_2_minor = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); + // Account of Node 3 will be a victim of tx fee insufficiency and will fall away, as its debt + // is the heaviest, implying the smallest weight evaluated and the last priority compared to + // those two others. let owed_to_serv_node_3_minor = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); - // Assuming all Nodes rely on the same set of payment thresholds - let consuming_node_initial_service_fee_balance_minor = (owed_to_serv_node_1_minor - + owed_to_serv_node_2_minor) - - gwei_to_wei::(2_345_678); + let enough_balance_for_serving_node_1_and_2 = + owed_to_serv_node_1_minor + owed_to_serv_node_2_minor; + let consuming_node_initial_service_fee_balance_minor = + enough_balance_for_serving_node_1_and_2 - gwei_to_wei::(2_345_678_u64); let agreed_transaction_fee_unit_price_major = 60; - let transaction_fee_needed_to_pay_for_one_payment_major = { - // We will need less but this should be accurate enough - let txn_data_with_maximized_cost = [0xff; 68]; + let tx_fee_needed_to_pay_for_one_payment_major = { + // We'll need littler funds, but we can stand mild inaccuracy from assuming the use of + // all nonzero bytes in the data in both txs, which represents maximized costs + let txn_data_with_maximized_costs = [0xff; 68]; let gas_limit_dev_chain = { let const_part = web3_gas_limit_const_part(Chain::Dev); u64::try_from(compute_gas_limit( const_part, - txn_data_with_maximized_cost.as_slice(), + txn_data_with_maximized_costs.as_slice(), )) .unwrap() }; - let transaction_fee_margin = Percentage::new(15); + let transaction_fee_margin = PurePercentage::try_from(15).unwrap(); transaction_fee_margin .add_percent_to(gas_limit_dev_chain * agreed_transaction_fee_unit_price_major) }; + const AFFORDABLE_PAYMENTS_COUNT: u128 = 2; + let tx_fee_needed_to_pay_for_one_payment_minor: u128 = + gwei_to_wei(tx_fee_needed_to_pay_for_one_payment_major); let consuming_node_transaction_fee_balance_minor = - 2 * gwei_to_wei::(transaction_fee_needed_to_pay_for_one_payment_major); + AFFORDABLE_PAYMENTS_COUNT * tx_fee_needed_to_pay_for_one_payment_minor; let test_inputs = TestInputs { ui_ports_opt: Some(Ports { consuming_node: find_free_port(), @@ -202,23 +216,23 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }), // Should be enough only for two payments, the least significant one will fall out consuming_node_initial_transaction_fee_balance_minor_opt: Some( - consuming_node_transaction_fee_balance_minor + 1, + consuming_node_transaction_fee_balance_minor, ), consuming_node_initial_service_fee_balance_minor, debts_config: DebtsSpecs { // This account will be the most significant and will deserve the full balance - serving_node_1: AccountedDebt { + serving_node_1: Debt { balance_minor: owed_to_serv_node_1_minor, age_s: payment_thresholds.maturity_threshold_sec + 1000, }, // This balance is of a middle size it will be reduced as there won't be enough // after the first one is filled up. - serving_node_2: AccountedDebt { + serving_node_2: Debt { balance_minor: owed_to_serv_node_2_minor, age_s: payment_thresholds.maturity_threshold_sec + 100_000, }, - // This account will be the least significant and therefore eliminated - serving_node_3: AccountedDebt { + // This account will be the least significant, therefore eliminated due to tx fee + serving_node_3: Debt { balance_minor: owed_to_serv_node_3_minor, age_s: payment_thresholds.maturity_threshold_sec + 30_000, }, @@ -228,8 +242,8 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }; let assertions_values = AssertionsValues { - // It seems like the ganache server sucked up quite less than those 55_000 units of gas?? - final_consuming_node_transaction_fee_balance_minor: 2_828_352_000_000_001, + // How much is left after the smart contract was successfully executed, those three payments + final_consuming_node_transaction_fee_balance_minor: 2_828_352_000_000_000, // Zero reached, because the algorithm is designed to exhaust the wallet completely final_consuming_node_service_fee_balance_minor: 0, // This account was granted with the full size as its lowest balance from the set makes @@ -242,65 +256,76 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }, }; - let process_scan_request_to_node = - |real_node: &MASQRealNode, ui_port: u16, scan_type: ScanType, context_id: u64| { - let ui_client = real_node.make_ui(ui_port); - ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); - let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); - UiScanResponse::fmb(response).unwrap(); - }; - - let stimulate_payments: StimulateConsumingNodePayments = - Box::new(|_cluster, real_consuming_node, global_values| { - process_scan_request_to_node( - &real_consuming_node, - global_values - .test_inputs - .port(NodeByRole::ConsumingNode) - .unwrap(), - ScanType::Payables, - 1111, - ) - }); - - let start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck = Box::new(|cluster, - serving_nodes_attributes, global_values| { - let real_nodes: Vec<_> = serving_nodes_attributes - .iter_mut() - .enumerate() - .map(|(idx, serving_node_attributes)| { - let node_config = serving_node_attributes.common.config_opt.take().unwrap(); - let common = &serving_node_attributes.common; - let serving_node = cluster.start_named_real_node( - &common.namings.node_name, - common.namings.index, - node_config, - ); - let ui_port = global_values.test_inputs - .port(common.node_by_role) - .expect("ui port missing"); - - process_scan_request_to_node( - &serving_node, - ui_port, - ScanType::Receivables, - (idx * 111) as u64, - ); - - serving_node - }) - .collect(); - real_nodes.try_into().unwrap() - }); - test_body( test_inputs, assertions_values, - stimulate_payments, - start_serving_nodes_and_activate_their_accountancy, + stimulate_consuming_node_to_pay_for_test_with_insufficient_funds, + activating_serving_nodes_for_test_with_insufficient_funds, ); } +fn stimulate_consuming_node_to_pay_for_test_with_insufficient_funds( + _cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + global_values: &GlobalValues, +) { + process_scan_request_to_node( + &real_consuming_node, + global_values + .test_inputs + .port(NodeByRole::ConsumingNode) + .unwrap(), + ScanType::Payables, + 1111, + ) +} + +fn activating_serving_nodes_for_test_with_insufficient_funds( + cluster: &mut MASQNodeCluster, + serving_nodes: &mut [ServingNodeAttributes; 3], + global_values: &GlobalValues, +) -> [MASQRealNode; 3] { + let real_nodes: Vec<_> = serving_nodes + .iter_mut() + .enumerate() + .map(|(idx, serving_node_attributes)| { + let node_config = serving_node_attributes.common.config_opt.take().unwrap(); + let common = &serving_node_attributes.common; + let serving_node = cluster.start_named_real_node( + &common.namings.node_name, + common.namings.index, + node_config, + ); + let ui_port = global_values + .test_inputs + .port(common.node_by_role) + .expect("ui port missing"); + + process_scan_request_to_node( + &serving_node, + ui_port, + ScanType::Receivables, + (idx * 111) as u64, + ); + + serving_node + }) + .collect(); + real_nodes.try_into().unwrap() +} + +fn process_scan_request_to_node( + real_node: &MASQRealNode, + ui_port: u16, + scan_type: ScanType, + context_id: u64, +) { + let ui_client = real_node.make_ui(ui_port); + ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); + let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); + UiScanResponse::fmb(response).expect("Scan request went wrong"); +} + fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, GlobalValues) { let now = SystemTime::now(); let cluster = match MASQNodeCluster::start() { @@ -356,7 +381,7 @@ fn to_wei(gwei: u64) -> u128 { gwei_to_wei(gwei) } -fn make_init_config(chain: Chain) -> DbInitializationConfig { +fn make_db_init_config(chain: Chain) -> DbInitializationConfig { DbInitializationConfig::create_or_migrate(ExternalData::new( chain, NeighborhoodModeLight::Standard, @@ -364,8 +389,22 @@ fn make_init_config(chain: Chain) -> DbInitializationConfig { )) } +fn load_contract_in_bytes() -> Vec { + let file_path = + standard_dir_for_test_input_data().join("smart_contract_for_on_blockchain_test"); + let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); + let mut data = String::new(); + file.read_to_string(&mut data).unwrap(); + let data = data + .chars() + .filter(|char| !char.is_whitespace()) + .collect::(); + data.from_hex::>() + .expect("bad contract: contains non-hexadecimal characters") +} + fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { - let data = "608060405234801561001057600080fd5b5060038054600160a060020a031916331790819055604051600160a060020a0391909116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3610080336b01866de34549d620d8000000640100000000610b9461008582021704565b610156565b600160a060020a038216151561009a57600080fd5b6002546100b490826401000000006109a461013d82021704565b600255600160a060020a0382166000908152602081905260409020546100e790826401000000006109a461013d82021704565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60008282018381101561014f57600080fd5b9392505050565b610c6a806101656000396000f3006080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610100578063095ea7b31461018a57806318160ddd146101c257806323b872dd146101e95780632ff2e9dc14610213578063313ce56714610228578063395093511461025357806342966c681461027757806370a0823114610291578063715018a6146102b257806379cc6790146102c75780638da5cb5b146102eb5780638f32d59b1461031c57806395d89b4114610331578063a457c2d714610346578063a9059cbb1461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561014f578181015183820152602001610137565b50505050905090810190601f16801561017c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019657600080fd5b506101ae600160a060020a0360043516602435610436565b604080519115158252519081900360200190f35b3480156101ce57600080fd5b506101d7610516565b60408051918252519081900360200190f35b3480156101f557600080fd5b506101ae600160a060020a036004358116906024351660443561051c565b34801561021f57600080fd5b506101d76105b9565b34801561023457600080fd5b5061023d6105c9565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b506101ae600160a060020a03600435166024356105ce565b34801561028357600080fd5b5061028f60043561067e565b005b34801561029d57600080fd5b506101d7600160a060020a036004351661068b565b3480156102be57600080fd5b5061028f6106a6565b3480156102d357600080fd5b5061028f600160a060020a0360043516602435610710565b3480156102f757600080fd5b5061030061071e565b60408051600160a060020a039092168252519081900360200190f35b34801561032857600080fd5b506101ae61072d565b34801561033d57600080fd5b5061011561073e565b34801561035257600080fd5b506101ae600160a060020a0360043516602435610775565b34801561037657600080fd5b506101ae600160a060020a03600435166024356107c0565b34801561039a57600080fd5b506101d7600160a060020a03600435811690602435166107d6565b3480156103c157600080fd5b5061028f600160a060020a0360043516610801565b606060405190810160405280602481526020017f486f7420746865206e657720746f6b656e20796f75277265206c6f6f6b696e6781526020017f20666f720000000000000000000000000000000000000000000000000000000081525081565b600081158061044c575061044a33846107d6565b155b151561050557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f55736520696e637265617365417070726f76616c206f7220646563726561736560448201527f417070726f76616c20746f2070726576656e7420646f75626c652d7370656e6460648201527f2e00000000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b61050f838361081d565b9392505050565b60025490565b600160a060020a038316600090815260016020908152604080832033845290915281205482111561054c57600080fd5b600160a060020a0384166000908152600160209081526040808320338452909152902054610580908363ffffffff61089b16565b600160a060020a03851660009081526001602090815260408083203384529091529020556105af8484846108b2565b5060019392505050565b6b01866de34549d620d800000081565b601281565b6000600160a060020a03831615156105e557600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff6109a416565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b61068833826109b6565b50565b600160a060020a031660009081526020819052604090205490565b6106ae61072d565b15156106b957600080fd5b600354604051600091600160a060020a0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36003805473ffffffffffffffffffffffffffffffffffffffff19169055565b61071a8282610a84565b5050565b600354600160a060020a031690565b600354600160a060020a0316331490565b60408051808201909152600381527f484f540000000000000000000000000000000000000000000000000000000000602082015281565b6000600160a060020a038316151561078c57600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff61089b16565b60006107cd3384846108b2565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b61080961072d565b151561081457600080fd5b61068881610b16565b6000600160a060020a038316151561083457600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b600080838311156108ab57600080fd5b5050900390565b600160a060020a0383166000908152602081905260409020548111156108d757600080fd5b600160a060020a03821615156108ec57600080fd5b600160a060020a038316600090815260208190526040902054610915908263ffffffff61089b16565b600160a060020a03808516600090815260208190526040808220939093559084168152205461094a908263ffffffff6109a416565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282018381101561050f57600080fd5b600160a060020a03821615156109cb57600080fd5b600160a060020a0382166000908152602081905260409020548111156109f057600080fd5b600254610a03908263ffffffff61089b16565b600255600160a060020a038216600090815260208190526040902054610a2f908263ffffffff61089b16565b600160a060020a038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600160a060020a0382166000908152600160209081526040808320338452909152902054811115610ab457600080fd5b600160a060020a0382166000908152600160209081526040808320338452909152902054610ae8908263ffffffff61089b16565b600160a060020a038316600090815260016020908152604080832033845290915290205561071a82826109b6565b600160a060020a0381161515610b2b57600080fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600160a060020a0382161515610ba957600080fd5b600254610bbc908263ffffffff6109a416565b600255600160a060020a038216600090815260208190526040902054610be8908263ffffffff6109a416565b600160a060020a0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350505600a165627a7a72305820d4ad56dfe541fec48c3ecb02cebad565a998dfca7774c0c4f4b1f4a8e2363a590029".from_hex::>().unwrap(); + let contract = load_contract_in_bytes(); let gas_price = to_wei(50); let gas_limit = 1_000_000_u64; let tx = TransactionParameters { @@ -374,10 +413,10 @@ fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Ad gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), value: ethereum_types::U256::zero(), - data: Bytes(data), + data: Bytes(contract), chain_id: Some(chain.rec().num_chain_id), }; - let signed_tx = sign_transaction(web3, tx, wallet); + let signed_tx = primitive_sign_transaction(web3, tx, wallet); match web3 .eth() .send_raw_transaction(signed_tx.raw_transaction) @@ -415,9 +454,7 @@ fn transfer_service_fee_amount_to_address( data: Bytes(data.to_vec()), chain_id: Some(chain.rec().num_chain_id), }; - - let signed_tx = sign_transaction(web3, tx, from_wallet); - + let signed_tx = primitive_sign_transaction(web3, tx, from_wallet); match web3 .eth() .send_raw_transaction(signed_tx.raw_transaction) @@ -431,7 +468,7 @@ fn transfer_service_fee_amount_to_address( } } -fn sign_transaction( +fn primitive_sign_transaction( web3: &Web3, tx: TransactionParameters, signing_wallet: &Wallet, @@ -466,7 +503,6 @@ fn transfer_transaction_fee_amount_to_address( nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), condition: None, }; - match web3 .personal() .unlock_account(from_wallet.address(), "", None) @@ -488,7 +524,6 @@ fn transfer_transaction_fee_amount_to_address( e ), } - match web3.eth().send_transaction(tx).wait() { Ok(tx_hash) => eprintln!( "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", @@ -540,14 +575,13 @@ fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { ) } +const MNEMONIC_PHRASE: &str = + "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ + lamp absent write kind term toddler sphere ripple idle dragon curious hold"; + fn make_seed() -> Seed { - let phrase = "\ - timber cage wide hawk phone shaft pattern movie \ - army dizzy hen tackle lamp absent write kind \ - term toddler sphere ripple idle dragon curious hold"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); - let seed = Seed::new(&mnemonic, ""); - seed + let mnemonic = Mnemonic::from_phrase(MNEMONIC_PHRASE, Language::English).unwrap(); + Seed::new(&mnemonic, "") } struct TestInputs { @@ -612,18 +646,18 @@ struct WholesomeConfig { } struct DebtsSpecs { - serving_node_1: AccountedDebt, - serving_node_2: AccountedDebt, - serving_node_3: AccountedDebt, + serving_node_1: Debt, + serving_node_2: Debt, + serving_node_3: Debt, } #[derive(Copy, Clone)] -struct AccountedDebt { +struct Debt { balance_minor: u128, age_s: u64, } -impl AccountedDebt { +impl Debt { fn proper_timestamp(&self, now: SystemTime) -> SystemTime { now.checked_sub(Duration::from_secs(self.age_s)).unwrap() } @@ -639,7 +673,7 @@ impl TestInputs { }) } - fn debt_specs(&self, requested: NodeByRole) -> AccountedDebt { + fn debt_specs(&self, requested: NodeByRole) -> Debt { match requested { NodeByRole::ServingNode1 => self.debts_config.serving_node_1, NodeByRole::ServingNode2 => self.debts_config.serving_node_2, @@ -661,7 +695,7 @@ impl GlobalValues { &self.blockchain_params.seed, wallet_derivation_path.as_str(), ); - let cfg_to_build = NodeStartupConfigBuilder::standard() + let mut cfg_to_build = NodeStartupConfigBuilder::standard() .blockchain_service_url(&self.blockchain_params.server_url) .chain(Chain::Dev) .payment_thresholds(payment_thresholds) @@ -670,16 +704,12 @@ impl GlobalValues { "{}", node_wallet.clone() ))); - let cfg_to_build = if let Some(port) = self.test_inputs.port(node_by_role) { - cfg_to_build.ui_port(port) - } else { - cfg_to_build - }; - let cfg_to_build = if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { - cfg_to_build.gas_price(price) - } else { - cfg_to_build - }; + if let Some(port) = self.test_inputs.port(node_by_role) { + cfg_to_build = cfg_to_build.ui_port(port) + } + if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { + cfg_to_build = cfg_to_build.gas_price(price) + } eprintln!("{:?} wallet established: {}\n", node_by_role, node_wallet,); @@ -725,19 +755,17 @@ impl GlobalValues { let consuming_node_connection = DbInitializerReal::default() .initialize( Path::new(&consuming_node_namings.db_path), - make_init_config(cluster.chain), + make_db_init_config(cluster.chain), ) .unwrap(); let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); - ConsumingNodeAttributes { - common: NodeAttributesCommon { - node_by_role: NodeByRole::ConsumingNode, - namings: consuming_node_namings, - config_opt: Some(consuming_node_config), - }, - consuming_wallet: consuming_node_wallet, - payable_dao: consuming_node_payable_dao, - } + ConsumingNodeAttributes::new( + NodeByRole::ConsumingNode, + consuming_node_namings, + Some(consuming_node_config), + consuming_node_wallet, + consuming_node_payable_dao, + ) } fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { @@ -751,22 +779,20 @@ impl GlobalValues { let (serving_node_config, serving_node_earning_wallet) = self.get_node_config_and_wallet(node_by_role); let serving_node_namings = cluster.prepare_real_node(&serving_node_config); - let serving_node_1_connection = DbInitializerReal::default() + let serving_node_connection = DbInitializerReal::default() .initialize( &serving_node_namings.db_path, - make_init_config(cluster.chain), + make_db_init_config(cluster.chain), ) .unwrap(); - let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_1_connection); - ServingNodeAttributes { - common: NodeAttributesCommon { - node_by_role, - namings: serving_node_namings, - config_opt: Some(serving_node_config), - }, - earning_wallet: serving_node_earning_wallet, - receivable_dao: serving_node_receivable_dao, - } + let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); + ServingNodeAttributes::new( + node_by_role, + serving_node_namings, + Some(serving_node_config), + serving_node_earning_wallet, + serving_node_receivable_dao, + ) }) .collect::>() .try_into() @@ -774,7 +800,6 @@ impl GlobalValues { } fn set_start_block_to_zero(path: &Path) { - //TODO do we want to do this?? DbInitializerReal::default() .initialize(path, DbInitializationConfig::panic_on_migration()) .unwrap() @@ -897,7 +922,7 @@ impl WholesomeConfig { assertions_values.final_consuming_node_service_fee_balance_minor, ); assertions_values - .serving_nodes_final_values() + .serving_nodes_actually_received_payments() .into_iter() .zip(self.serving_nodes.iter()) .for_each(|(expected_remaining_owed_value, serving_node)| { @@ -914,36 +939,42 @@ impl WholesomeConfig { &self, assertions_values: &AssertionsValues, ) { - let final_values = assertions_values.serving_nodes_final_values(); + let actually_received_payments = assertions_values.serving_nodes_actually_received_payments(); let consuming_node_wallet = &self.consuming_node.consuming_wallet; self.serving_nodes .iter() - .zip(final_values.into_iter()) - .for_each(|(serving_node, final_value)| { - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = serving_node - .receivable_dao - .account_status(&consuming_node_wallet) - { - status.balance_wei - == i128::try_from( - self.global_values - .test_inputs - .debt_specs(serving_node.common.node_by_role) - .balance_minor - - final_value, - ) - .unwrap() - } else { - false - } - }); + .zip(actually_received_payments.into_iter()) + .for_each(|(serving_node, received_payment)| { + let original_debt = self.global_values + .test_inputs + .debt_specs(serving_node.common.node_by_role) + .balance_minor; + let expected_final_balance = original_debt - received_payment; + Self::wait_for_exact_balance_in_receivables( + &serving_node.receivable_dao, + expected_final_balance, + consuming_node_wallet, + ) }) } + + fn wait_for_exact_balance_in_receivables( + node_receivable_dao: &ReceivableDaoReal, + expected_value: u128, + consuming_node_wallet: &Wallet, + ) { + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = node_receivable_dao.account_status(&consuming_node_wallet) { + status.balance_wei == i128::try_from(expected_value).unwrap() + } else { + false + } + }); + } } impl AssertionsValues { - fn serving_nodes_final_values(&self) -> [u128; 3] { + fn serving_nodes_actually_received_payments(&self) -> [u128; 3] { [ self.final_service_fee_balances.node_1_minor, self.final_service_fee_balances.node_2_minor, @@ -988,16 +1019,56 @@ struct ServingNodeAttributes { receivable_dao: ReceivableDaoReal, } -type StimulateConsumingNodePayments<'a> = - Box; -type StartServingNodesAndLetThemPerformReceivablesCheck<'a> = Box< - dyn FnOnce( - &mut MASQNodeCluster, - &mut [ServingNodeAttributes; 3], - &GlobalValues, - ) -> [MASQRealNode; 3] - + 'a, ->; +impl ConsumingNodeAttributes { + fn new( + node_by_role: NodeByRole, + namings: NodeNamingAndDir, + config_opt: Option, + consuming_wallet: Wallet, + payable_dao: PayableDaoReal, + ) -> Self { + let common = NodeAttributesCommon { + node_by_role, + namings, + config_opt, + }; + Self { + common, + consuming_wallet, + payable_dao, + } + } +} + +impl ServingNodeAttributes { + fn new( + node_by_role: NodeByRole, + namings: NodeNamingAndDir, + config_opt: Option, + earning_wallet: Wallet, + receivable_dao: ReceivableDaoReal, + ) -> Self { + let common = NodeAttributesCommon { + node_by_role, + namings, + config_opt, + }; + Self { + common, + earning_wallet, + receivable_dao, + } + } +} + +type StimulateConsumingNodePayments = + for<'a> fn(&'a mut MASQNodeCluster, &'a MASQRealNode, &'a GlobalValues); + +type StartServingNodesAndLetThemPerformReceivablesCheck = for<'a> fn( + &'a mut MASQNodeCluster, + &'a mut [ServingNodeAttributes; 3], + &'a GlobalValues, +) -> [MASQRealNode; 3]; fn test_body( test_inputs: TestInputs, @@ -1036,13 +1107,13 @@ fn test_body( &wholesome_config.global_values, ); - let now = Instant::now(); + let timeout_start = Instant::now(); while !wholesome_config .consuming_node .payable_dao .non_pending_payables() .is_empty() - && now.elapsed() < Duration::from_secs(10) + && timeout_start.elapsed() < Duration::from_secs(10) { thread::sleep(Duration::from_millis(400)); } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 1b139a1e1..c7a14b9fb 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -25,7 +25,7 @@ use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use ethereum_types::U256; use itertools::Either; use masq_lib::logger::Logger; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; pub struct PreparatoryAnalyzer {} @@ -167,7 +167,7 @@ impl PreparatoryAnalyzer { fn determine_transaction_count_limit_by_transaction_fee( &self, cw_transaction_fee_balance_minor: U256, - agreed_transaction_fee_margin: Percentage, + agreed_transaction_fee_margin: PurePercentage, per_transaction_requirement_minor: u128, number_of_qualified_accounts: usize, logger: &Logger, diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index a25630009..30856ad3e 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -4,7 +4,7 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockch use crate::sub_lib::wallet::Wallet; use ethereum_types::U256; use masq_lib::logger::Logger; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; #[derive(Clone)] pub struct BlockchainAgentNull { @@ -33,9 +33,9 @@ impl BlockchainAgent for BlockchainAgentNull { 0 } - fn agreed_transaction_fee_margin(&self) -> Percentage { + fn agreed_transaction_fee_margin(&self) -> PurePercentage { self.log_function_call("agreed_transaction_fee_margin()"); - Percentage::new(0) + PurePercentage::try_from(0).expect("0 should cause no issue") } fn consuming_wallet(&self) -> &Wallet { diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index cc4caf32f..45185ef34 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -7,14 +7,14 @@ use crate::sub_lib::wallet::Wallet; use crate::accountant::gwei_to_wei; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use web3::types::U256; #[derive(Debug, Clone)] pub struct BlockchainAgentWeb3 { gas_price_gwei: u64, gas_limit_const_part: u64, - agreed_transaction_fee_margin: Percentage, + agreed_transaction_fee_margin: PurePercentage, maximum_added_gas_margin: u64, consuming_wallet: Wallet, consuming_wallet_balances: ConsumingWalletBalances, @@ -42,7 +42,7 @@ impl BlockchainAgent for BlockchainAgentWeb3 { self.gas_price_gwei } - fn agreed_transaction_fee_margin(&self) -> Percentage { + fn agreed_transaction_fee_margin(&self) -> PurePercentage { self.agreed_transaction_fee_margin } diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index 649218c42..0ab3bdaed 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -2,7 +2,7 @@ use crate::arbitrary_id_stamp_in_trait; use crate::sub_lib::wallet::Wallet; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use web3::types::U256; // Table of chains by @@ -26,7 +26,7 @@ pub trait BlockchainAgent: Send { fn transaction_fee_balance_minor(&self) -> U256; fn service_fee_balance_minor(&self) -> u128; fn agreed_fee_per_computation_unit(&self) -> u64; - fn agreed_transaction_fee_margin(&self) -> Percentage; + fn agreed_transaction_fee_margin(&self) -> PurePercentage; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; 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 842c203a7..823c65cf1 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -37,7 +37,7 @@ use web3::types::{ H160, H256, U256, }; use web3::{BatchTransport, Error, Web3}; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use crate::accountant::db_access_objects::pending_payable_dao::PendingPayable; use crate::blockchain::blockchain_interface::data_structures::{BlockchainTransaction, ProcessedPayableFallible, RpcPayablesFailure}; use crate::sub_lib::blockchain_interface_web3::{compute_gas_limit, transaction_data_web3, web3_gas_limit_const_part}; @@ -71,7 +71,7 @@ pub const REQUESTS_IN_PARALLEL: usize = 1; lazy_static! { // TODO In the future, we'll replace this by a dynamical value of the user's choice. - pub static ref TRANSACTION_FEE_MARGIN: Percentage = Percentage::new(15); + pub static ref TRANSACTION_FEE_MARGIN: PurePercentage = PurePercentage::try_from(15).expect("Value below 100 should cause no issue"); } pub struct BlockchainInterfaceWeb3 diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index 32ea22f54..7bf9d4124 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -7,7 +7,7 @@ use crate::database::db_initializer::ExternalData; use crate::database::rusqlite_wrappers::ConnectionWrapper; use crate::database::db_migrations::db_migrator::DbMigrator; -use crate::test_utils::unshared_test_utils::standard_dir_for_test_input_data; +use crate::test_utils::standard_dir_for_test_input_data; use masq_lib::logger::Logger; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::utils::{to_string, NeighborhoodModeLight}; diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 0decd8d9b..05ac52f24 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -51,6 +51,7 @@ use serde_derive::{Deserialize, Serialize}; use std::collections::btree_set::BTreeSet; use std::collections::HashSet; use std::convert::From; +use std::env::current_dir; use std::fmt::Debug; use std::hash::Hash; @@ -58,7 +59,7 @@ use std::io::ErrorKind; use std::io::Read; use std::iter::repeat; use std::net::{Shutdown, TcpStream}; - +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; @@ -526,6 +527,17 @@ pub struct TestRawTransaction { pub data: Vec, } +pub fn standard_dir_for_test_input_data() -> PathBuf { + let mut working_dir = current_dir().unwrap(); + if !working_dir.ends_with("/node/") { + working_dir = working_dir.parent().unwrap().join("node"); + } + working_dir + .join("src") + .join("test_utils") + .join("test_input_data") +} + #[cfg(test)] pub mod unshared_test_utils { use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; @@ -554,8 +566,6 @@ pub mod unshared_test_utils { use lazy_static::lazy_static; use masq_lib::messages::{ToMessageBody, UiCrashRequest}; use masq_lib::multi_config::MultiConfig; - #[cfg(not(feature = "no_test_share"))] - use masq_lib::test_utils::utils::MutexIncrementInset; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; use masq_lib::utils::slice_of_strs_to_vec_of_strings; use std::any::TypeId; @@ -809,14 +819,6 @@ pub mod unshared_test_utils { .collect() } - pub fn standard_dir_for_test_input_data() -> PathBuf { - current_dir() - .unwrap() - .join("src") - .join("test_utils") - .join("input_data") - } - pub mod system_killer_actor { use super::*; diff --git a/node/src/test_utils/input_data/database_version_0_sqls.txt b/node/src/test_utils/test_input_data/database_version_0_sqls.txt similarity index 100% rename from node/src/test_utils/input_data/database_version_0_sqls.txt rename to node/src/test_utils/test_input_data/database_version_0_sqls.txt diff --git a/node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test b/node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test new file mode 100644 index 000000000..91e6e1dd2 --- /dev/null +++ b/node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test @@ -0,0 +1,60 @@ + +608060405234801561001057600080fd5b5060038054600160a060020a031916331790819055604051600160a060020a0391909116906000907f8be0 +079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3610080336b01866de34549d620d8000000640100000000610b94 +61008582021704565b610156565b600160a060020a038216151561009a57600080fd5b6002546100b490826401000000006109a461013d8202170456 +5b600255600160a060020a0382166000908152602081905260409020546100e790826401000000006109a461013d82021704565b600160a060020a03 +83166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a +4df523b3ef9281900390910190a35050565b60008282018381101561014f57600080fd5b9392505050565b610c6a806101656000396000f300608060 +4052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610100 +578063095ea7b31461018a57806318160ddd146101c257806323b872dd146101e95780632ff2e9dc14610213578063313ce567146102285780633950 +93511461025357806342966c681461027757806370a0823114610291578063715018a6146102b257806379cc6790146102c75780638da5cb5b146102 +eb5780638f32d59b1461031c57806395d89b4114610331578063a457c2d714610346578063a9059cbb1461036a578063dd62ed3e1461038e578063f2 +fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b60408051602080825283518183015283519192839290830191 +85019080838360005b8381101561014f578181015183820152602001610137565b50505050905090810190601f16801561017c578082038051600183 +6020036101000a031916815260200191505b509250505060405180910390f35b34801561019657600080fd5b506101ae600160a060020a0360043516 +602435610436565b604080519115158252519081900360200190f35b3480156101ce57600080fd5b506101d7610516565b6040805191825251908190 +0360200190f35b3480156101f557600080fd5b506101ae600160a060020a036004358116906024351660443561051c565b34801561021f57600080fd +5b506101d76105b9565b34801561023457600080fd5b5061023d6105c9565b6040805160ff9092168252519081900360200190f35b34801561025f57 +600080fd5b506101ae600160a060020a03600435166024356105ce565b34801561028357600080fd5b5061028f60043561067e565b005b3480156102 +9d57600080fd5b506101d7600160a060020a036004351661068b565b3480156102be57600080fd5b5061028f6106a6565b3480156102d357600080fd +5b5061028f600160a060020a0360043516602435610710565b3480156102f757600080fd5b5061030061071e565b60408051600160a060020a039092 +168252519081900360200190f35b34801561032857600080fd5b506101ae61072d565b34801561033d57600080fd5b5061011561073e565b34801561 +035257600080fd5b506101ae600160a060020a0360043516602435610775565b34801561037657600080fd5b506101ae600160a060020a0360043516 +6024356107c0565b34801561039a57600080fd5b506101d7600160a060020a03600435811690602435166107d6565b3480156103c157600080fd5b50 +61028f600160a060020a0360043516610801565b606060405190810160405280602481526020017f486f7420746865206e657720746f6b656e20796f +75277265206c6f6f6b696e6781526020017f20666f720000000000000000000000000000000000000000000000000000000081525081565b60008115 +8061044c575061044a33846107d6565b155b151561050557604080517f08c379a0000000000000000000000000000000000000000000000000000000 +00815260206004820152604160248201527f55736520696e637265617365417070726f76616c206f7220646563726561736560448201527f41707072 +6f76616c20746f2070726576656e7420646f75626c652d7370656e6460648201527f2e00000000000000000000000000000000000000000000000000 +000000000000608482015290519081900360a40190fd5b61050f838361081d565b9392505050565b60025490565b600160a060020a03831660009081 +5260016020908152604080832033845290915281205482111561054c57600080fd5b600160a060020a03841660009081526001602090815260408083 +20338452909152902054610580908363ffffffff61089b16565b600160a060020a038516600090815260016020908152604080832033845290915290 +20556105af8484846108b2565b5060019392505050565b6b01866de34549d620d800000081565b601281565b6000600160a060020a03831615156105 +e557600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff6109a416565b33 +6000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71 +427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b61068833826109b6565b50565b600160a060020a +031660009081526020819052604090205490565b6106ae61072d565b15156106b957600080fd5b600354604051600091600160a060020a0316907f8b +e0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36003805473ffffffffffffffffffffffffffffffffffffffff +19169055565b61071a8282610a84565b5050565b600354600160a060020a031690565b600354600160a060020a0316331490565b6040805180820190 +9152600381527f484f540000000000000000000000000000000000000000000000000000000000602082015281565b6000600160a060020a03831615 +1561078c57600080fd5b336000908152600160209081526040808320600160a060020a0387168452909152902054610619908363ffffffff61089b16 +565b60006107cd3384846108b2565b50600192915050565b600160a060020a0391821660009081526001602090815260408083209390941682529190 +9152205490565b61080961072d565b151561081457600080fd5b61068881610b16565b6000600160a060020a038316151561083457600080fd5b3360 +00818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f +71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b600080838311156108ab57600080fd5b505090 +0390565b600160a060020a0383166000908152602081905260409020548111156108d757600080fd5b600160a060020a03821615156108ec57600080 +fd5b600160a060020a038316600090815260208190526040902054610915908263ffffffff61089b16565b600160a060020a03808516600090815260 +208190526040808220939093559084168152205461094a908263ffffffff6109a416565b600160a060020a0380841660008181526020818152604091 +82902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190 +a3505050565b60008282018381101561050f57600080fd5b600160a060020a03821615156109cb57600080fd5b600160a060020a0382166000908152 +602081905260409020548111156109f057600080fd5b600254610a03908263ffffffff61089b16565b600255600160a060020a038216600090815260 +208190526040902054610a2f908263ffffffff61089b16565b600160a060020a03831660008181526020818152604080832094909455835185815293 +5191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600160a060020a038216 +6000908152600160209081526040808320338452909152902054811115610ab457600080fd5b600160a060020a038216600090815260016020908152 +6040808320338452909152902054610ae8908263ffffffff61089b16565b600160a060020a0383166000908152600160209081526040808320338452 +90915290205561071a82826109b6565b600160a060020a0381161515610b2b57600080fd5b600354604051600160a060020a038084169216907f8be0 +079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff +1916600160a060020a0392909216919091179055565b600160a060020a0382161515610ba957600080fd5b600254610bbc908263ffffffff6109a416 +565b600255600160a060020a038216600090815260208190526040902054610be8908263ffffffff6109a416565b600160a060020a03831660008181 +52602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92 +81900390910190a350505600a165627a7a72305820d4ad56dfe541fec48c3ecb02cebad565a998dfca7774c0c4f4b1f4a8e2363a590029 From baf51c48987d604cbd8bda151b4a26a3ac24e736 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 22 Sep 2024 21:16:55 +0200 Subject: [PATCH 200/250] GH-711-review-one: Decent version of verify_bill_payments --- masq_lib/src/percentage.rs | 5 +- .../src/masq_node_cluster.rs | 6 +- .../src/masq_real_node.rs | 2 +- .../tests/verify_bill_payment.rs | 994 ++---------------- .../tests/verify_bill_payment_utils/mod.rs | 3 + .../tests/verify_bill_payment_utils/utils.rs | 917 ++++++++++++++++ node/src/test_utils/mod.rs | 2 +- 7 files changed, 1006 insertions(+), 923 deletions(-) create mode 100644 multinode_integration_tests/tests/verify_bill_payment_utils/mod.rs create mode 100644 multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index b046ff592..a73f2e7fe 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -5,8 +5,7 @@ use num::CheckedSub; use num::{CheckedDiv, CheckedMul, Integer}; use std::any::type_name; use std::fmt::Debug; -use std::ops::Mul; -use std::ops::{Div, Rem}; +use std::ops::{Rem}; // Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage // operations over a wide variety of integer types. It is also to look after the least significant // digit on the resulted number in order to avoid the effect of a loss on precision that genuinely @@ -77,7 +76,7 @@ impl LoosePercentage { { let multiples = match N::try_from(self.multiples_of_100_percent) { Ok(num) => num, - Err(e) => return Err(BaseTypeOverflow {}), + Err(_) => return Err(BaseTypeOverflow {}), }; let by_wholes = match num.checked_mul(&multiples) { diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index c8c7d0f11..bc2685652 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -6,7 +6,7 @@ use crate::masq_mock_node::{ }; use crate::masq_node::{MASQNode, MASQNodeUtils}; use crate::masq_real_node::NodeStartupConfig; -use crate::masq_real_node::{MASQRealNode, NodeNamingAndDir}; +use crate::masq_real_node::{MASQRealNode, NodeID}; use crate::utils::{node_chain_specific_data_directory, open_all_file_permissions}; use masq_lib::blockchains::chains::Chain; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; @@ -52,7 +52,7 @@ impl MASQNodeCluster { self.next_index } - pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> NodeNamingAndDir { + pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> NodeID { let index = self.startup_configs.len() + 1; let name = MASQRealNode::make_name(index); self.next_index = index + 1; @@ -62,7 +62,7 @@ impl MASQNodeCluster { let db_path: PathBuf = node_chain_specific_data_directory(&name).into(); open_all_file_permissions(&db_path); - NodeNamingAndDir { + NodeID { node_name: name, index, db_path, diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 55cf44543..2e783f201 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -1222,7 +1222,7 @@ impl MASQRealNode { } #[derive(Debug)] -pub struct NodeNamingAndDir { +pub struct NodeID { pub node_name: String, pub index: usize, pub db_path: PathBuf, diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 8e1636628..6426a8874 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -1,49 +1,28 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use bip39::{Language, Mnemonic, Seed}; -use futures::Future; +use crate::verify_bill_payment_utils::utils::{ + test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, + GlobalValues, NodeByRole, Ports, ServingNodeAttributes, TestInputsBuilder, +}; use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; use masq_lib::percentage::PurePercentage; -use masq_lib::utils::{derivation_path, find_free_port, NeighborhoodModeLight}; -use multinode_integration_tests_lib::blockchain::BlockchainServer; +use masq_lib::utils::{find_free_port}; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; -use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeNamingAndDir, NodeStartupConfig, +use multinode_integration_tests_lib::masq_real_node::{MASQRealNode, NodeStartupConfigBuilder, }; -use multinode_integration_tests_lib::utils::UrlHolder; -use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; -use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; use node_lib::accountant::gwei_to_wei; -use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; -use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ - BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, -}; -use node_lib::blockchain::blockchain_interface::BlockchainInterface; -use node_lib::database::db_initializer::{ - DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, -}; use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::{ - compute_gas_limit, transaction_data_web3, web3_gas_limit_const_part, + compute_gas_limit, web3_gas_limit_const_part, }; -use node_lib::sub_lib::wallet::Wallet; -use node_lib::test_utils; -use node_lib::test_utils::standard_dir_for_test_input_data; -use rustc_hex::{FromHex, ToHex}; use std::convert::TryFrom; -use std::fs::File; -use std::io::Read; -use std::path::Path; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use std::{thread, u128}; -use tiny_hderive::bip32::ExtendedPrivKey; -use web3::transports::Http; -use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; -use web3::Web3; +use std::time::{Duration, UNIX_EPOCH}; +use std::{u128}; +mod verify_bill_payment_utils; #[test] fn full_payments_were_processed_for_sufficient_balances() { @@ -70,40 +49,29 @@ fn full_payments_were_processed_for_sufficient_balances() { let owed_to_serving_node_3_minor = debt_threshold_wei + 789_012; let consuming_node_initial_service_fee_balance_minor = debt_threshold_wei * 4; let long_ago = UNIX_EPOCH.elapsed().unwrap().as_secs(); - let test_inputs = TestInputs { - ui_ports_opt: None, - consuming_node_initial_transaction_fee_balance_minor_opt: None, - consuming_node_initial_service_fee_balance_minor, - debts_config: DebtsSpecs { - serving_node_1: Debt { - balance_minor: owed_to_serving_node_1_minor, - age_s: long_ago, - }, - serving_node_2: Debt { - balance_minor: owed_to_serving_node_2_minor, - age_s: long_ago, - }, - serving_node_3: Debt { - balance_minor: owed_to_serving_node_3_minor, - age_s: long_ago, - }, - }, - payment_thresholds_all_nodes: payment_thresholds, - consuming_node_gas_price_opt: None, - }; + let test_inputs = TestInputsBuilder::default() + .consuming_node_initial_service_fee_balance_minor( + consuming_node_initial_service_fee_balance_minor, + ) + .debts_config(DebtsSpecs::new( + Debt::new(owed_to_serving_node_1_minor, long_ago), + Debt::new(owed_to_serving_node_2_minor, long_ago), + Debt::new(owed_to_serving_node_3_minor, long_ago), + )) + .payment_thresholds_all_nodes(payment_thresholds) + .build(); + let debts_total = + owed_to_serving_node_1_minor + owed_to_serving_node_2_minor + owed_to_serving_node_3_minor; let final_consuming_node_service_fee_balance_minor = - consuming_node_initial_service_fee_balance_minor - - (owed_to_serving_node_1_minor - + owed_to_serving_node_2_minor - + owed_to_serving_node_3_minor); + consuming_node_initial_service_fee_balance_minor - debts_total; let assertions_values = AssertionsValues { - final_consuming_node_transaction_fee_balance_minor: 999_842_470_000_000_000, + final_consuming_node_transaction_fee_balance_minor: to_wei(999_842_470), final_consuming_node_service_fee_balance_minor, - final_service_fee_balances: FinalServiceFeeBalancesByNode { - node_1_minor: owed_to_serving_node_1_minor, - node_2_minor: owed_to_serving_node_2_minor, - node_3_minor: owed_to_serving_node_3_minor, - }, + final_service_fee_balances_by_serving_nodes: FinalServiceFeeBalancesByServingNodes::new( + owed_to_serving_node_1_minor, + owed_to_serving_node_2_minor, + owed_to_serving_node_3_minor, + ), }; test_body( @@ -114,10 +82,10 @@ fn full_payments_were_processed_for_sufficient_balances() { ); } -fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds<'a>( - cluster: &'a mut MASQNodeCluster, - real_consuming_node: &'a MASQRealNode, - _global_values: &'a GlobalValues, +fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds( + cluster: &mut MASQNodeCluster, + real_consuming_node: &MASQRealNode, + _global_values: &GlobalValues, ) { for _ in 0..6 { cluster.start_real_node( @@ -129,15 +97,15 @@ fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds<'a>( } } -fn activating_serving_nodes_for_test_with_sufficient_funds<'a>( - cluster: &'a mut MASQNodeCluster, - serving_nodes: &'a mut [ServingNodeAttributes; 3], - _global_values: &'a GlobalValues, +fn activating_serving_nodes_for_test_with_sufficient_funds( + cluster: &mut MASQNodeCluster, + serving_nodes: &mut [ServingNodeAttributes; 3], + _global_values: &GlobalValues, ) -> [MASQRealNode; 3] { let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes .into_iter() .map(|attributes| { - let namings = &attributes.common.namings; + let namings = &attributes.common.node_id; cluster.start_named_real_node( &namings.node_name, namings.index, @@ -184,8 +152,8 @@ fn payments_were_adjusted_due_to_insufficient_balances() { let enough_balance_for_serving_node_1_and_2 = owed_to_serv_node_1_minor + owed_to_serv_node_2_minor; let consuming_node_initial_service_fee_balance_minor = - enough_balance_for_serving_node_1_and_2 - gwei_to_wei::(2_345_678_u64); - let agreed_transaction_fee_unit_price_major = 60; + enough_balance_for_serving_node_1_and_2 - to_wei(2_345_678); + let gas_price_major = 60; let tx_fee_needed_to_pay_for_one_payment_major = { // We'll need littler funds, but we can stand mild inaccuracy from assuming the use of // all nonzero bytes in the data in both txs, which represents maximized costs @@ -199,61 +167,60 @@ fn payments_were_adjusted_due_to_insufficient_balances() { .unwrap() }; let transaction_fee_margin = PurePercentage::try_from(15).unwrap(); - transaction_fee_margin - .add_percent_to(gas_limit_dev_chain * agreed_transaction_fee_unit_price_major) + transaction_fee_margin.add_percent_to(gas_limit_dev_chain * gas_price_major) }; const AFFORDABLE_PAYMENTS_COUNT: u128 = 2; let tx_fee_needed_to_pay_for_one_payment_minor: u128 = gwei_to_wei(tx_fee_needed_to_pay_for_one_payment_major); let consuming_node_transaction_fee_balance_minor = AFFORDABLE_PAYMENTS_COUNT * tx_fee_needed_to_pay_for_one_payment_minor; - let test_inputs = TestInputs { - ui_ports_opt: Some(Ports { - consuming_node: find_free_port(), - serving_node_1: find_free_port(), - serving_node_2: find_free_port(), - serving_node_3: find_free_port(), - }), + let test_inputs = TestInputsBuilder::default() + .ui_ports(Ports::new( + find_free_port(), + find_free_port(), + find_free_port(), + find_free_port(), + )) // Should be enough only for two payments, the least significant one will fall out - consuming_node_initial_transaction_fee_balance_minor_opt: Some( - consuming_node_transaction_fee_balance_minor, - ), - consuming_node_initial_service_fee_balance_minor, - debts_config: DebtsSpecs { + .consuming_node_initial_tx_fee_balance_minor(consuming_node_transaction_fee_balance_minor) + .consuming_node_initial_service_fee_balance_minor( + consuming_node_initial_service_fee_balance_minor, + ) + .debts_config(DebtsSpecs::new( // This account will be the most significant and will deserve the full balance - serving_node_1: Debt { - balance_minor: owed_to_serv_node_1_minor, - age_s: payment_thresholds.maturity_threshold_sec + 1000, - }, + Debt::new( + owed_to_serv_node_1_minor, + payment_thresholds.maturity_threshold_sec + 1000, + ), // This balance is of a middle size it will be reduced as there won't be enough // after the first one is filled up. - serving_node_2: Debt { - balance_minor: owed_to_serv_node_2_minor, - age_s: payment_thresholds.maturity_threshold_sec + 100_000, - }, + Debt::new( + owed_to_serv_node_2_minor, + payment_thresholds.maturity_threshold_sec + 100_000, + ), // This account will be the least significant, therefore eliminated due to tx fee - serving_node_3: Debt { - balance_minor: owed_to_serv_node_3_minor, - age_s: payment_thresholds.maturity_threshold_sec + 30_000, - }, - }, - payment_thresholds_all_nodes: payment_thresholds, - consuming_node_gas_price_opt: Some(agreed_transaction_fee_unit_price_major), - }; + Debt::new( + owed_to_serv_node_3_minor, + payment_thresholds.maturity_threshold_sec + 30_000, + ), + )) + .payment_thresholds_all_nodes(payment_thresholds) + .consuming_node_gas_price_major(gas_price_major) + .build(); let assertions_values = AssertionsValues { // How much is left after the smart contract was successfully executed, those three payments - final_consuming_node_transaction_fee_balance_minor: 2_828_352_000_000_000, + final_consuming_node_transaction_fee_balance_minor: to_wei(2_828_352), // Zero reached, because the algorithm is designed to exhaust the wallet completely final_consuming_node_service_fee_balance_minor: 0, // This account was granted with the full size as its lowest balance from the set makes // it weight the most - final_service_fee_balances: FinalServiceFeeBalancesByNode { - node_1_minor: owed_to_serv_node_1_minor, - node_2_minor: owed_to_serv_node_2_minor - gwei_to_wei::(2_345_678), + final_service_fee_balances_by_serving_nodes: FinalServiceFeeBalancesByServingNodes::new( + owed_to_serv_node_1_minor, + owed_to_serv_node_2_minor - to_wei(2_345_678), // This account dropped out from the payment, so received no money - node_3_minor: 0, - }, + 0, + ), }; test_body( @@ -292,8 +259,8 @@ fn activating_serving_nodes_for_test_with_insufficient_funds( let node_config = serving_node_attributes.common.config_opt.take().unwrap(); let common = &serving_node_attributes.common; let serving_node = cluster.start_named_real_node( - &common.namings.node_name, - common.namings.index, + &common.node_id.node_name, + common.node_id.index, node_config, ); let ui_port = global_values @@ -324,807 +291,4 @@ fn process_scan_request_to_node( ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); UiScanResponse::fmb(response).expect("Scan request went wrong"); -} - -fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, GlobalValues) { - let now = SystemTime::now(); - let cluster = match MASQNodeCluster::start() { - Ok(cluster) => cluster, - Err(e) => panic!("{}", e), - }; - let blockchain_server = BlockchainServer { - name: "ganache-cli", - }; - blockchain_server.start(); - blockchain_server.wait_until_ready(); - let server_url = blockchain_server.url().to_string(); - let (event_loop_handle, http) = - Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); - let web3 = Web3::new(http.clone()); - let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet(&seed, &derivation_path(0, 0)); - let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - let blockchain_interface = Box::new(BlockchainInterfaceWeb3::new( - http, - event_loop_handle, - cluster.chain, - )); - let blockchain_params = BlockchainParams { - blockchain_interfaces: BlockchainInterfaces { - blockchain_interface, - web3, - }, - chain: cluster.chain, - server_url, - contract_owner_addr, - contract_owner_wallet, - seed, - }; - let global_values = GlobalValues { - test_inputs, - blockchain_params, - now_in_common: now, - }; - assert_eq!( - contract_owner_addr, - cluster.chain.rec().contract, - "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ - Resulted contact addr {:?} doesn't much what's expected: {:?}", - contract_owner_addr, - cluster.chain.rec().contract - ); - - (cluster, global_values) -} - -fn to_wei(gwei: u64) -> u128 { - gwei_to_wei(gwei) -} - -fn make_db_init_config(chain: Chain) -> DbInitializationConfig { - DbInitializationConfig::create_or_migrate(ExternalData::new( - chain, - NeighborhoodModeLight::Standard, - None, - )) -} - -fn load_contract_in_bytes() -> Vec { - let file_path = - standard_dir_for_test_input_data().join("smart_contract_for_on_blockchain_test"); - let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); - let mut data = String::new(); - file.read_to_string(&mut data).unwrap(); - let data = data - .chars() - .filter(|char| !char.is_whitespace()) - .collect::(); - data.from_hex::>() - .expect("bad contract: contains non-hexadecimal characters") -} - -fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { - let contract = load_contract_in_bytes(); - let gas_price = to_wei(50); - let gas_limit = 1_000_000_u64; - let tx = TransactionParameters { - nonce: Some(ethereum_types::U256::try_from(0).expect("Internal error")), - to: None, - gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), - gas_price: Some(ethereum_types::U256::try_from(gas_price).expect("Internal error")), - value: ethereum_types::U256::zero(), - data: Bytes(contract), - chain_id: Some(chain.rec().num_chain_id), - }; - let signed_tx = primitive_sign_transaction(web3, tx, wallet); - match web3 - .eth() - .send_raw_transaction(signed_tx.raw_transaction) - .wait() - { - Ok(tx_hash) => match web3.eth().transaction_receipt(tx_hash).wait() { - Ok(Some(tx_receipt)) => Address { - 0: tx_receipt.contract_address.unwrap().0, - }, - Ok(None) => panic!("Contract deployment failed Ok(None)"), - Err(e) => panic!("Contract deployment failed {:?}", e), - }, - Err(e) => panic!("Contract deployment failed {:?}", e), - } -} - -fn transfer_service_fee_amount_to_address( - contract_addr: Address, - from_wallet: &Wallet, - to_wallet: &Wallet, - amount_minor: u128, - transaction_nonce: u64, - web3: &Web3, - chain: Chain, -) { - let data = transaction_data_web3(to_wallet, amount_minor); - let gas_price_wei = to_wei(150); - let gas_limit = 1_000_000_u64; - let tx = TransactionParameters { - nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), - to: Some(contract_addr), - gas: ethereum_types::U256::try_from(gas_limit).expect("Internal error"), - gas_price: Some(ethereum_types::U256::try_from(gas_price_wei).expect("Internal error")), - value: ethereum_types::U256::zero(), - data: Bytes(data.to_vec()), - chain_id: Some(chain.rec().num_chain_id), - }; - let signed_tx = primitive_sign_transaction(web3, tx, from_wallet); - match web3 - .eth() - .send_raw_transaction(signed_tx.raw_transaction) - .wait() - { - Ok(tx_hash) => eprintln!( - "Transaction {:?} of {} wei of MASQ was sent from wallet {} to {}", - tx_hash, amount_minor, from_wallet, to_wallet - ), - Err(e) => panic!("Transaction for token transfer failed {:?}", e), - } -} - -fn primitive_sign_transaction( - web3: &Web3, - tx: TransactionParameters, - signing_wallet: &Wallet, -) -> SignedTransaction { - web3.accounts() - .sign_transaction( - tx, - &signing_wallet - .prepare_secp256k1_secret() - .expect("wallet without secret"), - ) - .wait() - .expect("transaction preparation failed") -} - -fn transfer_transaction_fee_amount_to_address( - from_wallet: &Wallet, - to_wallet: &Wallet, - amount_minor: u128, - transaction_nonce: u64, - web3: &Web3, -) { - let gas_price_wei = to_wei(150); - let gas_limit = 1_000_000_u64; - let tx = TransactionRequest { - from: from_wallet.address(), - to: Some(to_wallet.address()), - gas: Some(ethereum_types::U256::try_from(gas_limit).expect("Internal error")), - gas_price: Some(ethereum_types::U256::try_from(gas_price_wei).expect("Internal error")), - value: Some(ethereum_types::U256::from(amount_minor)), - data: None, - nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), - condition: None, - }; - match web3 - .personal() - .unlock_account(from_wallet.address(), "", None) - .wait() - { - Ok(was_successful) => { - if was_successful { - eprintln!("Account {} unlocked for a single transaction", from_wallet) - } else { - panic!( - "Couldn't unlock account {} for the purpose of signing the next transaction", - from_wallet - ) - } - } - Err(e) => panic!( - "Attempt to unlock account {:?} failed at {:?}", - from_wallet.address(), - e - ), - } - match web3.eth().send_transaction(tx).wait() { - Ok(tx_hash) => eprintln!( - "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", - tx_hash, amount_minor, from_wallet, to_wallet - ), - Err(e) => panic!("Transaction for token transfer failed {:?}", e), - } -} - -fn assert_balances( - wallet: &Wallet, - blockchain_interface: &dyn BlockchainInterface, - expected_eth_balance: u128, - expected_token_balance: u128, -) { - let eth_balance = blockchain_interface - .lower_interface() - .get_transaction_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); - assert_eq!( - eth_balance, - web3::types::U256::from(expected_eth_balance), - "Actual EthBalance {} doesn't much with expected {} for {}", - eth_balance, - expected_eth_balance, - wallet - ); - let token_balance = blockchain_interface - .lower_interface() - .get_service_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); - assert_eq!( - token_balance, - web3::types::U256::from(expected_token_balance), - "Actual TokenBalance {} doesn't match with expected {} for {}", - token_balance, - expected_token_balance, - wallet - ); -} - -fn make_node_wallet(seed: &Seed, derivation_path: &str) -> (Wallet, String) { - let extended_priv_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); - let secret = extended_priv_key.secret().to_hex::(); - - ( - Wallet::from(Bip32EncryptionKeyProvider::from_key(extended_priv_key)), - secret, - ) -} - -const MNEMONIC_PHRASE: &str = - "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ - lamp absent write kind term toddler sphere ripple idle dragon curious hold"; - -fn make_seed() -> Seed { - let mnemonic = Mnemonic::from_phrase(MNEMONIC_PHRASE, Language::English).unwrap(); - Seed::new(&mnemonic, "") -} - -struct TestInputs { - ui_ports_opt: Option, - // The contract owner wallet is populated with 100 ETH as defined in the set of commands - // with which we start up the Ganache server. - // - // Specify number of wei this account should possess at its initialisation. - // The consuming node gets the full balance of the contract owner if left as None. - // Cannot ever get more than what the "owner" has. - consuming_node_initial_transaction_fee_balance_minor_opt: Option, - consuming_node_initial_service_fee_balance_minor: u128, - debts_config: DebtsSpecs, - payment_thresholds_all_nodes: PaymentThresholds, - consuming_node_gas_price_opt: Option, -} - -struct AssertionsValues { - final_consuming_node_transaction_fee_balance_minor: u128, - final_consuming_node_service_fee_balance_minor: u128, - final_service_fee_balances: FinalServiceFeeBalancesByNode, -} - -struct FinalServiceFeeBalancesByNode { - node_1_minor: u128, - node_2_minor: u128, - node_3_minor: u128, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum NodeByRole { - ConsumingNode = 1, - ServingNode1 = 2, - ServingNode2 = 3, - ServingNode3 = 4, -} - -struct BlockchainParams { - blockchain_interfaces: BlockchainInterfaces, - chain: Chain, - server_url: String, - contract_owner_addr: Address, - contract_owner_wallet: Wallet, - seed: Seed, -} - -struct BlockchainInterfaces { - blockchain_interface: Box, - web3: Web3, -} - -struct GlobalValues { - test_inputs: TestInputs, - blockchain_params: BlockchainParams, - now_in_common: SystemTime, -} - -struct WholesomeConfig { - global_values: GlobalValues, - consuming_node: ConsumingNodeAttributes, - serving_nodes: [ServingNodeAttributes; 3], -} - -struct DebtsSpecs { - serving_node_1: Debt, - serving_node_2: Debt, - serving_node_3: Debt, -} - -#[derive(Copy, Clone)] -struct Debt { - balance_minor: u128, - age_s: u64, -} - -impl Debt { - fn proper_timestamp(&self, now: SystemTime) -> SystemTime { - now.checked_sub(Duration::from_secs(self.age_s)).unwrap() - } -} - -impl TestInputs { - fn port(&self, requested: NodeByRole) -> Option { - self.ui_ports_opt.as_ref().map(|ports| match requested { - NodeByRole::ConsumingNode => ports.consuming_node, - NodeByRole::ServingNode1 => ports.serving_node_1, - NodeByRole::ServingNode2 => ports.serving_node_2, - NodeByRole::ServingNode3 => ports.serving_node_3, - }) - } - - fn debt_specs(&self, requested: NodeByRole) -> Debt { - match requested { - NodeByRole::ServingNode1 => self.debts_config.serving_node_1, - NodeByRole::ServingNode2 => self.debts_config.serving_node_2, - NodeByRole::ServingNode3 => self.debts_config.serving_node_3, - NodeByRole::ConsumingNode => panic!( - "Version fully specified: These configs \ - describe debts owed to the consuming node, while that one should not \ - be here." - ), - } - } -} - -impl GlobalValues { - fn get_node_config_and_wallet(&self, node_by_role: NodeByRole) -> (NodeStartupConfig, Wallet) { - let wallet_derivation_path = node_by_role.derivation_path(); - let payment_thresholds = self.test_inputs.payment_thresholds_all_nodes; - let (node_wallet, node_secret) = make_node_wallet( - &self.blockchain_params.seed, - wallet_derivation_path.as_str(), - ); - let mut cfg_to_build = NodeStartupConfigBuilder::standard() - .blockchain_service_url(&self.blockchain_params.server_url) - .chain(Chain::Dev) - .payment_thresholds(payment_thresholds) - .consuming_wallet_info(ConsumingWalletInfo::PrivateKey(node_secret)) - .earning_wallet_info(EarningWalletInfo::Address(format!( - "{}", - node_wallet.clone() - ))); - if let Some(port) = self.test_inputs.port(node_by_role) { - cfg_to_build = cfg_to_build.ui_port(port) - } - if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { - cfg_to_build = cfg_to_build.gas_price(price) - } - - eprintln!("{:?} wallet established: {}\n", node_by_role, node_wallet,); - - (cfg_to_build.build(), node_wallet) - } - - fn prepare_consuming_node( - &self, - cluster: &mut MASQNodeCluster, - blockchain_interfaces: &BlockchainInterfaces, - ) -> ConsumingNodeAttributes { - let (consuming_node_config, consuming_node_wallet) = - self.get_node_config_and_wallet(NodeByRole::ConsumingNode); - let initial_transaction_fee_balance = self - .test_inputs - .consuming_node_initial_transaction_fee_balance_minor_opt - .unwrap_or(ONE_ETH_IN_WEI); - transfer_transaction_fee_amount_to_address( - &self.blockchain_params.contract_owner_wallet, - &consuming_node_wallet, - initial_transaction_fee_balance, - 1, - &blockchain_interfaces.web3, - ); - transfer_service_fee_amount_to_address( - self.blockchain_params.contract_owner_addr, - &self.blockchain_params.contract_owner_wallet, - &consuming_node_wallet, - self.test_inputs - .consuming_node_initial_service_fee_balance_minor, - 2, - &blockchain_interfaces.web3, - self.blockchain_params.chain, - ); - assert_balances( - &consuming_node_wallet, - blockchain_interfaces.blockchain_interface.as_ref(), - initial_transaction_fee_balance, - self.test_inputs - .consuming_node_initial_service_fee_balance_minor, - ); - let consuming_node_namings = cluster.prepare_real_node(&consuming_node_config); - let consuming_node_connection = DbInitializerReal::default() - .initialize( - Path::new(&consuming_node_namings.db_path), - make_db_init_config(cluster.chain), - ) - .unwrap(); - let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); - ConsumingNodeAttributes::new( - NodeByRole::ConsumingNode, - consuming_node_namings, - Some(consuming_node_config), - consuming_node_wallet, - consuming_node_payable_dao, - ) - } - - fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { - [ - NodeByRole::ServingNode1, - NodeByRole::ServingNode2, - NodeByRole::ServingNode3, - ] - .into_iter() - .map(|node_by_role: NodeByRole| { - let (serving_node_config, serving_node_earning_wallet) = - self.get_node_config_and_wallet(node_by_role); - let serving_node_namings = cluster.prepare_real_node(&serving_node_config); - let serving_node_connection = DbInitializerReal::default() - .initialize( - &serving_node_namings.db_path, - make_db_init_config(cluster.chain), - ) - .unwrap(); - let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); - ServingNodeAttributes::new( - node_by_role, - serving_node_namings, - Some(serving_node_config), - serving_node_earning_wallet, - serving_node_receivable_dao, - ) - }) - .collect::>() - .try_into() - .expect("failed to make [T;3] of provided Vec") - } - - fn set_start_block_to_zero(path: &Path) { - DbInitializerReal::default() - .initialize(path, DbInitializationConfig::panic_on_migration()) - .unwrap() - .prepare("update config set value = '0' where name = 'start_block'") - .unwrap() - .execute([]) - .unwrap(); - } - - fn serving_node_debt_balance_and_timestamp( - &self, - attributes: &ServingNodeAttributes, - ) -> (u128, SystemTime) { - let node_role = attributes.common.node_by_role; - let debt_specs = self.test_inputs.debt_specs(node_role); - ( - debt_specs.balance_minor, - debt_specs.proper_timestamp(self.now_in_common), - ) - } - - fn set_up_serving_nodes_databases( - &self, - serving_nodes_matrix: &[ServingNodeAttributes; 3], - consuming_node_attributes: &ConsumingNodeAttributes, - ) { - serving_nodes_matrix.iter().for_each(|node_attributes| { - let (balance, timestamp) = - self.serving_node_debt_balance_and_timestamp(node_attributes); - node_attributes - .receivable_dao - .more_money_receivable( - timestamp, - &consuming_node_attributes.consuming_wallet, - balance, - ) - .unwrap(); - assert_balances( - &node_attributes.earning_wallet, - self.blockchain_params - .blockchain_interfaces - .blockchain_interface - .as_ref(), - 0, - 0, - ); - Self::set_start_block_to_zero(&node_attributes.common.namings.db_path) - }) - } - - fn set_up_consuming_node_db( - &self, - serving_nodes_array: &[ServingNodeAttributes; 3], - consuming_node_attributes: &ConsumingNodeAttributes, - ) { - serving_nodes_array.iter().for_each(|node_attributes| { - let (balance, timestamp) = - self.serving_node_debt_balance_and_timestamp(node_attributes); - consuming_node_attributes - .payable_dao - .more_money_payable(timestamp, &node_attributes.earning_wallet, balance) - .unwrap(); - }); - Self::set_start_block_to_zero(&consuming_node_attributes.common.namings.db_path) - } -} - -impl WholesomeConfig { - fn new( - global_values: GlobalValues, - consuming_node: ConsumingNodeAttributes, - serving_nodes: [ServingNodeAttributes; 3], - ) -> Self { - WholesomeConfig { - global_values, - consuming_node, - serving_nodes, - } - } - - fn assert_expected_wallet_addresses(&self) { - let consuming_node_actual = self.consuming_node.consuming_wallet.to_string(); - let consuming_node_expected = "0x7a3cf474962646b18666b5a5be597bb0af013d81"; - assert_eq!( - &consuming_node_actual, consuming_node_expected, - "Consuming Node's wallet {} mismatched with expected {}", - consuming_node_actual, consuming_node_expected - ); - vec![ - "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6", - "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924", - "0xb45a33ef3e3097f34c826369b74141ed268cdb5a", - ] - .iter() - .zip(self.serving_nodes.iter()) - .for_each(|(expected_wallet_addr, serving_node_attributes)| { - let serving_node_actual = serving_node_attributes.earning_wallet.to_string(); - assert_eq!( - &serving_node_actual, - expected_wallet_addr, - "{:?} wallet {} mismatched with expected {}", - serving_node_attributes.common.node_by_role, - serving_node_actual, - expected_wallet_addr - ); - }) - } - - fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { - let blockchain_interface = self - .global_values - .blockchain_params - .blockchain_interfaces - .blockchain_interface - .as_ref(); - assert_balances( - &self.consuming_node.consuming_wallet, - blockchain_interface, - assertions_values.final_consuming_node_transaction_fee_balance_minor, - assertions_values.final_consuming_node_service_fee_balance_minor, - ); - assertions_values - .serving_nodes_actually_received_payments() - .into_iter() - .zip(self.serving_nodes.iter()) - .for_each(|(expected_remaining_owed_value, serving_node)| { - assert_balances( - &serving_node.earning_wallet, - blockchain_interface, - 0, - expected_remaining_owed_value, - ); - }) - } - - fn assert_serving_nodes_addressed_received_payments( - &self, - assertions_values: &AssertionsValues, - ) { - let actually_received_payments = assertions_values.serving_nodes_actually_received_payments(); - let consuming_node_wallet = &self.consuming_node.consuming_wallet; - self.serving_nodes - .iter() - .zip(actually_received_payments.into_iter()) - .for_each(|(serving_node, received_payment)| { - let original_debt = self.global_values - .test_inputs - .debt_specs(serving_node.common.node_by_role) - .balance_minor; - let expected_final_balance = original_debt - received_payment; - Self::wait_for_exact_balance_in_receivables( - &serving_node.receivable_dao, - expected_final_balance, - consuming_node_wallet, - ) - }) - } - - fn wait_for_exact_balance_in_receivables( - node_receivable_dao: &ReceivableDaoReal, - expected_value: u128, - consuming_node_wallet: &Wallet, - ) { - test_utils::wait_for(Some(1000), Some(15000), || { - if let Some(status) = node_receivable_dao.account_status(&consuming_node_wallet) { - status.balance_wei == i128::try_from(expected_value).unwrap() - } else { - false - } - }); - } -} - -impl AssertionsValues { - fn serving_nodes_actually_received_payments(&self) -> [u128; 3] { - [ - self.final_service_fee_balances.node_1_minor, - self.final_service_fee_balances.node_2_minor, - self.final_service_fee_balances.node_3_minor, - ] - } -} - -impl NodeByRole { - fn derivation_path(self) -> String { - derivation_path(0, self as usize as u8) - } -} - -const ONE_ETH_IN_WEI: u128 = 1_000_000_000_000_000_000; - -struct Ports { - consuming_node: u16, - serving_node_1: u16, - serving_node_2: u16, - serving_node_3: u16, -} - -#[derive(Debug)] -struct NodeAttributesCommon { - node_by_role: NodeByRole, - namings: NodeNamingAndDir, - config_opt: Option, -} - -#[derive(Debug)] -struct ConsumingNodeAttributes { - common: NodeAttributesCommon, - consuming_wallet: Wallet, - payable_dao: PayableDaoReal, -} - -#[derive(Debug)] -struct ServingNodeAttributes { - common: NodeAttributesCommon, - earning_wallet: Wallet, - receivable_dao: ReceivableDaoReal, -} - -impl ConsumingNodeAttributes { - fn new( - node_by_role: NodeByRole, - namings: NodeNamingAndDir, - config_opt: Option, - consuming_wallet: Wallet, - payable_dao: PayableDaoReal, - ) -> Self { - let common = NodeAttributesCommon { - node_by_role, - namings, - config_opt, - }; - Self { - common, - consuming_wallet, - payable_dao, - } - } -} - -impl ServingNodeAttributes { - fn new( - node_by_role: NodeByRole, - namings: NodeNamingAndDir, - config_opt: Option, - earning_wallet: Wallet, - receivable_dao: ReceivableDaoReal, - ) -> Self { - let common = NodeAttributesCommon { - node_by_role, - namings, - config_opt, - }; - Self { - common, - earning_wallet, - receivable_dao, - } - } -} - -type StimulateConsumingNodePayments = - for<'a> fn(&'a mut MASQNodeCluster, &'a MASQRealNode, &'a GlobalValues); - -type StartServingNodesAndLetThemPerformReceivablesCheck = for<'a> fn( - &'a mut MASQNodeCluster, - &'a mut [ServingNodeAttributes; 3], - &'a GlobalValues, -) -> [MASQRealNode; 3]; - -fn test_body( - test_inputs: TestInputs, - assertions_values: AssertionsValues, - stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, - start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, -) { - let (mut cluster, global_values) = establish_test_frame(test_inputs); - let consuming_node_attributes = global_values.prepare_consuming_node( - &mut cluster, - &global_values.blockchain_params.blockchain_interfaces, - ); - let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); - global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node_attributes); - global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node_attributes); - let mut wholesome_config = WholesomeConfig::new( - global_values, - consuming_node_attributes, - serving_nodes_array, - ); - wholesome_config.assert_expected_wallet_addresses(); - let real_consuming_node = cluster.start_named_real_node( - &wholesome_config.consuming_node.common.namings.node_name, - wholesome_config.consuming_node.common.namings.index, - wholesome_config - .consuming_node - .common - .config_opt - .take() - .unwrap(), - ); - - stimulate_consuming_node_to_pay( - &mut cluster, - &real_consuming_node, - &wholesome_config.global_values, - ); - - let timeout_start = Instant::now(); - while !wholesome_config - .consuming_node - .payable_dao - .non_pending_payables() - .is_empty() - && timeout_start.elapsed() < Duration::from_secs(10) - { - thread::sleep(Duration::from_millis(400)); - } - wholesome_config.assert_payments_via_direct_blockchain_scanning(&assertions_values); - - let _ = start_serving_nodes_and_activate_their_accountancy( - &mut cluster, - // So that individual Configs can be pulled out and used - &mut wholesome_config.serving_nodes, - &wholesome_config.global_values, - ); - - wholesome_config.assert_serving_nodes_addressed_received_payments(&assertions_values) -} +} \ No newline at end of file diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/mod.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/mod.rs new file mode 100644 index 000000000..583a5129a --- /dev/null +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/mod.rs @@ -0,0 +1,3 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub mod utils; diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs new file mode 100644 index 000000000..dce0ab452 --- /dev/null +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -0,0 +1,917 @@ +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use bip39::{Language, Mnemonic, Seed}; +use futures::Future; +use masq_lib::blockchains::chains::Chain; +use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; +use multinode_integration_tests_lib::blockchain::BlockchainServer; +use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; +use multinode_integration_tests_lib::masq_real_node::{ + ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeID, NodeStartupConfig, + NodeStartupConfigBuilder, +}; +use multinode_integration_tests_lib::utils::UrlHolder; +use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; +use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; +use node_lib::accountant::gwei_to_wei; +use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; +use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ + BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, +}; +use node_lib::blockchain::blockchain_interface::BlockchainInterface; +use node_lib::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, +}; +use node_lib::sub_lib::accountant::PaymentThresholds; +use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; +use node_lib::sub_lib::wallet::Wallet; +use node_lib::test_utils; +use node_lib::test_utils::standard_dir_for_test_input_data; +use rustc_hex::{FromHex, ToHex}; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::thread; +use std::time::{Duration, Instant, SystemTime}; +use lazy_static::lazy_static; +use tiny_hderive::bip32::ExtendedPrivKey; +use web3::transports::Http; +use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; +use web3::Web3; + +pub type StimulateConsumingNodePayments = + fn(&mut MASQNodeCluster, &MASQRealNode, &GlobalValues); + +pub type StartServingNodesAndLetThemPerformReceivablesCheck = fn( + &mut MASQNodeCluster, + &mut [ServingNodeAttributes; 3], + &GlobalValues, +) -> [MASQRealNode; 3]; + +pub fn test_body( + test_inputs: TestInputs, + assertions_values: AssertionsValues, + stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, + start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, +) { + let (mut cluster, global_values) = establish_test_frame(test_inputs); + let consuming_node_attributes = global_values.prepare_consuming_node( + &mut cluster, + &global_values.blockchain_params.blockchain_interfaces, + ); + let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); + global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node_attributes); + global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node_attributes); + let mut wholesome_config = WholesomeConfig::new( + global_values, + consuming_node_attributes, + serving_nodes_array, + ); + wholesome_config.assert_expected_wallet_addresses(); + let real_consuming_node = cluster.start_named_real_node( + &wholesome_config.consuming_node.common.node_id.node_name, + wholesome_config.consuming_node.common.node_id.index, + wholesome_config + .consuming_node + .common + .config_opt + .take() + .unwrap(), + ); + + stimulate_consuming_node_to_pay( + &mut cluster, + &real_consuming_node, + &wholesome_config.global_values, + ); + + let timeout_start = Instant::now(); + while !wholesome_config + .consuming_node + .payable_dao + .non_pending_payables() + .is_empty() + && timeout_start.elapsed() < Duration::from_secs(10) + { + thread::sleep(Duration::from_millis(400)); + } + wholesome_config.assert_payments_via_direct_blockchain_scanning(&assertions_values); + + let _ = start_serving_nodes_and_activate_their_accountancy( + &mut cluster, + // So that individual Configs can be pulled out and used + &mut wholesome_config.serving_nodes, + &wholesome_config.global_values, + ); + + wholesome_config.assert_serving_nodes_addressed_received_payments(&assertions_values) +} + +const MNEMONIC_PHRASE: &str = + "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ + lamp absent write kind term toddler sphere ripple idle dragon curious hold"; + +pub struct TestInputs { + ui_ports_opt: Option, + // The contract owner wallet is populated with 100 ETH as defined in the set of commands + // with which we start up the Ganache server. + // + // Specify number of wei this account should possess at its initialisation. + // The consuming node gets the full balance of the contract owner if left as None. + // Cannot ever get more than what the "owner" has. + consuming_node_initial_tx_fee_balance_minor_opt: Option, + consuming_node_initial_service_fee_balance_minor: u128, + debts_config: DebtsSpecs, + payment_thresholds_all_nodes: PaymentThresholds, + consuming_node_gas_price_opt: Option, +} + +#[derive(Default)] +pub struct TestInputsBuilder { + ui_ports_opt: Option, + consuming_node_initial_tx_fee_balance_minor_opt: Option, + consuming_node_initial_service_fee_balance_minor_opt: Option, + debts_config_opt: Option, + payment_thresholds_all_nodes_opt: Option, + consuming_node_gas_price_opt: Option, +} + +impl TestInputsBuilder { + pub fn ui_ports(mut self, ports: Ports) -> Self { + self.ui_ports_opt = Some(ports); + self + } + + pub fn consuming_node_initial_tx_fee_balance_minor(mut self, balance: u128) -> Self { + self.consuming_node_initial_tx_fee_balance_minor_opt = Some(balance); + self + } + + pub fn consuming_node_initial_service_fee_balance_minor(mut self, balance: u128) -> Self { + self.consuming_node_initial_service_fee_balance_minor_opt = Some(balance); + self + } + + pub fn debts_config(mut self, debts: DebtsSpecs) -> Self { + self.debts_config_opt = Some(debts); + self + } + + pub fn payment_thresholds_all_nodes(mut self, thresholds: PaymentThresholds) -> Self { + self.payment_thresholds_all_nodes_opt = Some(thresholds); + self + } + + pub fn consuming_node_gas_price_major(mut self, gas_price: u64) -> Self { + self.consuming_node_gas_price_opt = Some(gas_price); + self + } + + pub fn build(self) -> TestInputs { + TestInputs{ + ui_ports_opt: self.ui_ports_opt, + consuming_node_initial_tx_fee_balance_minor_opt: self.consuming_node_initial_tx_fee_balance_minor_opt, + consuming_node_initial_service_fee_balance_minor: self.consuming_node_initial_service_fee_balance_minor_opt.expect("You forgot providing a mandatory input: consuming node initial service fee balance"), + debts_config: self.debts_config_opt.expect("You forgot providing a mandatory input: debts config"), + payment_thresholds_all_nodes: self.payment_thresholds_all_nodes_opt.expect("You forgot providing a mandatory input: payment thresholds"), + consuming_node_gas_price_opt: self.consuming_node_gas_price_opt, + } + } +} + +pub struct AssertionsValues { + pub final_consuming_node_transaction_fee_balance_minor: u128, + pub final_consuming_node_service_fee_balance_minor: u128, + pub final_service_fee_balances_by_serving_nodes: FinalServiceFeeBalancesByServingNodes, +} + +pub struct FinalServiceFeeBalancesByServingNodes { + balances: [u128; 3], +} + +impl FinalServiceFeeBalancesByServingNodes { + pub fn new(node_1: u128, node_2: u128, node_3: u128) -> Self { + Self { + balances: [node_1, node_2, node_3], + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NodeByRole { + ConsumingNode = 1, + ServingNode1 = 2, + ServingNode2 = 3, + ServingNode3 = 4, +} + +pub struct BlockchainParams { + blockchain_interfaces: BlockchainInterfaces, + chain: Chain, + server_url: String, + contract_owner_addr: Address, + contract_owner_wallet: Wallet, + seed: Seed, +} + +struct BlockchainInterfaces { + blockchain_interface: Box, + web3: Web3, +} + +pub struct GlobalValues { + pub test_inputs: TestInputs, + pub blockchain_params: BlockchainParams, + pub now_in_common: SystemTime, +} + +pub struct WholesomeConfig { + pub global_values: GlobalValues, + pub consuming_node: ConsumingNodeAttributes, + pub serving_nodes: [ServingNodeAttributes; 3], +} + +pub struct DebtsSpecs { + debts: [Debt; 3], +} + +impl DebtsSpecs { + pub fn new(node_1: Debt, node_2: Debt, node_3: Debt) -> Self { + Self { + debts: [node_1, node_2, node_3], + } + } +} + +#[derive(Copy, Clone)] +pub struct Debt { + pub balance_minor: u128, + pub age_s: u64, +} + +impl Debt { + pub fn new(balance_minor: u128, age_s: u64) -> Self { + Self { + balance_minor, + age_s, + } + } + + fn proper_timestamp(&self, now: SystemTime) -> SystemTime { + now.checked_sub(Duration::from_secs(self.age_s)).unwrap() + } +} + +impl TestInputs { + pub fn port(&self, requested: NodeByRole) -> Option { + self.ui_ports_opt.as_ref().map(|ports| match requested { + NodeByRole::ConsumingNode => ports.consuming_node, + NodeByRole::ServingNode1 => ports.serving_nodes[0], + NodeByRole::ServingNode2 => ports.serving_nodes[1], + NodeByRole::ServingNode3 => ports.serving_nodes[2], + }) + } + + pub fn debt_specs(&self, requested: NodeByRole) -> Debt { + match requested { + NodeByRole::ServingNode1 => self.debts_config.debts[0], + NodeByRole::ServingNode2 => self.debts_config.debts[1], + NodeByRole::ServingNode3 => self.debts_config.debts[2], + NodeByRole::ConsumingNode => panic!( + "Version fully specified: These configs describe debts owed to the consuming node, \ + while that one should not be here." + ), + } + } +} + +pub fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, GlobalValues) { + let now = SystemTime::now(); + let cluster = match MASQNodeCluster::start() { + Ok(cluster) => cluster, + Err(e) => panic!("{}", e), + }; + let blockchain_server = BlockchainServer { + name: "ganache-cli", + }; + blockchain_server.start(); + blockchain_server.wait_until_ready(); + let server_url = blockchain_server.url().to_string(); + let (event_loop_handle, http) = + Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); + let web3 = Web3::new(http.clone()); + let seed = make_seed(); + let (contract_owner_wallet, _) = make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); + let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); + let blockchain_interface = Box::new(BlockchainInterfaceWeb3::new( + http, + event_loop_handle, + cluster.chain, + )); + let blockchain_params = BlockchainParams { + blockchain_interfaces: BlockchainInterfaces { + blockchain_interface, + web3, + }, + chain: cluster.chain, + server_url, + contract_owner_addr, + contract_owner_wallet, + seed, + }; + let global_values = GlobalValues { + test_inputs, + blockchain_params, + now_in_common: now, + }; + assert_eq!( + contract_owner_addr, + cluster.chain.rec().contract, + "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ + Resulted contact addr {:?} doesn't much what's expected: {:?}", + contract_owner_addr, + cluster.chain.rec().contract + ); + + (cluster, global_values) +} + +fn make_seed() -> Seed { + let mnemonic = Mnemonic::from_phrase(MNEMONIC_PHRASE, Language::English).unwrap(); + Seed::new(&mnemonic, "") +} + +pub fn to_wei(gwei: u64) -> u128 { + gwei_to_wei(gwei) +} + +fn make_db_init_config(chain: Chain) -> DbInitializationConfig { + DbInitializationConfig::create_or_migrate(ExternalData::new( + chain, + NeighborhoodModeLight::Standard, + None, + )) +} + +fn load_contract_in_bytes() -> Vec { + let file_path = + standard_dir_for_test_input_data().join("smart_contract_for_on_blockchain_test"); + let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); + let mut data = String::new(); + file.read_to_string(&mut data).unwrap(); + let data = data + .chars() + .filter(|char| !char.is_whitespace()) + .collect::(); + data.from_hex::>() + .expect("bad contract: contains non-hexadecimal characters") +} + +lazy_static! { + static ref GAS_PRICE: ethereum_types::U256 = 50_u64.try_into().expect("Gas price, internal error"); + static ref GAS_LIMIT: ethereum_types::U256 = 1_000_000_u64.try_into().expect("Gas limit, internal error"); +} + +fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { + let contract = load_contract_in_bytes(); + let tx = TransactionParameters { + nonce: Some(ethereum_types::U256::zero()), + to: None, + gas: *GAS_LIMIT, + gas_price: Some(*GAS_PRICE), + value: ethereum_types::U256::zero(), + data: Bytes(contract), + chain_id: Some(chain.rec().num_chain_id), + }; + let signed_tx = primitive_sign_transaction(web3, tx, wallet); + match web3 + .eth() + .send_raw_transaction(signed_tx.raw_transaction) + .wait() + { + Ok(tx_hash) => match web3.eth().transaction_receipt(tx_hash).wait() { + Ok(Some(tx_receipt)) => tx_receipt.contract_address.unwrap(), + Ok(None) => panic!("Contract deployment failed Ok(None)"), + Err(e) => panic!("Contract deployment failed {:?}", e), + }, + Err(e) => panic!("Contract deployment failed {:?}", e), + } +} + +fn transfer_service_fee_amount_to_address( + contract_addr: Address, + from_wallet: &Wallet, + to_wallet: &Wallet, + amount_minor: u128, + transaction_nonce: u64, + web3: &Web3, + chain: Chain, +) { + let data = transaction_data_web3(to_wallet, amount_minor); + let tx = TransactionParameters { + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + to: Some(contract_addr), + gas: *GAS_LIMIT, + gas_price: Some(*GAS_PRICE), + value: ethereum_types::U256::zero(), + data: Bytes(data.to_vec()), + chain_id: Some(chain.rec().num_chain_id), + }; + let signed_tx = primitive_sign_transaction(web3, tx, from_wallet); + match web3 + .eth() + .send_raw_transaction(signed_tx.raw_transaction) + .wait() + { + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of MASQ was sent from wallet {} to {}", + tx_hash, amount_minor, from_wallet, to_wallet + ), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } +} + +fn primitive_sign_transaction( + web3: &Web3, + tx: TransactionParameters, + signing_wallet: &Wallet, +) -> SignedTransaction { + web3.accounts() + .sign_transaction( + tx, + &signing_wallet + .prepare_secp256k1_secret() + .expect("wallet without secret"), + ) + .wait() + .expect("transaction preparation failed") +} + +fn transfer_transaction_fee_amount_to_address( + from_wallet: &Wallet, + to_wallet: &Wallet, + amount_minor: u128, + transaction_nonce: u64, + web3: &Web3, +) { + let tx = TransactionRequest { + from: from_wallet.address(), + to: Some(to_wallet.address()), + gas: Some(*GAS_LIMIT), + gas_price: Some(*GAS_PRICE), + value: Some(ethereum_types::U256::from(amount_minor)), + data: None, + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + condition: None, + }; + match web3 + .personal() + .unlock_account(from_wallet.address(), "", None) + .wait() + { + Ok(was_successful) => { + if was_successful { + eprintln!("Account {} unlocked for a single transaction", from_wallet) + } else { + panic!( + "Couldn't unlock account {} for the purpose of signing the next transaction", + from_wallet + ) + } + } + Err(e) => panic!( + "Attempt to unlock account {:?} failed at {:?}", + from_wallet.address(), + e + ), + } + match web3.eth().send_transaction(tx).wait() { + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", + tx_hash, amount_minor, from_wallet, to_wallet + ), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } +} + +fn assert_balances( + wallet: &Wallet, + blockchain_interface: &dyn BlockchainInterface, + expected_eth_balance: u128, + expected_token_balance: u128, +) { + let eth_balance = blockchain_interface + .lower_interface() + .get_transaction_fee_balance(&wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); + assert_eq!( + eth_balance, + web3::types::U256::from(expected_eth_balance), + "Actual EthBalance {} doesn't much with expected {} for {}", + eth_balance, + expected_eth_balance, + wallet + ); + let token_balance = blockchain_interface + .lower_interface() + .get_service_fee_balance(&wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); + assert_eq!( + token_balance, + web3::types::U256::from(expected_token_balance), + "Actual TokenBalance {} doesn't match with expected {} for {}", + token_balance, + expected_token_balance, + wallet + ); +} + +fn make_node_wallet_and_private_key(seed: &Seed, derivation_path: &str) -> (Wallet, String) { + let extended_private_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); + let str_private_key: String = extended_private_key.secret().to_hex(); + let wallet = Wallet::from(Bip32EncryptionKeyProvider::from_key(extended_private_key)); + ( + wallet, + str_private_key, + ) +} + +impl GlobalValues { + fn get_node_config_and_wallet(&self, node_by_role: NodeByRole) -> (NodeStartupConfig, Wallet) { + let wallet_derivation_path = node_by_role.derivation_path(); + let payment_thresholds = self.test_inputs.payment_thresholds_all_nodes; + let (node_wallet, node_secret) = make_node_wallet_and_private_key( + &self.blockchain_params.seed, + wallet_derivation_path.as_str(), + ); + let mut cfg_to_build = NodeStartupConfigBuilder::standard() + .blockchain_service_url(&self.blockchain_params.server_url) + .chain(Chain::Dev) + .payment_thresholds(payment_thresholds) + .consuming_wallet_info(ConsumingWalletInfo::PrivateKey(node_secret)) + .earning_wallet_info(EarningWalletInfo::Address(format!( + "{}", + node_wallet.clone() + ))); + if let Some(port) = self.test_inputs.port(node_by_role) { + cfg_to_build = cfg_to_build.ui_port(port) + } + if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { + cfg_to_build = cfg_to_build.gas_price(price) + } + + eprintln!("{:?} wallet established: {}\n", node_by_role, node_wallet,); + + (cfg_to_build.build(), node_wallet) + } + + fn prepare_consuming_node( + &self, + cluster: &mut MASQNodeCluster, + blockchain_interfaces: &BlockchainInterfaces, + ) -> ConsumingNodeAttributes { + let (consuming_node_config, consuming_node_wallet) = + self.get_node_config_and_wallet(NodeByRole::ConsumingNode); + let initial_transaction_fee_balance = self + .test_inputs + .consuming_node_initial_tx_fee_balance_minor_opt + .unwrap_or(ONE_ETH_IN_WEI); + transfer_transaction_fee_amount_to_address( + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + initial_transaction_fee_balance, + 1, + &blockchain_interfaces.web3, + ); + transfer_service_fee_amount_to_address( + self.blockchain_params.contract_owner_addr, + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + self.test_inputs + .consuming_node_initial_service_fee_balance_minor, + 2, + &blockchain_interfaces.web3, + self.blockchain_params.chain, + ); + assert_balances( + &consuming_node_wallet, + blockchain_interfaces.blockchain_interface.as_ref(), + initial_transaction_fee_balance, + self.test_inputs + .consuming_node_initial_service_fee_balance_minor, + ); + let consuming_node_namings = cluster.prepare_real_node(&consuming_node_config); + let consuming_node_connection = DbInitializerReal::default() + .initialize( + Path::new(&consuming_node_namings.db_path), + make_db_init_config(cluster.chain), + ) + .unwrap(); + let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); + ConsumingNodeAttributes::new( + NodeByRole::ConsumingNode, + consuming_node_namings, + Some(consuming_node_config), + consuming_node_wallet, + consuming_node_payable_dao, + ) + } + + fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { + [ + NodeByRole::ServingNode1, + NodeByRole::ServingNode2, + NodeByRole::ServingNode3, + ] + .into_iter() + .map(|node_by_role: NodeByRole| { + let (serving_node_config, serving_node_earning_wallet) = + self.get_node_config_and_wallet(node_by_role); + let serving_node_namings = cluster.prepare_real_node(&serving_node_config); + let serving_node_connection = DbInitializerReal::default() + .initialize( + &serving_node_namings.db_path, + make_db_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); + ServingNodeAttributes::new( + node_by_role, + serving_node_namings, + Some(serving_node_config), + serving_node_earning_wallet, + serving_node_receivable_dao, + ) + }) + .collect::>() + .try_into() + .expect("failed to make [T;3] of provided Vec") + } + + fn set_start_block_to_zero(path: &Path) { + DbInitializerReal::default() + .initialize(path, DbInitializationConfig::panic_on_migration()) + .unwrap() + .prepare("update config set value = '0' where name = 'start_block'") + .unwrap() + .execute([]) + .unwrap(); + } + + fn serving_node_debt_balance_and_timestamp( + &self, + attributes: &ServingNodeAttributes, + ) -> (u128, SystemTime) { + let node_role = attributes.common.node_by_role; + let debt_specs = self.test_inputs.debt_specs(node_role); + ( + debt_specs.balance_minor, + debt_specs.proper_timestamp(self.now_in_common), + ) + } + + fn set_up_serving_nodes_databases( + &self, + serving_nodes_matrix: &[ServingNodeAttributes; 3], + consuming_node_attributes: &ConsumingNodeAttributes, + ) { + serving_nodes_matrix.iter().for_each(|node_attributes| { + let (balance, timestamp) = + self.serving_node_debt_balance_and_timestamp(node_attributes); + node_attributes + .receivable_dao + .more_money_receivable( + timestamp, + &consuming_node_attributes.consuming_wallet, + balance, + ) + .unwrap(); + assert_balances( + &node_attributes.earning_wallet, + self.blockchain_params + .blockchain_interfaces + .blockchain_interface + .as_ref(), + 0, + 0, + ); + Self::set_start_block_to_zero(&node_attributes.common.node_id.db_path) + }) + } + + fn set_up_consuming_node_db( + &self, + serving_nodes_array: &[ServingNodeAttributes; 3], + consuming_node_attributes: &ConsumingNodeAttributes, + ) { + serving_nodes_array.iter().for_each(|node_attributes| { + let (balance, timestamp) = + self.serving_node_debt_balance_and_timestamp(node_attributes); + consuming_node_attributes + .payable_dao + .more_money_payable(timestamp, &node_attributes.earning_wallet, balance) + .unwrap(); + }); + Self::set_start_block_to_zero(&consuming_node_attributes.common.node_id.db_path) + } +} + +impl WholesomeConfig { + fn new( + global_values: GlobalValues, + consuming_node: ConsumingNodeAttributes, + serving_nodes: [ServingNodeAttributes; 3], + ) -> Self { + WholesomeConfig { + global_values, + consuming_node, + serving_nodes, + } + } + + fn assert_expected_wallet_addresses(&self) { + let consuming_node_actual = self.consuming_node.consuming_wallet.to_string(); + let consuming_node_expected = "0x7a3cf474962646b18666b5a5be597bb0af013d81"; + assert_eq!( + &consuming_node_actual, consuming_node_expected, + "Consuming Node's wallet {} mismatched with expected {}", + consuming_node_actual, consuming_node_expected + ); + vec![ + "0x0bd8bc4b8aba5d8abf13ea78a6668ad0e9985ad6", + "0xb329c8b029a2d3d217e71bc4d188e8e1a4a8b924", + "0xb45a33ef3e3097f34c826369b74141ed268cdb5a", + ] + .iter() + .zip(self.serving_nodes.iter()) + .for_each(|(expected_wallet_addr, serving_node_attributes)| { + let serving_node_actual = serving_node_attributes.earning_wallet.to_string(); + assert_eq!( + &serving_node_actual, + expected_wallet_addr, + "{:?} wallet {} mismatched with expected {}", + serving_node_attributes.common.node_by_role, + serving_node_actual, + expected_wallet_addr + ); + }) + } + + fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { + let blockchain_interface = self + .global_values + .blockchain_params + .blockchain_interfaces + .blockchain_interface + .as_ref(); + assert_balances( + &self.consuming_node.consuming_wallet, + blockchain_interface, + assertions_values.final_consuming_node_transaction_fee_balance_minor, + assertions_values.final_consuming_node_service_fee_balance_minor, + ); + assertions_values + .final_service_fee_balances_by_serving_nodes + .balances + .into_iter() + .zip(self.serving_nodes.iter()) + .for_each(|(expected_remaining_owed_value, serving_node)| { + assert_balances( + &serving_node.earning_wallet, + blockchain_interface, + 0, + expected_remaining_owed_value, + ); + }) + } + + fn assert_serving_nodes_addressed_received_payments( + &self, + assertions_values: &AssertionsValues, + ) { + let actually_received_payments = assertions_values + .final_service_fee_balances_by_serving_nodes + .balances; + let consuming_node_wallet = &self.consuming_node.consuming_wallet; + self.serving_nodes + .iter() + .zip(actually_received_payments.into_iter()) + .for_each(|(serving_node, received_payment)| { + let original_debt = self + .global_values + .test_inputs + .debt_specs(serving_node.common.node_by_role) + .balance_minor; + let expected_final_balance = original_debt - received_payment; + Self::wait_for_exact_balance_in_receivables( + &serving_node.receivable_dao, + expected_final_balance, + consuming_node_wallet, + ) + }) + } + + fn wait_for_exact_balance_in_receivables( + node_receivable_dao: &ReceivableDaoReal, + expected_value: u128, + consuming_node_wallet: &Wallet, + ) { + test_utils::wait_for(Some(1000), Some(15000), || { + if let Some(status) = node_receivable_dao.account_status(&consuming_node_wallet) { + status.balance_wei == i128::try_from(expected_value).unwrap() + } else { + false + } + }); + } +} + +impl NodeByRole { + fn derivation_path(self) -> String { + derivation_path(0, self as usize as u8) + } +} + +pub const ONE_ETH_IN_WEI: u128 = 10_u128.pow(18); + +pub struct Ports { + consuming_node: u16, + serving_nodes: [u16; 3], +} + +impl Ports { + pub fn new( + consuming_node: u16, + serving_node_1: u16, + serving_node_2: u16, + serving_node_3: u16, + ) -> Self { + Self { + consuming_node, + serving_nodes: [serving_node_1, serving_node_2, serving_node_3], + } + } +} + +#[derive(Debug)] +pub struct NodeAttributesCommon { + pub node_by_role: NodeByRole, + pub node_id: NodeID, + pub config_opt: Option, +} + +#[derive(Debug)] +pub struct ConsumingNodeAttributes { + pub common: NodeAttributesCommon, + pub consuming_wallet: Wallet, + pub payable_dao: PayableDaoReal, +} + +#[derive(Debug)] +pub struct ServingNodeAttributes { + pub common: NodeAttributesCommon, + pub earning_wallet: Wallet, + pub receivable_dao: ReceivableDaoReal, +} + +impl ConsumingNodeAttributes { + fn new( + node_by_role: NodeByRole, + node_id: NodeID, + config_opt: Option, + consuming_wallet: Wallet, + payable_dao: PayableDaoReal, + ) -> Self { + let common = NodeAttributesCommon { + node_by_role, + node_id, + config_opt, + }; + Self { + common, + consuming_wallet, + payable_dao, + } + } +} + +impl ServingNodeAttributes { + fn new( + node_by_role: NodeByRole, + node_id: NodeID, + config_opt: Option, + earning_wallet: Wallet, + receivable_dao: ReceivableDaoReal, + ) -> Self { + let common = NodeAttributesCommon { + node_by_role, + node_id, + config_opt, + }; + Self { + common, + earning_wallet, + receivable_dao, + } + } +} diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 05ac52f24..b40560616 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -59,7 +59,7 @@ use std::io::ErrorKind; use std::io::Read; use std::iter::repeat; use std::net::{Shutdown, TcpStream}; -use std::path::{Path, PathBuf}; +use std::path::{PathBuf}; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; From eedf9ce798d609d81ac080623eddc72faeb0a523 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 23 Sep 2024 00:03:54 +0200 Subject: [PATCH 201/250] GH-711-review-one: interim commit --- .../tests/verify_bill_payment.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 6426a8874..9ed63bd67 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -1,4 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + use crate::verify_bill_payment_utils::utils::{ test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, GlobalValues, NodeByRole, Ports, ServingNodeAttributes, TestInputsBuilder, @@ -14,7 +15,6 @@ use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{MASQRealNode, NodeStartupConfigBuilder, }; -use node_lib::accountant::gwei_to_wei; use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::{ compute_gas_limit, web3_gas_limit_const_part, @@ -141,14 +141,14 @@ fn payments_were_adjusted_due_to_insufficient_balances() { unban_below_gwei: 1_000_000, }; // Assuming all Nodes rely on the same set of payment thresholds - let owed_to_serv_node_1_minor = gwei_to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); + let owed_to_serv_node_1_minor = to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); let owed_to_serv_node_2_minor = - gwei_to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); + to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); // Account of Node 3 will be a victim of tx fee insufficiency and will fall away, as its debt // is the heaviest, implying the smallest weight evaluated and the last priority compared to // those two others. let owed_to_serv_node_3_minor = - gwei_to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); + to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); let enough_balance_for_serving_node_1_and_2 = owed_to_serv_node_1_minor + owed_to_serv_node_2_minor; let consuming_node_initial_service_fee_balance_minor = @@ -171,7 +171,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }; const AFFORDABLE_PAYMENTS_COUNT: u128 = 2; let tx_fee_needed_to_pay_for_one_payment_minor: u128 = - gwei_to_wei(tx_fee_needed_to_pay_for_one_payment_major); + to_wei(tx_fee_needed_to_pay_for_one_payment_major); let consuming_node_transaction_fee_balance_minor = AFFORDABLE_PAYMENTS_COUNT * tx_fee_needed_to_pay_for_one_payment_minor; let test_inputs = TestInputsBuilder::default() From a9f86aecfb5bfbf5e0b16122c8e6b7d60557140b Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 25 Sep 2024 23:06:41 +0200 Subject: [PATCH 202/250] GH-711-review-one: the revamp of the verify_bill_payment is finishing, next I should check if the test is still passing --- masq_lib/src/percentage.rs | 2 +- .../src/masq_node_cluster.rs | 2 +- .../src/masq_real_node.rs | 2 +- .../tests/blockchain_interaction_test.rs | 19 +- .../tests/verify_bill_payment.rs | 75 +-- .../tests/verify_bill_payment_utils/utils.rs | 464 +++++++++++------- node/src/test_utils/mod.rs | 2 +- 7 files changed, 338 insertions(+), 228 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index a73f2e7fe..8edd7f692 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -5,7 +5,7 @@ use num::CheckedSub; use num::{CheckedDiv, CheckedMul, Integer}; use std::any::type_name; use std::fmt::Debug; -use std::ops::{Rem}; +use std::ops::Rem; // Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage // operations over a wide variety of integer types. It is also to look after the least significant // digit on the resulted number in order to avoid the effect of a loss on precision that genuinely diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index bc2685652..8faa266ca 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -63,7 +63,7 @@ impl MASQNodeCluster { open_all_file_permissions(&db_path); NodeID { - node_name: name, + node_docker_name: name, index, db_path, } diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 2e783f201..bf9d5e329 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -1223,7 +1223,7 @@ impl MASQRealNode { #[derive(Debug)] pub struct NodeID { - pub node_name: String, + pub node_docker_name: String, pub index: usize, pub db_path: PathBuf, } diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 507036630..98c4813ad 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -73,13 +73,13 @@ fn debtors_are_credited_once_but_not_twice() { .build(); let node_namings = cluster.prepare_real_node(&node_config); { - let config_dao = config_dao(&node_namings.node_name); + let config_dao = config_dao(&node_namings.node_docker_name); config_dao .set("start_block", Some("1000".to_string())) .unwrap(); } { - let receivable_dao = receivable_dao(&node_namings.node_name); + let receivable_dao = receivable_dao(&node_namings.node_docker_name); receivable_dao .more_money_receivable( SystemTime::UNIX_EPOCH.add(Duration::from_secs(15_000_000)), @@ -90,7 +90,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the receivable DAO to verify that the receivable's balance has been initialized { - let receivable_dao = receivable_dao(&node_namings.node_name); + let receivable_dao = receivable_dao(&node_namings.node_docker_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -105,14 +105,17 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been set to 1000 { - let config_dao = config_dao(&node_namings.node_name); + let config_dao = config_dao(&node_namings.node_docker_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "1000" ); } - let node = - cluster.start_named_real_node(&node_namings.node_name, node_namings.index, node_config); + let node = cluster.start_named_real_node( + &node_namings.node_docker_name, + node_namings.index, + node_config, + ); let ui_client = node.make_ui(ui_port); // Command a scan log ui_client.send_request( @@ -128,7 +131,7 @@ fn debtors_are_credited_once_but_not_twice() { node.kill_node(); // Use the receivable DAO to verify that the receivable's balance has been adjusted { - let receivable_dao = receivable_dao(&node_namings.node_name); + let receivable_dao = receivable_dao(&node_namings.node_docker_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -143,7 +146,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been advanced to 2001 { - let config_dao = config_dao(&node_namings.node_name); + let config_dao = config_dao(&node_namings.node_docker_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "2001" diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 9ed63bd67..363e167f3 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -2,26 +2,22 @@ use crate::verify_bill_payment_utils::utils::{ test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, - GlobalValues, NodeByRole, Ports, ServingNodeAttributes, TestInputsBuilder, + NodeProfile, Ports, TestInputsBuilder, WholesomeConfig, }; use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; use masq_lib::percentage::PurePercentage; -use masq_lib::utils::{find_free_port}; +use masq_lib::utils::find_free_port; use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; -use multinode_integration_tests_lib::masq_real_node::{MASQRealNode, - NodeStartupConfigBuilder, -}; +use multinode_integration_tests_lib::masq_real_node::{MASQRealNode, NodeStartupConfigBuilder}; use node_lib::sub_lib::accountant::PaymentThresholds; -use node_lib::sub_lib::blockchain_interface_web3::{ - compute_gas_limit, web3_gas_limit_const_part, -}; +use node_lib::sub_lib::blockchain_interface_web3::{compute_gas_limit, web3_gas_limit_const_part}; use std::convert::TryFrom; use std::time::{Duration, UNIX_EPOCH}; -use std::{u128}; +use std::u128; mod verify_bill_payment_utils; #[test] @@ -85,7 +81,7 @@ fn full_payments_were_processed_for_sufficient_balances() { fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds( cluster: &mut MASQNodeCluster, real_consuming_node: &MASQRealNode, - _global_values: &GlobalValues, + _wholesome_config: &WholesomeConfig, ) { for _ in 0..6 { cluster.start_real_node( @@ -99,17 +95,22 @@ fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds( fn activating_serving_nodes_for_test_with_sufficient_funds( cluster: &mut MASQNodeCluster, - serving_nodes: &mut [ServingNodeAttributes; 3], - _global_values: &GlobalValues, + wholesome_values: &WholesomeConfig, ) -> [MASQRealNode; 3] { - let (node_references, serving_nodes): (Vec<_>, Vec<_>) = serving_nodes - .into_iter() + let (node_references, serving_nodes): (Vec<_>, Vec<_>) = wholesome_values + .serving_nodes + .iter() .map(|attributes| { let namings = &attributes.common.node_id; cluster.start_named_real_node( - &namings.node_name, + &namings.node_docker_name, namings.index, - attributes.common.config_opt.take().unwrap(), + attributes + .common + .startup_config_opt + .borrow_mut() + .take() + .unwrap(), ) }) .map(|node| (node.node_reference(), node)) @@ -142,13 +143,11 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }; // Assuming all Nodes rely on the same set of payment thresholds let owed_to_serv_node_1_minor = to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); - let owed_to_serv_node_2_minor = - to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); + let owed_to_serv_node_2_minor = to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); // Account of Node 3 will be a victim of tx fee insufficiency and will fall away, as its debt // is the heaviest, implying the smallest weight evaluated and the last priority compared to // those two others. - let owed_to_serv_node_3_minor = - to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); + let owed_to_serv_node_3_minor = to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); let enough_balance_for_serving_node_1_and_2 = owed_to_serv_node_1_minor + owed_to_serv_node_2_minor; let consuming_node_initial_service_fee_balance_minor = @@ -234,14 +233,15 @@ fn payments_were_adjusted_due_to_insufficient_balances() { fn stimulate_consuming_node_to_pay_for_test_with_insufficient_funds( _cluster: &mut MASQNodeCluster, real_consuming_node: &MASQRealNode, - global_values: &GlobalValues, + wholesome_config: &WholesomeConfig, ) { process_scan_request_to_node( &real_consuming_node, - global_values - .test_inputs - .port(NodeByRole::ConsumingNode) - .unwrap(), + wholesome_config + .consuming_node + .node_profile + .ui_port() + .expect("UI port missing"), ScanType::Payables, 1111, ) @@ -249,23 +249,28 @@ fn stimulate_consuming_node_to_pay_for_test_with_insufficient_funds( fn activating_serving_nodes_for_test_with_insufficient_funds( cluster: &mut MASQNodeCluster, - serving_nodes: &mut [ServingNodeAttributes; 3], - global_values: &GlobalValues, + wholesome_config: &WholesomeConfig, ) -> [MASQRealNode; 3] { - let real_nodes: Vec<_> = serving_nodes - .iter_mut() + let real_nodes: Vec<_> = wholesome_config + .serving_nodes + .iter() .enumerate() .map(|(idx, serving_node_attributes)| { - let node_config = serving_node_attributes.common.config_opt.take().unwrap(); + let node_config = serving_node_attributes + .common + .startup_config_opt + .borrow_mut() + .take() + .unwrap(); let common = &serving_node_attributes.common; let serving_node = cluster.start_named_real_node( - &common.node_id.node_name, + &common.node_id.node_docker_name, common.node_id.index, node_config, ); - let ui_port = global_values - .test_inputs - .port(common.node_by_role) + let ui_port = serving_node_attributes + .serving_node_profile + .ui_port() .expect("ui port missing"); process_scan_request_to_node( @@ -291,4 +296,4 @@ fn process_scan_request_to_node( ui_client.send_request(UiScanRequest { scan_type }.tmb(context_id)); let response = ui_client.wait_for_response(context_id, Duration::from_secs(10)); UiScanResponse::fmb(response).expect("Scan request went wrong"); -} \ No newline at end of file +} diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index dce0ab452..c99e970b5 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -2,6 +2,7 @@ use bip39::{Language, Mnemonic, Seed}; use futures::Future; +use lazy_static::lazy_static; use masq_lib::blockchains::chains::Chain; use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; @@ -18,6 +19,9 @@ use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, }; +use node_lib::blockchain::blockchain_interface::lower_level_interface::{ + LowBlockchainInt, ResultForBalance, +}; use node_lib::blockchain::blockchain_interface::BlockchainInterface; use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, @@ -28,25 +32,22 @@ use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; use node_lib::test_utils::standard_dir_for_test_input_data; use rustc_hex::{FromHex, ToHex}; +use std::cell::RefCell; +use std::collections::VecDeque; use std::fs::File; use std::io::Read; use std::path::Path; use std::thread; use std::time::{Duration, Instant, SystemTime}; -use lazy_static::lazy_static; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; use web3::Web3; -pub type StimulateConsumingNodePayments = - fn(&mut MASQNodeCluster, &MASQRealNode, &GlobalValues); +pub type StimulateConsumingNodePayments = fn(&mut MASQNodeCluster, &MASQRealNode, &WholesomeConfig); -pub type StartServingNodesAndLetThemPerformReceivablesCheck = fn( - &mut MASQNodeCluster, - &mut [ServingNodeAttributes; 3], - &GlobalValues, -) -> [MASQRealNode; 3]; +pub type StartServingNodesAndLetThemPerformReceivablesCheck = + fn(&mut MASQNodeCluster, &WholesomeConfig) -> [MASQRealNode; 3]; pub fn test_body( test_inputs: TestInputs, @@ -62,28 +63,29 @@ pub fn test_body( let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node_attributes); global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node_attributes); - let mut wholesome_config = WholesomeConfig::new( + let wholesome_config = WholesomeConfig::new( global_values, consuming_node_attributes, serving_nodes_array, ); wholesome_config.assert_expected_wallet_addresses(); let real_consuming_node = cluster.start_named_real_node( - &wholesome_config.consuming_node.common.node_id.node_name, + &wholesome_config + .consuming_node + .common + .node_id + .node_docker_name, wholesome_config.consuming_node.common.node_id.index, wholesome_config .consuming_node .common - .config_opt + .startup_config_opt + .borrow_mut() .take() .unwrap(), ); - stimulate_consuming_node_to_pay( - &mut cluster, - &real_consuming_node, - &wholesome_config.global_values, - ); + stimulate_consuming_node_to_pay(&mut cluster, &real_consuming_node, &wholesome_config); let timeout_start = Instant::now(); while !wholesome_config @@ -100,8 +102,7 @@ pub fn test_body( let _ = start_serving_nodes_and_activate_their_accountancy( &mut cluster, // So that individual Configs can be pulled out and used - &mut wholesome_config.serving_nodes, - &wholesome_config.global_values, + &wholesome_config, ); wholesome_config.assert_serving_nodes_addressed_received_payments(&assertions_values) @@ -112,18 +113,14 @@ const MNEMONIC_PHRASE: &str = lamp absent write kind term toddler sphere ripple idle dragon curious hold"; pub struct TestInputs { - ui_ports_opt: Option, - // The contract owner wallet is populated with 100 ETH as defined in the set of commands - // with which we start up the Ganache server. + // The contract owner wallet is populated with 100 ETH as defined in the set of commands with + // which we start up the Ganache server. // - // Specify number of wei this account should possess at its initialisation. - // The consuming node gets the full balance of the contract owner if left as None. - // Cannot ever get more than what the "owner" has. - consuming_node_initial_tx_fee_balance_minor_opt: Option, - consuming_node_initial_service_fee_balance_minor: u128, - debts_config: DebtsSpecs, + // This specifies number of wei this account should possess at its initialisation. + // The consuming node gets the full balance of the contract owner if left as None. Cannot ever + // get more than what the "owner" has. payment_thresholds_all_nodes: PaymentThresholds, - consuming_node_gas_price_opt: Option, + node_profiles: NodeProfiles, } #[derive(Default)] @@ -168,17 +165,82 @@ impl TestInputsBuilder { } pub fn build(self) -> TestInputs { - TestInputs{ - ui_ports_opt: self.ui_ports_opt, - consuming_node_initial_tx_fee_balance_minor_opt: self.consuming_node_initial_tx_fee_balance_minor_opt, - consuming_node_initial_service_fee_balance_minor: self.consuming_node_initial_service_fee_balance_minor_opt.expect("You forgot providing a mandatory input: consuming node initial service fee balance"), - debts_config: self.debts_config_opt.expect("You forgot providing a mandatory input: debts config"), - payment_thresholds_all_nodes: self.payment_thresholds_all_nodes_opt.expect("You forgot providing a mandatory input: payment thresholds"), - consuming_node_gas_price_opt: self.consuming_node_gas_price_opt, + let mut debts = self + .debts_config_opt + .expect("You forgot providing a mandatory input: debts config") + .debts + .to_vec(); + let mut ui_ports = Self::resolve_ports(self.ui_ports_opt).to_vec(); + let consuming_node = ConsumingNodeProfile { + ui_port_opt: ui_ports.remove(0), + gas_price_opt: self.consuming_node_gas_price_opt, + initial_tx_fee_balance_minor_opt: self.consuming_node_initial_tx_fee_balance_minor_opt, + initial_service_fee_balance_minor: self + .consuming_node_initial_service_fee_balance_minor_opt + .expect("Mandatory input not provided: consuming node initial service fee balance"), + }; + let mut serving_nodes = [ + ServingNodeByName::ServingNode1, + ServingNodeByName::ServingNode2, + ServingNodeByName::ServingNode3, + ] + .into_iter() + .map(|serving_node_by_name| ServingNodeProfile { + serving_node_by_name, + debt: debts.remove(0), + ui_port_opt: ui_ports.remove(0), + }) + .collect::>(); + let node_profiles = NodeProfiles { + consuming_node, + serving_nodes: core::array::from_fn(|_| serving_nodes.remove(0)), + }; + + TestInputs { + payment_thresholds_all_nodes: self + .payment_thresholds_all_nodes_opt + .expect("Mandatory input not provided: payment thresholds"), + node_profiles, + } + } + + fn resolve_ports(ui_ports_opt: Option) -> [Option; 4] { + match ui_ports_opt { + Some(ui_ports) => { + let mut serialized = VecDeque::new(); + serialized.push_front(ui_ports.consuming_node); + serialized.extend(ui_ports.serving_nodes); + let array: [Option; 4] = core::array::from_fn(|_| serialized.pop_front()); + if array.iter().any(|item| item.is_none()) { + panic!("UI ports are expected for each Node, but at least one isn't populated") + } + array + } + None => Default::default(), } } } +struct NodeProfiles { + consuming_node: ConsumingNodeProfile, + serving_nodes: [ServingNodeProfile; 3], +} + +#[derive(Debug, Clone)] +pub struct ConsumingNodeProfile { + ui_port_opt: Option, + gas_price_opt: Option, + initial_tx_fee_balance_minor_opt: Option, + initial_service_fee_balance_minor: u128, +} + +#[derive(Debug, Clone)] +pub struct ServingNodeProfile { + serving_node_by_name: ServingNodeByName, + debt: Debt, + ui_port_opt: Option, +} + pub struct AssertionsValues { pub final_consuming_node_transaction_fee_balance_minor: u128, pub final_consuming_node_service_fee_balance_minor: u128, @@ -197,14 +259,6 @@ impl FinalServiceFeeBalancesByServingNodes { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum NodeByRole { - ConsumingNode = 1, - ServingNode1 = 2, - ServingNode2 = 3, - ServingNode3 = 4, -} - pub struct BlockchainParams { blockchain_interfaces: BlockchainInterfaces, chain: Chain, @@ -243,7 +297,7 @@ impl DebtsSpecs { } } -#[derive(Copy, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Debt { pub balance_minor: u128, pub age_s: u64, @@ -262,26 +316,66 @@ impl Debt { } } -impl TestInputs { - pub fn port(&self, requested: NodeByRole) -> Option { - self.ui_ports_opt.as_ref().map(|ports| match requested { - NodeByRole::ConsumingNode => ports.consuming_node, - NodeByRole::ServingNode1 => ports.serving_nodes[0], - NodeByRole::ServingNode2 => ports.serving_nodes[1], - NodeByRole::ServingNode3 => ports.serving_nodes[2], - }) +pub trait NodeProfile { + fn ui_port(&self) -> Option; + + fn debt_specs(&self) -> Debt; + + fn derivation_path(&self) -> String; + + fn name(&self) -> String; + + fn gas_price_opt(&self) -> Option; +} + +impl NodeProfile for ConsumingNodeProfile { + fn ui_port(&self) -> Option { + self.ui_port_opt } - pub fn debt_specs(&self, requested: NodeByRole) -> Debt { - match requested { - NodeByRole::ServingNode1 => self.debts_config.debts[0], - NodeByRole::ServingNode2 => self.debts_config.debts[1], - NodeByRole::ServingNode3 => self.debts_config.debts[2], - NodeByRole::ConsumingNode => panic!( - "Version fully specified: These configs describe debts owed to the consuming node, \ - while that one should not be here." - ), - } + fn debt_specs(&self) -> Debt { + panic!("This method should be called only by the serving Nodes.") + } + + fn derivation_path(&self) -> String { + derivation_path(0, 1) + } + + fn name(&self) -> String { + "ConsumingNode".to_string() + } + + fn gas_price_opt(&self) -> Option { + self.gas_price_opt + } +} + +#[derive(Debug, Clone, Copy)] +pub enum ServingNodeByName { + ServingNode1 = 1, + ServingNode2 = 2, + ServingNode3 = 3, +} + +impl NodeProfile for ServingNodeProfile { + fn ui_port(&self) -> Option { + self.ui_port_opt + } + + fn debt_specs(&self) -> Debt { + self.debt + } + + fn derivation_path(&self) -> String { + derivation_path(0, (self.serving_node_by_name as usize + 1) as u8) + } + + fn name(&self) -> String { + format!("{:?}", self.serving_node_by_name) + } + + fn gas_price_opt(&self) -> Option { + None } } @@ -301,7 +395,8 @@ pub fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, Global Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); let web3 = Web3::new(http.clone()); let seed = make_seed(); - let (contract_owner_wallet, _) = make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); + let (contract_owner_wallet, _) = + make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); let blockchain_interface = Box::new(BlockchainInterfaceWeb3::new( http, @@ -368,8 +463,10 @@ fn load_contract_in_bytes() -> Vec { } lazy_static! { - static ref GAS_PRICE: ethereum_types::U256 = 50_u64.try_into().expect("Gas price, internal error"); - static ref GAS_LIMIT: ethereum_types::U256 = 1_000_000_u64.try_into().expect("Gas limit, internal error"); + static ref GAS_PRICE: ethereum_types::U256 = + 50_u64.try_into().expect("Gas price, internal error"); + static ref GAS_LIMIT: ethereum_types::U256 = + 1_000_000_u64.try_into().expect("Gas limit, internal error"); } fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { @@ -500,28 +597,46 @@ fn assert_balances( expected_eth_balance: u128, expected_token_balance: u128, ) { - let eth_balance = blockchain_interface - .lower_interface() - .get_transaction_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve gas balance for {}", wallet)); - assert_eq!( - eth_balance, - web3::types::U256::from(expected_eth_balance), - "Actual EthBalance {} doesn't much with expected {} for {}", - eth_balance, + single_balance_assertion( + blockchain_interface, + wallet, expected_eth_balance, - wallet + "ETH balance", + |blockchain_interface, wallet| blockchain_interface.get_transaction_fee_balance(wallet), ); - let token_balance = blockchain_interface - .lower_interface() - .get_service_fee_balance(&wallet) - .unwrap_or_else(|_| panic!("Failed to retrieve masq balance for {}", wallet)); - assert_eq!( - token_balance, - web3::types::U256::from(expected_token_balance), - "Actual TokenBalance {} doesn't match with expected {} for {}", - token_balance, + + single_balance_assertion( + blockchain_interface, + wallet, expected_token_balance, + "MASQ balance", + |blockchain_interface, wallet| blockchain_interface.get_service_fee_balance(wallet), + ); +} + +fn single_balance_assertion( + blockchain_interface: &dyn BlockchainInterface, + wallet: &Wallet, + expected_balance: u128, + balance_specification: &str, + balance_fetcher: fn(&dyn LowBlockchainInt, &Wallet) -> ResultForBalance, +) { + let actual_balance = { + let lower_blockchain_int = blockchain_interface.lower_interface(); + balance_fetcher(lower_blockchain_int, &wallet).unwrap_or_else(|_| { + panic!( + "Failed to retrieve {} for {}", + balance_specification, wallet + ) + }) + }; + assert_eq!( + actual_balance, + web3::types::U256::from(expected_balance), + "Actual {} {} doesn't much with expected {} for {}", + balance_specification, + actual_balance, + expected_balance, wallet ); } @@ -530,21 +645,18 @@ fn make_node_wallet_and_private_key(seed: &Seed, derivation_path: &str) -> (Wall let extended_private_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); let str_private_key: String = extended_private_key.secret().to_hex(); let wallet = Wallet::from(Bip32EncryptionKeyProvider::from_key(extended_private_key)); - ( - wallet, - str_private_key, - ) + (wallet, str_private_key) } impl GlobalValues { - fn get_node_config_and_wallet(&self, node_by_role: NodeByRole) -> (NodeStartupConfig, Wallet) { - let wallet_derivation_path = node_by_role.derivation_path(); + fn get_node_config_and_wallet(&self, node: &dyn NodeProfile) -> (NodeStartupConfig, Wallet) { + let wallet_derivation_path = node.derivation_path(); let payment_thresholds = self.test_inputs.payment_thresholds_all_nodes; let (node_wallet, node_secret) = make_node_wallet_and_private_key( &self.blockchain_params.seed, wallet_derivation_path.as_str(), ); - let mut cfg_to_build = NodeStartupConfigBuilder::standard() + let mut config_builder = NodeStartupConfigBuilder::standard() .blockchain_service_url(&self.blockchain_params.server_url) .chain(Chain::Dev) .payment_thresholds(payment_thresholds) @@ -553,16 +665,14 @@ impl GlobalValues { "{}", node_wallet.clone() ))); - if let Some(port) = self.test_inputs.port(node_by_role) { - cfg_to_build = cfg_to_build.ui_port(port) + if let Some(port) = node.ui_port() { + config_builder = config_builder.ui_port(port) } - if let Some(price) = self.test_inputs.consuming_node_gas_price_opt { - cfg_to_build = cfg_to_build.gas_price(price) + if let Some(gas_price) = node.gas_price_opt() { + config_builder = config_builder.gas_price(gas_price) } - - eprintln!("{:?} wallet established: {}\n", node_by_role, node_wallet,); - - (cfg_to_build.build(), node_wallet) + eprintln!("{} wallet established: {}\n", node.name(), node_wallet,); + (config_builder.build(), node_wallet) } fn prepare_consuming_node( @@ -570,12 +680,14 @@ impl GlobalValues { cluster: &mut MASQNodeCluster, blockchain_interfaces: &BlockchainInterfaces, ) -> ConsumingNodeAttributes { + let consuming_node_profile = self.test_inputs.node_profiles.consuming_node.clone(); + let initial_service_fee_balance_minor = + consuming_node_profile.initial_service_fee_balance_minor; + let initial_tx_fee_balance_opt = consuming_node_profile.initial_tx_fee_balance_minor_opt; + let (consuming_node_config, consuming_node_wallet) = - self.get_node_config_and_wallet(NodeByRole::ConsumingNode); - let initial_transaction_fee_balance = self - .test_inputs - .consuming_node_initial_tx_fee_balance_minor_opt - .unwrap_or(ONE_ETH_IN_WEI); + self.get_node_config_and_wallet(&consuming_node_profile); + let initial_transaction_fee_balance = initial_tx_fee_balance_opt.unwrap_or(ONE_ETH_IN_WEI); transfer_transaction_fee_amount_to_address( &self.blockchain_params.contract_owner_wallet, &consuming_node_wallet, @@ -587,19 +699,19 @@ impl GlobalValues { self.blockchain_params.contract_owner_addr, &self.blockchain_params.contract_owner_wallet, &consuming_node_wallet, - self.test_inputs - .consuming_node_initial_service_fee_balance_minor, + initial_service_fee_balance_minor, 2, &blockchain_interfaces.web3, self.blockchain_params.chain, ); + assert_balances( &consuming_node_wallet, blockchain_interfaces.blockchain_interface.as_ref(), initial_transaction_fee_balance, - self.test_inputs - .consuming_node_initial_service_fee_balance_minor, + initial_service_fee_balance_minor, ); + let consuming_node_namings = cluster.prepare_real_node(&consuming_node_config); let consuming_node_connection = DbInitializerReal::default() .initialize( @@ -609,43 +721,42 @@ impl GlobalValues { .unwrap(); let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); ConsumingNodeAttributes::new( - NodeByRole::ConsumingNode, + consuming_node_profile, consuming_node_namings, - Some(consuming_node_config), + consuming_node_config, consuming_node_wallet, consuming_node_payable_dao, ) } fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { - [ - NodeByRole::ServingNode1, - NodeByRole::ServingNode2, - NodeByRole::ServingNode3, - ] - .into_iter() - .map(|node_by_role: NodeByRole| { - let (serving_node_config, serving_node_earning_wallet) = - self.get_node_config_and_wallet(node_by_role); - let serving_node_namings = cluster.prepare_real_node(&serving_node_config); - let serving_node_connection = DbInitializerReal::default() - .initialize( - &serving_node_namings.db_path, - make_db_init_config(cluster.chain), + self.test_inputs + .node_profiles + .serving_nodes + .clone() + .into_iter() + .map(|serving_node_profile: ServingNodeProfile| { + let (serving_node_config, serving_node_earning_wallet) = + self.get_node_config_and_wallet(&serving_node_profile); + let serving_node_namings = cluster.prepare_real_node(&serving_node_config); + let serving_node_connection = DbInitializerReal::default() + .initialize( + &serving_node_namings.db_path, + make_db_init_config(cluster.chain), + ) + .unwrap(); + let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); + ServingNodeAttributes::new( + serving_node_profile, + serving_node_namings, + serving_node_config, + serving_node_earning_wallet, + serving_node_receivable_dao, ) - .unwrap(); - let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); - ServingNodeAttributes::new( - node_by_role, - serving_node_namings, - Some(serving_node_config), - serving_node_earning_wallet, - serving_node_receivable_dao, - ) - }) - .collect::>() - .try_into() - .expect("failed to make [T;3] of provided Vec") + }) + .collect::>() + .try_into() + .expect("failed to make [T;3] of provided Vec") } fn set_start_block_to_zero(path: &Path) { @@ -658,26 +769,14 @@ impl GlobalValues { .unwrap(); } - fn serving_node_debt_balance_and_timestamp( - &self, - attributes: &ServingNodeAttributes, - ) -> (u128, SystemTime) { - let node_role = attributes.common.node_by_role; - let debt_specs = self.test_inputs.debt_specs(node_role); - ( - debt_specs.balance_minor, - debt_specs.proper_timestamp(self.now_in_common), - ) - } - fn set_up_serving_nodes_databases( &self, serving_nodes_matrix: &[ServingNodeAttributes; 3], consuming_node_attributes: &ConsumingNodeAttributes, ) { + let now = self.now_in_common; serving_nodes_matrix.iter().for_each(|node_attributes| { - let (balance, timestamp) = - self.serving_node_debt_balance_and_timestamp(node_attributes); + let (balance, timestamp) = node_attributes.debt_balance_and_timestamp(now); node_attributes .receivable_dao .more_money_receivable( @@ -704,9 +803,9 @@ impl GlobalValues { serving_nodes_array: &[ServingNodeAttributes; 3], consuming_node_attributes: &ConsumingNodeAttributes, ) { + let now = self.now_in_common; serving_nodes_array.iter().for_each(|node_attributes| { - let (balance, timestamp) = - self.serving_node_debt_balance_and_timestamp(node_attributes); + let (balance, timestamp) = node_attributes.debt_balance_and_timestamp(now); consuming_node_attributes .payable_dao .more_money_payable(timestamp, &node_attributes.earning_wallet, balance) @@ -750,7 +849,9 @@ impl WholesomeConfig { &serving_node_actual, expected_wallet_addr, "{:?} wallet {} mismatched with expected {}", - serving_node_attributes.common.node_by_role, + serving_node_attributes + .serving_node_profile + .serving_node_by_name, serving_node_actual, expected_wallet_addr ); @@ -797,11 +898,7 @@ impl WholesomeConfig { .iter() .zip(actually_received_payments.into_iter()) .for_each(|(serving_node, received_payment)| { - let original_debt = self - .global_values - .test_inputs - .debt_specs(serving_node.common.node_by_role) - .balance_minor; + let original_debt = serving_node.serving_node_profile.debt_specs().balance_minor; let expected_final_balance = original_debt - received_payment; Self::wait_for_exact_balance_in_receivables( &serving_node.receivable_dao, @@ -826,12 +923,6 @@ impl WholesomeConfig { } } -impl NodeByRole { - fn derivation_path(self) -> String { - derivation_path(0, self as usize as u8) - } -} - pub const ONE_ETH_IN_WEI: u128 = 10_u128.pow(18); pub struct Ports { @@ -855,13 +946,22 @@ impl Ports { #[derive(Debug)] pub struct NodeAttributesCommon { - pub node_by_role: NodeByRole, pub node_id: NodeID, - pub config_opt: Option, + pub startup_config_opt: RefCell>, +} + +impl NodeAttributesCommon { + fn new(node_id: NodeID, config: NodeStartupConfig) -> Self { + NodeAttributesCommon { + node_id, + startup_config_opt: RefCell::new(Some(config)), + } + } } #[derive(Debug)] pub struct ConsumingNodeAttributes { + pub node_profile: ConsumingNodeProfile, pub common: NodeAttributesCommon, pub consuming_wallet: Wallet, pub payable_dao: PayableDaoReal, @@ -869,25 +969,30 @@ pub struct ConsumingNodeAttributes { #[derive(Debug)] pub struct ServingNodeAttributes { + pub serving_node_profile: ServingNodeProfile, pub common: NodeAttributesCommon, pub earning_wallet: Wallet, pub receivable_dao: ReceivableDaoReal, } +impl ServingNodeAttributes { + fn debt_balance_and_timestamp(&self, now: SystemTime) -> (u128, SystemTime) { + let debt_specs = self.serving_node_profile.debt_specs(); + (debt_specs.balance_minor, debt_specs.proper_timestamp(now)) + } +} + impl ConsumingNodeAttributes { fn new( - node_by_role: NodeByRole, + node_profile: ConsumingNodeProfile, node_id: NodeID, - config_opt: Option, + config: NodeStartupConfig, consuming_wallet: Wallet, payable_dao: PayableDaoReal, ) -> Self { - let common = NodeAttributesCommon { - node_by_role, - node_id, - config_opt, - }; + let common = NodeAttributesCommon::new(node_id, config); Self { + node_profile, common, consuming_wallet, payable_dao, @@ -897,18 +1002,15 @@ impl ConsumingNodeAttributes { impl ServingNodeAttributes { fn new( - node_by_role: NodeByRole, + serving_node_profile: ServingNodeProfile, node_id: NodeID, - config_opt: Option, + config: NodeStartupConfig, earning_wallet: Wallet, receivable_dao: ReceivableDaoReal, ) -> Self { - let common = NodeAttributesCommon { - node_by_role, - node_id, - config_opt, - }; + let common = NodeAttributesCommon::new(node_id, config); Self { + serving_node_profile, common, earning_wallet, receivable_dao, diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index b40560616..fc90f567c 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -59,7 +59,7 @@ use std::io::ErrorKind; use std::io::Read; use std::iter::repeat; use std::net::{Shutdown, TcpStream}; -use std::path::{PathBuf}; +use std::path::PathBuf; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; From 9067c8c6faf7e7abc6c98ea305edfc609c4aa7cc Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Oct 2024 17:12:02 +0200 Subject: [PATCH 203/250] GH-711-review-one: minor clean-up --- masq_lib/src/percentage.rs | 84 +++++++------------ .../tests/blockchain_interaction_test.rs | 4 +- 2 files changed, 31 insertions(+), 57 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 8edd7f692..6a3201b3b 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -152,7 +152,7 @@ impl PurePercentage { >::Error: Debug, { let divisor = N::try_from(100).expect("Each type has 100"); - let rounded_rule = Self::should_be_rounded_as(num, divisor); + let desired_rounding = Self::should_be_rounded_to(num, divisor); let significant_digits_only = num.checked_div(&divisor).expect("Division failed"); macro_rules! adjust_num { @@ -163,20 +163,18 @@ impl PurePercentage { }; } - match rounded_rule { - RoundingRule::ToBiggerPositive => { + match desired_rounding { + RoundingTo::BiggerPositive => { adjust_num!(significant_digits_only, checked_add, "Addition failed") } - RoundingRule::ToBiggerNegative => { + RoundingTo::BiggerNegative => { adjust_num!(significant_digits_only, checked_sub, "Subtraction failed") } - RoundingRule::ToSmallerNegative | RoundingRule::ToSmallerPositive => { - significant_digits_only - } + RoundingTo::SmallerNegative | RoundingTo::SmallerPositive => significant_digits_only, } } - fn should_be_rounded_as(num: N, divisor: N) -> RoundingRule + fn should_be_rounded_to(num: N, divisor: N) -> RoundingTo where N: PercentageInteger, >::Error: Debug, @@ -186,18 +184,12 @@ impl PurePercentage { let divider = N::try_from(50).expect("Each type has 50"); let abs_of_significant_digits = Self::abs_of_least_significant_digits(least_significant_digits, is_signed); - let is_minor: bool = if abs_of_significant_digits == divider { - false - } else if abs_of_significant_digits > divider { - false - } else { - true - }; + let is_minor = abs_of_significant_digits < divider; match (is_signed, is_minor) { - (false, true) => RoundingRule::ToSmallerPositive, - (false, false) => RoundingRule::ToBiggerPositive, - (true, true) => RoundingRule::ToSmallerNegative, - (true, false) => RoundingRule::ToBiggerNegative, + (false, true) => RoundingTo::SmallerPositive, + (false, false) => RoundingTo::BiggerPositive, + (true, true) => RoundingTo::SmallerNegative, + (true, false) => RoundingTo::BiggerNegative, } } @@ -279,17 +271,17 @@ impl PurePercentage { } #[derive(Debug, PartialEq, Eq)] -enum RoundingRule { - ToBiggerPositive, - ToBiggerNegative, - ToSmallerPositive, - ToSmallerNegative, +enum RoundingTo { + BiggerPositive, + BiggerNegative, + SmallerPositive, + SmallerNegative, } #[cfg(test)] mod tests { use crate::percentage::{ - BaseTypeOverflow, LoosePercentage, PercentageInteger, PurePercentage, RoundingRule, + BaseTypeOverflow, LoosePercentage, PercentageInteger, PurePercentage, RoundingTo, }; use std::fmt::Debug; @@ -427,48 +419,32 @@ mod tests { } #[test] - fn should_be_rounded_as_works_for_last_but_one_digit() { + fn should_be_rounded_to_works_for_last_but_one_digit() { [ - ( - 49, - RoundingRule::ToSmallerPositive, - RoundingRule::ToSmallerNegative, - ), - ( - 50, - RoundingRule::ToBiggerPositive, - RoundingRule::ToBiggerNegative, - ), - ( - 51, - RoundingRule::ToBiggerPositive, - RoundingRule::ToBiggerNegative, - ), - ( - 5, - RoundingRule::ToSmallerPositive, - RoundingRule::ToSmallerNegative, - ), + (49, RoundingTo::SmallerPositive, RoundingTo::SmallerNegative), + (50, RoundingTo::BiggerPositive, RoundingTo::BiggerNegative), + (51, RoundingTo::BiggerPositive, RoundingTo::BiggerNegative), + (5, RoundingTo::SmallerPositive, RoundingTo::SmallerNegative), ( 100, - RoundingRule::ToSmallerPositive, - RoundingRule::ToSmallerNegative, + RoundingTo::SmallerPositive, + RoundingTo::SmallerNegative, ), ( 787879, - RoundingRule::ToBiggerPositive, - RoundingRule::ToBiggerNegative, + RoundingTo::BiggerPositive, + RoundingTo::BiggerNegative, ), ( 898784545, - RoundingRule::ToSmallerPositive, - RoundingRule::ToSmallerNegative, + RoundingTo::SmallerPositive, + RoundingTo::SmallerNegative, ), ] .into_iter() .for_each( |(num, expected_result_for_unsigned_base, expected_result_for_signed_base)| { - let result = PurePercentage::should_be_rounded_as(num, 100); + let result = PurePercentage::should_be_rounded_to(num, 100); assert_eq!( result, expected_result_for_unsigned_base, @@ -478,7 +454,7 @@ mod tests { expected_result_for_unsigned_base ); let signed = num as i64 * -1; - let result = PurePercentage::should_be_rounded_as(signed, 100); + let result = PurePercentage::should_be_rounded_to(signed, 100); assert_eq!( result, expected_result_for_signed_base, diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 98c4813ad..6d79f5575 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use std::ops::Add; -use std::path::PathBuf; use std::time::{Duration, SystemTime}; use log::Level; @@ -19,8 +18,7 @@ use multinode_integration_tests_lib::masq_real_node::{ }; use multinode_integration_tests_lib::mock_blockchain_client_server::MBCSBuilder; use multinode_integration_tests_lib::utils::{ - config_dao, node_chain_specific_data_directory, open_all_file_permissions, receivable_dao, - UrlHolder, + config_dao, receivable_dao, UrlHolder, }; use node_lib::accountant::db_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; From 6bb13e72cd94def6804f5ed8e07e68f8d9b9bbfb Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Oct 2024 22:37:27 +0200 Subject: [PATCH 204/250] GH-711-review-one: tests in verify_bill_payments are in a fine condition and can be left as done --- multinode_integration_tests/src/blockchain.rs | 23 +- .../src/masq_node_cluster.rs | 12 +- .../src/masq_real_node.rs | 2 +- .../src/neighborhood_constructor.rs | 4 +- .../tests/blockchain_interaction_test.rs | 25 +- .../tests/verify_bill_payment.rs | 39 ++- .../tests/verify_bill_payment_utils/utils.rs | 228 ++++++++---------- 7 files changed, 168 insertions(+), 165 deletions(-) diff --git a/multinode_integration_tests/src/blockchain.rs b/multinode_integration_tests/src/blockchain.rs index aecb91e22..fbbc55665 100644 --- a/multinode_integration_tests/src/blockchain.rs +++ b/multinode_integration_tests/src/blockchain.rs @@ -6,26 +6,31 @@ use crate::utils::UrlHolder; use node_lib::test_utils; use std::net::{IpAddr, Ipv4Addr}; -pub struct BlockchainServer<'a> { - pub name: &'a str, +pub struct BlockchainServer { + pub name: String, } -impl<'a> UrlHolder for BlockchainServer<'a> { +impl UrlHolder for BlockchainServer { fn url(&self) -> String { format!("http://{}:18545", self.ip().unwrap().trim()) } } -impl<'a> BlockchainServer<'a> { +impl BlockchainServer { + pub fn new(name: &str) -> Self { + Self { + name: name.to_string(), + } + } pub fn start(&self) { - MASQNodeUtils::clean_up_existing_container(self.name); + MASQNodeUtils::clean_up_existing_container(&self.name); let ip_addr = IpAddr::V4(Ipv4Addr::new(172, 18, 1, 250)); let ip_addr_string = ip_addr.to_string(); let args = vec![ "run", "--detach", "--name", - self.name, + &self.name, "--ip", ip_addr_string.as_str(), "-p", @@ -43,7 +48,7 @@ impl<'a> BlockchainServer<'a> { "inspect", "-f", "{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}", - self.name, + &self.name, ]; let mut command = Command::new("docker", Command::strings(args)); command.stdout_or_stderr() @@ -58,8 +63,8 @@ impl<'a> BlockchainServer<'a> { } } -impl<'a> Drop for BlockchainServer<'a> { +impl Drop for BlockchainServer { fn drop(&mut self) { - MASQNodeUtils::stop(self.name); + MASQNodeUtils::stop(&self.name); } } diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index 8faa266ca..8bf6892e4 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -6,7 +6,7 @@ use crate::masq_mock_node::{ }; use crate::masq_node::{MASQNode, MASQNodeUtils}; use crate::masq_real_node::NodeStartupConfig; -use crate::masq_real_node::{MASQRealNode, NodeID}; +use crate::masq_real_node::{MASQRealNode, PreparedNodeInfo}; use crate::utils::{node_chain_specific_data_directory, open_all_file_permissions}; use masq_lib::blockchains::chains::Chain; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; @@ -23,7 +23,7 @@ pub struct MASQNodeCluster { mock_nodes: HashMap, host_node_parent_dir: Option, next_index: usize, - pub chain: Chain, + chain: Chain, } impl MASQNodeCluster { @@ -52,7 +52,7 @@ impl MASQNodeCluster { self.next_index } - pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> NodeID { + pub fn prepare_real_node(&mut self, config: &NodeStartupConfig) -> PreparedNodeInfo { let index = self.startup_configs.len() + 1; let name = MASQRealNode::make_name(index); self.next_index = index + 1; @@ -62,7 +62,7 @@ impl MASQNodeCluster { let db_path: PathBuf = node_chain_specific_data_directory(&name).into(); open_all_file_permissions(&db_path); - NodeID { + PreparedNodeInfo { node_docker_name: name, index, db_path, @@ -199,6 +199,10 @@ impl MASQNodeCluster { ) } + pub fn chain(&self) -> Chain { + self.chain + } + pub fn is_in_jenkins() -> bool { match env::var("HOST_NODE_PARENT_DIR") { Ok(ref value) if value.is_empty() => false, diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index bf9d5e329..767d50e1b 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -1222,7 +1222,7 @@ impl MASQRealNode { } #[derive(Debug)] -pub struct NodeID { +pub struct PreparedNodeInfo { pub node_docker_name: String, pub index: usize, pub db_path: PathBuf, diff --git a/multinode_integration_tests/src/neighborhood_constructor.rs b/multinode_integration_tests/src/neighborhood_constructor.rs index 3ab6158f5..907314802 100644 --- a/multinode_integration_tests/src/neighborhood_constructor.rs +++ b/multinode_integration_tests/src/neighborhood_constructor.rs @@ -70,7 +70,7 @@ where model_db.root().public_key().to_string().as_str(), )) .rate_pack(model_db.root().inner.rate_pack) - .chain(cluster.chain); + .chain(cluster.chain()); let config = modify_config(config_builder); let real_node = cluster.start_real_node(config); let (mock_node_map, adjacent_mock_node_keys) = @@ -203,7 +203,7 @@ fn form_mock_node_skeleton( let standard_gossip = StandardBuilder::new() .add_masq_node(&node, 1) .half_neighbors(node.main_public_key(), real_node.main_public_key()) - .chain_id(cluster.chain) + .chain_id(cluster.chain()) .build(); node.transmit_multinode_gossip(real_node, &standard_gossip) .unwrap(); diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 6d79f5575..101617282 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -17,9 +17,7 @@ use multinode_integration_tests_lib::masq_real_node::{ ConsumingWalletInfo, NodeStartupConfigBuilder, }; use multinode_integration_tests_lib::mock_blockchain_client_server::MBCSBuilder; -use multinode_integration_tests_lib::utils::{ - config_dao, receivable_dao, UrlHolder, -}; +use multinode_integration_tests_lib::utils::{config_dao, receivable_dao, UrlHolder}; use node_lib::accountant::db_access_objects::utils::CustomQuery; use node_lib::sub_lib::wallet::Wallet; @@ -69,15 +67,15 @@ fn debtors_are_credited_once_but_not_twice() { .blockchain_service_url(&blockchain_client_server.url()) .ui_port(ui_port) .build(); - let node_namings = cluster.prepare_real_node(&node_config); + let (docker_id, _) = cluster.prepare_real_node(&node_config); { - let config_dao = config_dao(&node_namings.node_docker_name); + let config_dao = config_dao(&docker_id.node_docker_name); config_dao .set("start_block", Some("1000".to_string())) .unwrap(); } { - let receivable_dao = receivable_dao(&node_namings.node_docker_name); + let receivable_dao = receivable_dao(&docker_id.node_docker_name); receivable_dao .more_money_receivable( SystemTime::UNIX_EPOCH.add(Duration::from_secs(15_000_000)), @@ -88,7 +86,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the receivable DAO to verify that the receivable's balance has been initialized { - let receivable_dao = receivable_dao(&node_namings.node_docker_name); + let receivable_dao = receivable_dao(&docker_id.node_docker_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -103,17 +101,14 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been set to 1000 { - let config_dao = config_dao(&node_namings.node_docker_name); + let config_dao = config_dao(&docker_id.node_docker_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "1000" ); } - let node = cluster.start_named_real_node( - &node_namings.node_docker_name, - node_namings.index, - node_config, - ); + let node = + cluster.start_named_real_node(&docker_id.node_docker_name, docker_id.index, node_config); let ui_client = node.make_ui(ui_port); // Command a scan log ui_client.send_request( @@ -129,7 +124,7 @@ fn debtors_are_credited_once_but_not_twice() { node.kill_node(); // Use the receivable DAO to verify that the receivable's balance has been adjusted { - let receivable_dao = receivable_dao(&node_namings.node_docker_name); + let receivable_dao = receivable_dao(&docker_id.node_docker_name); let receivable_accounts = receivable_dao .custom_query(CustomQuery::RangeQuery { min_age_s: 0, @@ -144,7 +139,7 @@ fn debtors_are_credited_once_but_not_twice() { } // Use the config DAO to verify that the start block has been advanced to 2001 { - let config_dao = config_dao(&node_namings.node_docker_name); + let config_dao = config_dao(&docker_id.node_docker_name); assert_eq!( config_dao.get("start_block").unwrap().value_opt.unwrap(), "2001" diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 363e167f3..a250b8ed3 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -4,6 +4,7 @@ use crate::verify_bill_payment_utils::utils::{ test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, NodeProfile, Ports, TestInputsBuilder, WholesomeConfig, }; +use itertools::Itertools; use masq_lib::blockchains::chains::Chain; use masq_lib::messages::FromMessageBody; use masq_lib::messages::ToMessageBody; @@ -16,8 +17,9 @@ use multinode_integration_tests_lib::masq_real_node::{MASQRealNode, NodeStartupC use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::{compute_gas_limit, web3_gas_limit_const_part}; use std::convert::TryFrom; -use std::time::{Duration, UNIX_EPOCH}; +use std::time::Duration; use std::u128; + mod verify_bill_payment_utils; #[test] @@ -44,15 +46,17 @@ fn full_payments_were_processed_for_sufficient_balances() { let owed_to_serving_node_2_minor = debt_threshold_wei + 456_789; let owed_to_serving_node_3_minor = debt_threshold_wei + 789_012; let consuming_node_initial_service_fee_balance_minor = debt_threshold_wei * 4; - let long_ago = UNIX_EPOCH.elapsed().unwrap().as_secs(); let test_inputs = TestInputsBuilder::default() .consuming_node_initial_service_fee_balance_minor( consuming_node_initial_service_fee_balance_minor, ) - .debts_config(DebtsSpecs::new( - Debt::new(owed_to_serving_node_1_minor, long_ago), - Debt::new(owed_to_serving_node_2_minor, long_ago), - Debt::new(owed_to_serving_node_3_minor, long_ago), + .debts_config(set_old_debts( + [ + owed_to_serving_node_1_minor, + owed_to_serving_node_2_minor, + owed_to_serving_node_3_minor, + ], + &payment_thresholds, )) .payment_thresholds_all_nodes(payment_thresholds) .build(); @@ -101,10 +105,10 @@ fn activating_serving_nodes_for_test_with_sufficient_funds( .serving_nodes .iter() .map(|attributes| { - let namings = &attributes.common.node_id; + let node_id = &attributes.common.prepared_node; cluster.start_named_real_node( - &namings.node_docker_name, - namings.index, + &node_id.node_docker_name, + node_id.index, attributes .common .startup_config_opt @@ -131,6 +135,19 @@ fn activating_serving_nodes_for_test_with_sufficient_funds( serving_nodes.try_into().unwrap() } +fn set_old_debts( + owed_money_to_serving_nodes: [u128; 3], + payment_thresholds: &PaymentThresholds, +) -> DebtsSpecs { + let quite_long_ago = + payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec + 1; + let debts = owed_money_to_serving_nodes + .into_iter() + .map(|balance_minor| Debt::new(balance_minor, quite_long_ago)) + .collect_vec(); + DebtsSpecs::new(debts[0], debts[1], debts[2]) +} + #[test] fn payments_were_adjusted_due_to_insufficient_balances() { let payment_thresholds = PaymentThresholds { @@ -264,8 +281,8 @@ fn activating_serving_nodes_for_test_with_insufficient_funds( .unwrap(); let common = &serving_node_attributes.common; let serving_node = cluster.start_named_real_node( - &common.node_id.node_docker_name, - common.node_id.index, + &common.prepared_node.node_docker_name, + common.prepared_node.index, node_config, ); let ui_port = serving_node_attributes diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index c99e970b5..0d73364d6 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -8,10 +8,10 @@ use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; use multinode_integration_tests_lib::blockchain::BlockchainServer; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ - ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeID, NodeStartupConfig, - NodeStartupConfigBuilder, + ConsumingWalletInfo, EarningWalletInfo, MASQRealNode, NodeStartupConfig, + NodeStartupConfigBuilder, PreparedNodeInfo, }; -use multinode_integration_tests_lib::utils::UrlHolder; +use multinode_integration_tests_lib::utils::{open_all_file_permissions, UrlHolder}; use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal}; use node_lib::accountant::db_access_objects::receivable_dao::{ReceivableDao, ReceivableDaoReal}; use node_lib::accountant::gwei_to_wei; @@ -33,12 +33,12 @@ use node_lib::test_utils; use node_lib::test_utils::standard_dir_for_test_input_data; use rustc_hex::{FromHex, ToHex}; use std::cell::RefCell; -use std::collections::VecDeque; use std::fs::File; use std::io::Read; use std::path::Path; use std::thread; use std::time::{Duration, Instant, SystemTime}; +use itertools::Itertools; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; @@ -55,27 +55,22 @@ pub fn test_body( stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, ) { - let (mut cluster, global_values) = establish_test_frame(test_inputs); - let consuming_node_attributes = global_values.prepare_consuming_node( - &mut cluster, - &global_values.blockchain_params.blockchain_interfaces, - ); + // It's important to prevent the blockchain server handle being dropped too early + let (mut cluster, global_values, _blockchain_server) = establish_test_frame(test_inputs); + let consuming_node = + global_values.prepare_consuming_node(&mut cluster, &global_values.blockchain_interfaces); let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); - global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node_attributes); - global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node_attributes); - let wholesome_config = WholesomeConfig::new( - global_values, - consuming_node_attributes, - serving_nodes_array, - ); + global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node); + global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node); + let wholesome_config = WholesomeConfig::new(global_values, consuming_node, serving_nodes_array); wholesome_config.assert_expected_wallet_addresses(); let real_consuming_node = cluster.start_named_real_node( &wholesome_config .consuming_node .common - .node_id + .prepared_node .node_docker_name, - wholesome_config.consuming_node.common.node_id.index, + wholesome_config.consuming_node.common.prepared_node.index, wholesome_config .consuming_node .common @@ -170,9 +165,10 @@ impl TestInputsBuilder { .expect("You forgot providing a mandatory input: debts config") .debts .to_vec(); - let mut ui_ports = Self::resolve_ports(self.ui_ports_opt).to_vec(); + let (consuming_node_ui_port_opt, serving_nodes_ui_ports_opt) = Self::resolve_ports(self.ui_ports_opt); + let mut serving_nodes_ui_ports_opt = serving_nodes_ui_ports_opt.to_vec(); let consuming_node = ConsumingNodeProfile { - ui_port_opt: ui_ports.remove(0), + ui_port_opt: consuming_node_ui_port_opt, gas_price_opt: self.consuming_node_gas_price_opt, initial_tx_fee_balance_minor_opt: self.consuming_node_initial_tx_fee_balance_minor_opt, initial_service_fee_balance_minor: self @@ -185,11 +181,14 @@ impl TestInputsBuilder { ServingNodeByName::ServingNode3, ] .into_iter() - .map(|serving_node_by_name| ServingNodeProfile { + .map(|serving_node_by_name|{ + let debt = debts.remove(0); + let ui_port_opt = serving_nodes_ui_ports_opt.remove(0); + ServingNodeProfile { serving_node_by_name, - debt: debts.remove(0), - ui_port_opt: ui_ports.remove(0), - }) + debt, + ui_port_opt, + }}) .collect::>(); let node_profiles = NodeProfiles { consuming_node, @@ -204,17 +203,12 @@ impl TestInputsBuilder { } } - fn resolve_ports(ui_ports_opt: Option) -> [Option; 4] { + fn resolve_ports(ui_ports_opt: Option) -> (Option, [Option; 3]) { match ui_ports_opt { Some(ui_ports) => { - let mut serialized = VecDeque::new(); - serialized.push_front(ui_ports.consuming_node); - serialized.extend(ui_ports.serving_nodes); - let array: [Option; 4] = core::array::from_fn(|_| serialized.pop_front()); - if array.iter().any(|item| item.is_none()) { - panic!("UI ports are expected for each Node, but at least one isn't populated") - } - array + let mut ui_ports_as_opt = ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); + let serving_nodes_array: [Option; 3] = core::array::from_fn(|_| ui_ports_as_opt.remove(0)); + (Some(ui_ports.consuming_node), serving_nodes_array) } None => Default::default(), } @@ -253,14 +247,14 @@ pub struct FinalServiceFeeBalancesByServingNodes { impl FinalServiceFeeBalancesByServingNodes { pub fn new(node_1: u128, node_2: u128, node_3: u128) -> Self { + let balances = [node_1, node_2, node_3]; Self { - balances: [node_1, node_2, node_3], + balances, } } } pub struct BlockchainParams { - blockchain_interfaces: BlockchainInterfaces, chain: Chain, server_url: String, contract_owner_addr: Address, @@ -269,7 +263,7 @@ pub struct BlockchainParams { } struct BlockchainInterfaces { - blockchain_interface: Box, + standard_blockchain_interface: Box, web3: Web3, } @@ -277,12 +271,13 @@ pub struct GlobalValues { pub test_inputs: TestInputs, pub blockchain_params: BlockchainParams, pub now_in_common: SystemTime, + blockchain_interfaces: BlockchainInterfaces, } pub struct WholesomeConfig { pub global_values: GlobalValues, - pub consuming_node: ConsumingNodeAttributes, - pub serving_nodes: [ServingNodeAttributes; 3], + pub consuming_node: ConsumingNode, + pub serving_nodes: [ServingNode; 3], } pub struct DebtsSpecs { @@ -291,9 +286,8 @@ pub struct DebtsSpecs { impl DebtsSpecs { pub fn new(node_1: Debt, node_2: Debt, node_3: Debt) -> Self { - Self { - debts: [node_1, node_2, node_3], - } + let debts = [node_1, node_2, node_3]; + Self { debts } } } @@ -379,15 +373,15 @@ impl NodeProfile for ServingNodeProfile { } } -pub fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, GlobalValues) { +pub fn establish_test_frame( + test_inputs: TestInputs, +) -> (MASQNodeCluster, GlobalValues, BlockchainServer) { let now = SystemTime::now(); let cluster = match MASQNodeCluster::start() { Ok(cluster) => cluster, Err(e) => panic!("{}", e), }; - let blockchain_server = BlockchainServer { - name: "ganache-cli", - }; + let blockchain_server = BlockchainServer::new("ganache-cli"); blockchain_server.start(); blockchain_server.wait_until_ready(); let server_url = blockchain_server.url().to_string(); @@ -397,38 +391,37 @@ pub fn establish_test_frame(test_inputs: TestInputs) -> (MASQNodeCluster, Global let seed = make_seed(); let (contract_owner_wallet, _) = make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); - let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, cluster.chain); - let blockchain_interface = Box::new(BlockchainInterfaceWeb3::new( - http, - event_loop_handle, - cluster.chain, - )); + let chain = cluster.chain(); + let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, chain); + let blockchain_interface = + Box::new(BlockchainInterfaceWeb3::new(http, event_loop_handle, chain)); let blockchain_params = BlockchainParams { - blockchain_interfaces: BlockchainInterfaces { - blockchain_interface, - web3, - }, - chain: cluster.chain, + chain, server_url, contract_owner_addr, contract_owner_wallet, seed, }; + let blockchain_interfaces = BlockchainInterfaces { + standard_blockchain_interface: blockchain_interface, + web3, + }; let global_values = GlobalValues { test_inputs, blockchain_params, + blockchain_interfaces, now_in_common: now, }; assert_eq!( contract_owner_addr, - cluster.chain.rec().contract, + chain.rec().contract, "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ Resulted contact addr {:?} doesn't much what's expected: {:?}", contract_owner_addr, - cluster.chain.rec().contract + chain.rec().contract ); - (cluster, global_values) + (cluster, global_values, blockchain_server) } fn make_seed() -> Seed { @@ -533,13 +526,11 @@ fn primitive_sign_transaction( tx: TransactionParameters, signing_wallet: &Wallet, ) -> SignedTransaction { + let secret = &signing_wallet + .prepare_secp256k1_secret() + .expect("wallet without secret"); web3.accounts() - .sign_transaction( - tx, - &signing_wallet - .prepare_secp256k1_secret() - .expect("wallet without secret"), - ) + .sign_transaction(tx, secret) .wait() .expect("transaction preparation failed") } @@ -679,7 +670,7 @@ impl GlobalValues { &self, cluster: &mut MASQNodeCluster, blockchain_interfaces: &BlockchainInterfaces, - ) -> ConsumingNodeAttributes { + ) -> ConsumingNode { let consuming_node_profile = self.test_inputs.node_profiles.consuming_node.clone(); let initial_service_fee_balance_minor = consuming_node_profile.initial_service_fee_balance_minor; @@ -707,29 +698,27 @@ impl GlobalValues { assert_balances( &consuming_node_wallet, - blockchain_interfaces.blockchain_interface.as_ref(), + blockchain_interfaces.standard_blockchain_interface.as_ref(), initial_transaction_fee_balance, initial_service_fee_balance_minor, ); - let consuming_node_namings = cluster.prepare_real_node(&consuming_node_config); + let prepared_node = cluster.prepare_real_node(&consuming_node_config); let consuming_node_connection = DbInitializerReal::default() - .initialize( - Path::new(&consuming_node_namings.db_path), - make_db_init_config(cluster.chain), - ) + .initialize(&prepared_node.db_path, make_db_init_config(cluster.chain())) .unwrap(); let consuming_node_payable_dao = PayableDaoReal::new(consuming_node_connection); - ConsumingNodeAttributes::new( + open_all_file_permissions(&prepared_node.db_path); + ConsumingNode::new( consuming_node_profile, - consuming_node_namings, + prepared_node, consuming_node_config, consuming_node_wallet, consuming_node_payable_dao, ) } - fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNodeAttributes; 3] { + fn prepare_serving_nodes(&self, cluster: &mut MASQNodeCluster) -> [ServingNode; 3] { self.test_inputs .node_profiles .serving_nodes @@ -738,17 +727,18 @@ impl GlobalValues { .map(|serving_node_profile: ServingNodeProfile| { let (serving_node_config, serving_node_earning_wallet) = self.get_node_config_and_wallet(&serving_node_profile); - let serving_node_namings = cluster.prepare_real_node(&serving_node_config); + let prepared_node_info = cluster.prepare_real_node(&serving_node_config); let serving_node_connection = DbInitializerReal::default() .initialize( - &serving_node_namings.db_path, - make_db_init_config(cluster.chain), + &prepared_node_info.db_path, + make_db_init_config(cluster.chain()), ) .unwrap(); let serving_node_receivable_dao = ReceivableDaoReal::new(serving_node_connection); - ServingNodeAttributes::new( + open_all_file_permissions(&prepared_node_info.db_path); + ServingNode::new( serving_node_profile, - serving_node_namings, + prepared_node_info, serving_node_config, serving_node_earning_wallet, serving_node_receivable_dao, @@ -771,55 +761,50 @@ impl GlobalValues { fn set_up_serving_nodes_databases( &self, - serving_nodes_matrix: &[ServingNodeAttributes; 3], - consuming_node_attributes: &ConsumingNodeAttributes, + serving_nodes_array: &[ServingNode; 3], + consuming_node: &ConsumingNode, ) { let now = self.now_in_common; - serving_nodes_matrix.iter().for_each(|node_attributes| { - let (balance, timestamp) = node_attributes.debt_balance_and_timestamp(now); - node_attributes + serving_nodes_array.iter().for_each(|serving_node| { + let (balance, timestamp) = serving_node.debt_balance_and_timestamp(now); + serving_node .receivable_dao - .more_money_receivable( - timestamp, - &consuming_node_attributes.consuming_wallet, - balance, - ) + .more_money_receivable(timestamp, &consuming_node.consuming_wallet, balance) .unwrap(); assert_balances( - &node_attributes.earning_wallet, - self.blockchain_params - .blockchain_interfaces - .blockchain_interface + &serving_node.earning_wallet, + self.blockchain_interfaces + .standard_blockchain_interface .as_ref(), 0, 0, ); - Self::set_start_block_to_zero(&node_attributes.common.node_id.db_path) + Self::set_start_block_to_zero(&serving_node.common.prepared_node.db_path) }) } fn set_up_consuming_node_db( &self, - serving_nodes_array: &[ServingNodeAttributes; 3], - consuming_node_attributes: &ConsumingNodeAttributes, + serving_nodes_array: &[ServingNode; 3], + consuming_node: &ConsumingNode, ) { let now = self.now_in_common; - serving_nodes_array.iter().for_each(|node_attributes| { - let (balance, timestamp) = node_attributes.debt_balance_and_timestamp(now); - consuming_node_attributes + serving_nodes_array.iter().for_each(|serving_node| { + let (balance, timestamp) = serving_node.debt_balance_and_timestamp(now); + consuming_node .payable_dao - .more_money_payable(timestamp, &node_attributes.earning_wallet, balance) + .more_money_payable(timestamp, &serving_node.earning_wallet, balance) .unwrap(); }); - Self::set_start_block_to_zero(&consuming_node_attributes.common.node_id.db_path) + Self::set_start_block_to_zero(&consuming_node.common.prepared_node.db_path) } } impl WholesomeConfig { fn new( global_values: GlobalValues, - consuming_node: ConsumingNodeAttributes, - serving_nodes: [ServingNodeAttributes; 3], + consuming_node: ConsumingNode, + serving_nodes: [ServingNode; 3], ) -> Self { WholesomeConfig { global_values, @@ -843,15 +828,13 @@ impl WholesomeConfig { ] .iter() .zip(self.serving_nodes.iter()) - .for_each(|(expected_wallet_addr, serving_node_attributes)| { - let serving_node_actual = serving_node_attributes.earning_wallet.to_string(); + .for_each(|(expected_wallet_addr, serving_node)| { + let serving_node_actual = serving_node.earning_wallet.to_string(); assert_eq!( &serving_node_actual, expected_wallet_addr, "{:?} wallet {} mismatched with expected {}", - serving_node_attributes - .serving_node_profile - .serving_node_by_name, + serving_node.serving_node_profile.serving_node_by_name, serving_node_actual, expected_wallet_addr ); @@ -861,9 +844,8 @@ impl WholesomeConfig { fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { let blockchain_interface = self .global_values - .blockchain_params .blockchain_interfaces - .blockchain_interface + .standard_blockchain_interface .as_ref(); assert_balances( &self.consuming_node.consuming_wallet, @@ -946,21 +928,21 @@ impl Ports { #[derive(Debug)] pub struct NodeAttributesCommon { - pub node_id: NodeID, + pub prepared_node: PreparedNodeInfo, pub startup_config_opt: RefCell>, } impl NodeAttributesCommon { - fn new(node_id: NodeID, config: NodeStartupConfig) -> Self { + fn new(prepared_node: PreparedNodeInfo, config: NodeStartupConfig) -> Self { NodeAttributesCommon { - node_id, + prepared_node, startup_config_opt: RefCell::new(Some(config)), } } } #[derive(Debug)] -pub struct ConsumingNodeAttributes { +pub struct ConsumingNode { pub node_profile: ConsumingNodeProfile, pub common: NodeAttributesCommon, pub consuming_wallet: Wallet, @@ -968,29 +950,29 @@ pub struct ConsumingNodeAttributes { } #[derive(Debug)] -pub struct ServingNodeAttributes { +pub struct ServingNode { pub serving_node_profile: ServingNodeProfile, pub common: NodeAttributesCommon, pub earning_wallet: Wallet, pub receivable_dao: ReceivableDaoReal, } -impl ServingNodeAttributes { +impl ServingNode { fn debt_balance_and_timestamp(&self, now: SystemTime) -> (u128, SystemTime) { let debt_specs = self.serving_node_profile.debt_specs(); (debt_specs.balance_minor, debt_specs.proper_timestamp(now)) } } -impl ConsumingNodeAttributes { +impl ConsumingNode { fn new( node_profile: ConsumingNodeProfile, - node_id: NodeID, + prepared_node: PreparedNodeInfo, config: NodeStartupConfig, consuming_wallet: Wallet, payable_dao: PayableDaoReal, ) -> Self { - let common = NodeAttributesCommon::new(node_id, config); + let common = NodeAttributesCommon::new(prepared_node, config); Self { node_profile, common, @@ -1000,15 +982,15 @@ impl ConsumingNodeAttributes { } } -impl ServingNodeAttributes { +impl ServingNode { fn new( serving_node_profile: ServingNodeProfile, - node_id: NodeID, + prepared_node: PreparedNodeInfo, config: NodeStartupConfig, earning_wallet: Wallet, receivable_dao: ReceivableDaoReal, ) -> Self { - let common = NodeAttributesCommon::new(node_id, config); + let common = NodeAttributesCommon::new(prepared_node, config); Self { serving_node_profile, common, From 788c1f528e5d69550d8f4d30e4661f1b2dde438f Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 5 Oct 2024 23:02:02 +0200 Subject: [PATCH 205/250] GH-711-review-one: mid-scan space more ergonomical by code --- masq_lib/src/test_utils/utils.rs | 1 - .../tests/verify_bill_payment_utils/utils.rs | 26 ++--- .../src/accountant/db_access_objects/utils.rs | 11 ++- node/src/accountant/mod.rs | 99 +++++++++++-------- .../payment_adjuster/non_unit_tests/mod.rs | 4 +- .../payable_scanner/agent_null.rs | 4 +- .../payable_scanner/mod.rs | 2 + .../payable_scanner/test_utils.rs | 8 +- node/src/accountant/scanners/mod.rs | 11 ++- node/src/accountant/test_utils.rs | 4 + .../blockchain_interface_web3/mod.rs | 12 ++- node/src/test_utils/mod.rs | 2 +- 12 files changed, 110 insertions(+), 74 deletions(-) diff --git a/masq_lib/src/test_utils/utils.rs b/masq_lib/src/test_utils/utils.rs index 2fed96981..07908280f 100644 --- a/masq_lib/src/test_utils/utils.rs +++ b/masq_lib/src/test_utils/utils.rs @@ -76,7 +76,6 @@ pub fn to_millis(dur: &Duration) -> u64 { (dur.as_secs() * 1000) + (u64::from(dur.subsec_nanos()) / 1_000_000) } -#[cfg(not(feature = "no_test_share"))] pub struct MutexIncrementInset(pub usize); #[cfg(test)] diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index 0d73364d6..18eee8ae7 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -2,6 +2,7 @@ use bip39::{Language, Mnemonic, Seed}; use futures::Future; +use itertools::Itertools; use lazy_static::lazy_static; use masq_lib::blockchains::chains::Chain; use masq_lib::utils::{derivation_path, NeighborhoodModeLight}; @@ -38,7 +39,6 @@ use std::io::Read; use std::path::Path; use std::thread; use std::time::{Duration, Instant, SystemTime}; -use itertools::Itertools; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; @@ -165,7 +165,8 @@ impl TestInputsBuilder { .expect("You forgot providing a mandatory input: debts config") .debts .to_vec(); - let (consuming_node_ui_port_opt, serving_nodes_ui_ports_opt) = Self::resolve_ports(self.ui_ports_opt); + let (consuming_node_ui_port_opt, serving_nodes_ui_ports_opt) = + Self::resolve_ports(self.ui_ports_opt); let mut serving_nodes_ui_ports_opt = serving_nodes_ui_ports_opt.to_vec(); let consuming_node = ConsumingNodeProfile { ui_port_opt: consuming_node_ui_port_opt, @@ -181,14 +182,15 @@ impl TestInputsBuilder { ServingNodeByName::ServingNode3, ] .into_iter() - .map(|serving_node_by_name|{ + .map(|serving_node_by_name| { let debt = debts.remove(0); let ui_port_opt = serving_nodes_ui_ports_opt.remove(0); ServingNodeProfile { - serving_node_by_name, - debt, - ui_port_opt, - }}) + serving_node_by_name, + debt, + ui_port_opt, + } + }) .collect::>(); let node_profiles = NodeProfiles { consuming_node, @@ -206,8 +208,10 @@ impl TestInputsBuilder { fn resolve_ports(ui_ports_opt: Option) -> (Option, [Option; 3]) { match ui_ports_opt { Some(ui_ports) => { - let mut ui_ports_as_opt = ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); - let serving_nodes_array: [Option; 3] = core::array::from_fn(|_| ui_ports_as_opt.remove(0)); + let mut ui_ports_as_opt = + ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); + let serving_nodes_array: [Option; 3] = + core::array::from_fn(|_| ui_ports_as_opt.remove(0)); (Some(ui_ports.consuming_node), serving_nodes_array) } None => Default::default(), @@ -248,9 +252,7 @@ pub struct FinalServiceFeeBalancesByServingNodes { impl FinalServiceFeeBalancesByServingNodes { pub fn new(node_1: u128, node_2: u128, node_3: u128) -> Self { let balances = [node_1, node_2, node_3]; - Self { - balances, - } + Self { balances } } } diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index fcc48becf..03ff00504 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -384,12 +384,13 @@ impl ThresholdUtils { and the denominator must be less than or equal to 10^9. These restrictions do not seem overly strict for having .permanent_debt_allowed greater - than or equal to .debt_threshold_gwei would not make any sense and setting - .threshold_interval_sec over 10^9 would mean stretching out for debts across more than - 31 years. + than or equal to .debt_threshold_gwei would be silly (this is because the former one defines + the absolutely lowest point of the threshold curves) and setting .threshold_interval_sec + to more than 10^9 seconds would mean the user would allow for debts stretching out into 31 + years of age. - If payment_thresholds are ever configurable by the user, these validations should be done - on the values before they are accepted. + As long as the thresholds are configurable in a set, a validation should always be done on + some of these values before they are loaded in. */ (gwei_to_wei::(payment_thresholds.permanent_debt_allowed_gwei) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 205bd8680..fd87b72dc 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -139,12 +139,12 @@ pub struct QualifiedPayableAccount { impl QualifiedPayableAccount { pub fn new( - qualified_as: PayableAccount, + bare_account: PayableAccount, payment_threshold_intercept_minor: u128, creditor_thresholds: CreditorThresholds, ) -> QualifiedPayableAccount { Self { - bare_account: qualified_as, + bare_account, payment_threshold_intercept_minor, creditor_thresholds, } @@ -265,7 +265,7 @@ impl Handler for Accountant { msg: BlockchainAgentWithContextMessage, _ctx: &mut Self::Context, ) -> Self::Result { - self.handle_payable_payment_setup(msg) + self.send_outbound_payments_instructions(msg) } } @@ -703,48 +703,61 @@ impl Accountant { }) } - fn handle_payable_payment_setup(&mut self, msg: BlockchainAgentWithContextMessage) { + fn send_outbound_payments_instructions(&mut self, msg: BlockchainAgentWithContextMessage) { let response_skeleton_opt = msg.response_skeleton_opt; - let blockchain_bridge_instructions_opt = match self + if let Some(blockchain_bridge_instructions) = self.try_composing_instructions(msg) { + self.outbound_payments_instructions_sub_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(blockchain_bridge_instructions) + .expect("BlockchainBridge is dead") + } else { + self.handle_obstruction(response_skeleton_opt) + } + } + + fn try_composing_instructions( + &mut self, + msg: BlockchainAgentWithContextMessage, + ) -> Option { + let analysed_without_an_error = match self .scanners .payable .try_skipping_payment_adjustment(msg, &self.logger) { - Some(Either::Left(complete_msg)) => Some(complete_msg), - Some(Either::Right(unaccepted_msg)) => { - //TODO we will eventually query info from Neighborhood before the adjustment, according to GH-699 + Some(analysed) => analysed, + None => return None, + }; + + match analysed_without_an_error { + Either::Left(prepared_msg_with_unadjusted_payables) => { + Some(prepared_msg_with_unadjusted_payables) + } + Either::Right(adjustment_order) => { + //TODO we will eventually query info from Neighborhood before the adjustment, + // according to GH-699, but probably with asynchronous messages that will be + // more in favour after GH-676 self.scanners .payable - .perform_payment_adjustment(unaccepted_msg, &self.logger) + .perform_payment_adjustment(adjustment_order, &self.logger) } - None => None, - }; + } + } + + fn handle_obstruction(&mut self, response_skeleton_opt: Option) { + self.scanners + .payable + .scan_canceled_by_payment_instructor(&self.logger); - match blockchain_bridge_instructions_opt { - Some(blockchain_bridge_instructions) => self - .outbound_payments_instructions_sub_opt + if let Some(response_skeleton) = response_skeleton_opt { + self.ui_message_sub_opt .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(blockchain_bridge_instructions) - .expect("BlockchainBridge is dead"), - None => { - error!( - self.logger, - "Payable scanner could not finish. If matured payables stay untreated long, your \ - creditors may impose a ban on you" - ); - self.scanners.payable.mark_as_ended(&self.logger); - if let Some(response_skeleton) = response_skeleton_opt { - self.ui_message_sub_opt - .as_ref() - .expect("UI gateway unbound") - .try_send(NodeToUiMessage { - target: MessageTarget::ClientId(response_skeleton.client_id), - body: UiScanResponse {}.tmb(response_skeleton.context_id), - }) - .expect("UI gateway is dead") - } - } + .expect("UI gateway unbound") + .try_send(NodeToUiMessage { + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), + }) + .expect("UI gateway is dead") } } @@ -1760,7 +1773,7 @@ mod tests { log_handler.exists_log_containing(&format!( "WARN: {test_name}: Insolvency detected led to an analysis of feasibility for making \ payments adjustment, however, giving no satisfactory solution. Please be advised that \ - your balances can cover neither reasonable portion of any of those payables recently \ + your balances can cover neither reasonable portion of those payables recently \ qualified for an imminent payment. You must add more funds into your consuming wallet \ in order to stay off delinquency bans that your creditors may apply against you \ otherwise. Details: Current transaction fee balance is not enough to pay a single \ @@ -1770,8 +1783,8 @@ mod tests { log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay \ - untreated long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ + If matured payables are not repaid in time, creditors may treat you with a ban" )); } @@ -1799,8 +1812,8 @@ mod tests { log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated \ - long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ + If matured payables are not repaid in time, creditors may treat you with a ban" )); } @@ -1835,13 +1848,13 @@ mod tests { None, ); - subject.handle_payable_payment_setup(msg); + subject.send_outbound_payments_instructions(msg); // Test didn't blow up while the subject was unbound to other actors // therefore we didn't attempt to send the NodeUiMessage TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner could not finish. If matured payables stay untreated \ - long, your creditors may impose a ban on you" + "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ + If matured payables are not repaid in time, creditors may treat you with a ban" )); } diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 118e085e4..4b0d405bb 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -24,7 +24,7 @@ use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::convert_collection; use rand; @@ -441,7 +441,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) - .agreed_transaction_fee_margin_result(Percentage::new(15)) + .agreed_transaction_fee_margin_result(PurePercentage::try_from(15).unwrap()) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 30856ad3e..d9f23e876 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -85,7 +85,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; - use masq_lib::percentage::Percentage; + use masq_lib::percentage::PurePercentage; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use web3::types::U256; @@ -184,7 +184,7 @@ mod tests { let result = subject.agreed_transaction_fee_margin(); - assert_eq!(result, Percentage::new(0)); + assert_eq!(result, PurePercentage::try_from(0).unwrap()); assert_error_log(test_name, "agreed_transaction_fee_margin") } diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index 506a94bee..cfa265d3a 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -36,6 +36,8 @@ pub trait SolvencySensitivePaymentInstructor { setup: PreparedAdjustment, logger: &Logger, ) -> Option; + + fn scan_canceled_by_payment_instructor(&mut self, logger: &Logger); } pub struct PreparedAdjustment { diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index 6bb667c0f..dc6cc49d4 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -7,7 +7,7 @@ use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; use ethereum_types::U256; -use masq_lib::percentage::Percentage; +use masq_lib::percentage::PurePercentage; use std::cell::RefCell; #[derive(Default)] @@ -16,7 +16,7 @@ pub struct BlockchainAgentMock { transaction_fee_balance_minor_results: RefCell>, service_fee_balance_minor_results: RefCell>, agreed_fee_per_computation_unit_results: RefCell>, - agreed_transaction_fee_margin: RefCell>, + agreed_transaction_fee_margin: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, arbitrary_id_stamp_opt: Option, @@ -47,7 +47,7 @@ impl BlockchainAgent for BlockchainAgentMock { .remove(0) } - fn agreed_transaction_fee_margin(&self) -> Percentage { + fn agreed_transaction_fee_margin(&self) -> PurePercentage { self.agreed_transaction_fee_margin.borrow_mut().remove(0) } @@ -95,7 +95,7 @@ impl BlockchainAgentMock { self } - pub fn agreed_transaction_fee_margin_result(self, result: Percentage) -> Self { + pub fn agreed_transaction_fee_margin_result(self, result: PurePercentage) -> Self { self.agreed_transaction_fee_margin.borrow_mut().push(result); self } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index f722e1911..78173a744 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -304,7 +304,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { logger, "Insolvency detected led to an analysis of feasibility for making payments \ adjustment, however, giving no satisfactory solution. Please be advised that \ - your balances can cover neither reasonable portion of any of those payables \ + your balances can cover neither reasonable portion of those payables \ recently qualified for an imminent payment. You must add more funds into your \ consuming wallet in order to stay off delinquency bans that your creditors may \ apply against you otherwise. Details: {}.", @@ -341,6 +341,15 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { } } } + + fn scan_canceled_by_payment_instructor(&mut self, logger: &Logger) { + error!( + logger, + "Payable scanner is blocked from preparing instructions for payments. If matured payables \ + are not repaid in time, creditors may treat you with a ban" + ); + self.mark_as_ended(logger) + } } impl MultistagePayableScanner for PayableScanner {} diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 92d192eb3..4689cc67c 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1560,6 +1560,10 @@ macro_rules! formal_traits_for_payable_mid_scan_msg_handling { ) -> Option { intentionally_blank!() } + + fn scan_canceled_by_payment_instructor(&mut self, _logger: &Logger) { + intentionally_blank!() + } } }; } 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 823c65cf1..1f390fe95 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -659,7 +659,7 @@ mod tests { }; use crate::sub_lib::blockchain_interface_web3::web3_gas_limit_const_part; use indoc::indoc; - use masq_lib::percentage::Percentage; + use masq_lib::percentage::PurePercentage; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::SystemTime; @@ -701,7 +701,10 @@ mod tests { assert_eq!(CONTRACT_ABI, contract_abi_expected); assert_eq!(TRANSACTION_LITERAL, transaction_literal_expected); assert_eq!(REQUESTS_IN_PARALLEL, 1); - assert_eq!(*TRANSACTION_FEE_MARGIN, Percentage::new(15)); + assert_eq!( + *TRANSACTION_FEE_MARGIN, + PurePercentage::try_from(15).unwrap() + ); } #[test] @@ -1058,7 +1061,10 @@ mod tests { transaction_fee_balance ); assert_eq!(result.service_fee_balance_minor(), masq_balance.as_u128()); - assert_eq!(result.agreed_transaction_fee_margin(), Percentage::new(15)); + assert_eq!( + result.agreed_transaction_fee_margin(), + PurePercentage::try_from(15).unwrap() + ); assert_eq!(result.agreed_fee_per_computation_unit(), 50); assert_eq!( result.estimated_transaction_fee_per_transaction_minor(), diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index fc90f567c..a3b279eca 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -571,7 +571,6 @@ pub mod unshared_test_utils { use std::any::TypeId; use std::cell::RefCell; use std::collections::HashMap; - use std::env::current_dir; use std::num::ParseIntError; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::path::{Path, PathBuf}; @@ -966,6 +965,7 @@ pub mod unshared_test_utils { pub mod arbitrary_id_stamp { use super::*; + use masq_lib::test_utils::utils::MutexIncrementInset; //The issues we are to solve might look as follows: From bb207cdaf4350848544f0bdaf10d48a934cac681 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 5 Oct 2024 23:48:40 +0200 Subject: [PATCH 206/250] GH-711-review-one: a few more comments kncoked off in Accountant --- node/src/accountant/mod.rs | 70 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index fd87b72dc..f22aa30d8 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -172,9 +172,9 @@ pub struct CreditorThresholds { } impl CreditorThresholds { - pub fn new(permanent_debt_allowed_wei: u128) -> Self { + pub fn new(permanent_debt_allowed_minor: u128) -> Self { Self { - permanent_debt_allowed_minor: permanent_debt_allowed_wei, + permanent_debt_allowed_minor, } } } @@ -1490,9 +1490,6 @@ mod tests { #[test] fn received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge( ) { - // the numbers for balances don't do real math, they need not match either the condition for - // the payment adjustment or the actual values that come from the payable size reducing - // algorithm: all that is mocked in this test init_test_logging(); let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; let search_for_indispensable_adjustment_params_arc = Arc::new(Mutex::new(vec![])); @@ -1505,8 +1502,8 @@ mod tests { let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); let qualified_payables = vec![ - QualifiedPayableAccount::new(account_1.clone(), 2345, CreditorThresholds::new(1111)), - QualifiedPayableAccount::new(account_2.clone(), 6789, CreditorThresholds::new(2222)), + {let mut qp= make_non_guaranteed_qualified_payable(1234); qp.bare_account = account_1.clone(); qp}, + {let mut qp= make_non_guaranteed_qualified_payable(6789); qp.bare_account = account_2.clone(); qp} ]; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_params( @@ -1525,11 +1522,9 @@ mod tests { let system = System::new("test"); let expected_agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); - + let protected_qualified_payables = protect_qualified_payables_in_test(qualified_payables.clone()); let msg = BlockchainAgentWithContextMessage { - protected_qualified_payables: protect_qualified_payables_in_test( - qualified_payables.clone(), - ), + protected_qualified_payables, agent: Box::new(agent), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, @@ -1573,9 +1568,7 @@ mod tests { #[test] fn received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge( ) { - // the numbers for balances don't do real math, they need not match either the condition for - // the payment adjustment or the actual values that come from the payable size reducing algorithm; - // all that is mocked in this test + // The numbers in balances, etc. don't do real math, the payment adjuster is mocked init_test_logging(); let test_name = "received_qualified_payables_exceeding_our_masq_balance_are_adjusted_before_forwarded_to_blockchain_bridge"; let adjust_payments_params_arc = Arc::new(Mutex::new(vec![])); @@ -1585,24 +1578,16 @@ mod tests { .start() .recipient(); let mut subject = AccountantBuilder::default().build(); - let unadjusted_account_1 = QualifiedPayableAccount::new( - make_payable_account(111_111), - 1234567, - CreditorThresholds::new(1111111), - ); - let unadjusted_account_2 = QualifiedPayableAccount::new( - make_payable_account(999_999), - 444555666, - CreditorThresholds::new(111111111), - ); - let adjusted_account_1 = PayableAccount { - balance_wei: gwei_to_wei(55_550_u64), - ..unadjusted_account_1.bare_account.clone() - }; - let adjusted_account_2 = PayableAccount { - balance_wei: gwei_to_wei(100_000_u64), - ..unadjusted_account_2.bare_account.clone() + let prepare_unadjusted_and_adjusted_payable = |n: u64| { + let unadjusted_account = make_non_guaranteed_qualified_payable(n); + let adjusted_account = PayableAccount { + balance_wei: gwei_to_wei(n / 3), + ..unadjusted_account.bare_account.clone() + }; + (unadjusted_account, adjusted_account) }; + let (unadjusted_account_1, adjusted_account_1) = prepare_unadjusted_and_adjusted_payable(12345678); + let (unadjusted_account_2, adjusted_account_2) = prepare_unadjusted_and_adjusted_payable(33445566); let response_skeleton = ResponseSkeleton { client_id: 12, context_id: 55, @@ -1690,7 +1675,7 @@ mod tests { assert_eq!(blockchain_bridge_recording.len(), 1); } - fn test_handling_payment_adjuster_error( + fn test_payment_adjuster_error_during_different_stages( test_name: &str, payment_adjuster: PaymentAdjusterMock, ) { @@ -1713,8 +1698,7 @@ mod tests { subject.ui_message_sub_opt = Some(ui_gateway_recipient); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); - let scan_started_at = SystemTime::now(); - subject.scanners.payable.mark_as_started(scan_started_at); + subject.scanners.payable.mark_as_started(SystemTime::now()); let subject_addr = subject.start(); let account = make_payable_account(111_111); let qualified_payable = @@ -1752,22 +1736,22 @@ mod tests { } #[test] - fn payment_adjuster_throws_out_an_error_from_the_insolvency_check() { + fn payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check() { init_test_logging(); - let test_name = "payment_adjuster_throws_out_an_error_from_the_insolvency_check"; + let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 1, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: 60 * 55_000, + per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), }), service_fee_opt: None, }, )); - test_handling_payment_adjuster_error(test_name, payment_adjuster); + test_payment_adjuster_error_during_different_stages(test_name, payment_adjuster); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( @@ -1777,8 +1761,8 @@ mod tests { qualified for an imminent payment. You must add more funds into your consuming wallet \ in order to stay off delinquency bans that your creditors may apply against you \ otherwise. Details: Current transaction fee balance is not enough to pay a single \ - payment. Number of canceled payments: 1. Transaction fee per payment: 3,300,000 wei, \ - while the wallet contains: 123,000,000,000 wei." + payment. Number of canceled payments: 1. Transaction fee per payment: \ + 3,300,000,000,000,000 wei, while the wallet contains: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); @@ -1789,10 +1773,10 @@ mod tests { } #[test] - fn payment_adjuster_throws_out_an_error_meaning_entry_check_passed_but_adjustment_went_wrong() { + fn payment_adjuster_throws_out_an_error_during_stage_two_adjustment_went_wrong() { init_test_logging(); let test_name = - "payment_adjuster_throws_out_an_error_meaning_entry_check_passed_but_adjustment_went_wrong"; + "payment_adjuster_throws_out_an_error_during_stage_two_adjustment_went_wrong"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Either::Right(AdjustmentAnalysis::new( Adjustment::ByServiceFee, @@ -1800,7 +1784,7 @@ mod tests { )))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); - test_handling_payment_adjuster_error(test_name, payment_adjuster); + test_payment_adjuster_error_during_different_stages(test_name, payment_adjuster); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( From 5cfc84f10daa083e2cc695460df1d059d8990aab Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 6 Oct 2024 22:07:45 +0200 Subject: [PATCH 207/250] GH-711-review-one: accountant/mod.rs finished --- node/src/accountant/mod.rs | 86 +++++++++++-------- .../payment_adjuster/adjustment_runners.rs | 7 +- .../balance_calculator.rs | 4 +- .../disqualification_arbiter.rs | 5 +- .../account_stages_conversions.rs | 6 +- .../miscellaneous/helper_functions.rs | 6 +- node/src/accountant/payment_adjuster/mod.rs | 25 +++--- .../preparatory_analyser/mod.rs | 16 ++-- .../accountant/payment_adjuster/test_utils.rs | 14 +-- node/src/accountant/scanners/mod.rs | 36 ++++---- node/src/accountant/test_utils.rs | 25 ++---- node/src/blockchain/blockchain_bridge.rs | 10 +-- 12 files changed, 119 insertions(+), 121 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f22aa30d8..05687b3ab 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1090,9 +1090,9 @@ mod tests { ForAccountantBody, ForPayableScanner, ForPendingPayableScanner, ForReceivableScanner, }; use crate::accountant::test_utils::{ - bc_from_earning_wallet, bc_from_wallets, make_analyzed_account, - make_guaranteed_qualified_payables, make_non_guaranteed_qualified_payable, - make_payable_account, make_unqualified_and_qualified_payables, BannedDaoFactoryMock, + bc_from_earning_wallet, bc_from_wallets, make_meaningless_analyzed_account, + make_meaningless_qualified_payable, make_payable_account, + make_qualified_and_unqualified_payables, make_qualified_payables, BannedDaoFactoryMock, ConfigDaoFactoryMock, MessageIdGeneratorMock, NullScanner, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PaymentAdjusterMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, ReceivableDaoMock, ScannerMock, @@ -1428,7 +1428,7 @@ mod tests { system.run(); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); let expected_qualified_payables = - make_guaranteed_qualified_payables(vec![payable], &DEFAULT_PAYMENT_THRESHOLDS, now); + make_qualified_payables(vec![payable], &DEFAULT_PAYMENT_THRESHOLDS, now); assert_eq!( blockchain_bridge_recording.get_record::(0), &QualifiedPayablesMessage { @@ -1502,8 +1502,16 @@ mod tests { let account_1 = make_payable_account(44_444); let account_2 = make_payable_account(333_333); let qualified_payables = vec![ - {let mut qp= make_non_guaranteed_qualified_payable(1234); qp.bare_account = account_1.clone(); qp}, - {let mut qp= make_non_guaranteed_qualified_payable(6789); qp.bare_account = account_2.clone(); qp} + { + let mut qp = make_meaningless_qualified_payable(1234); + qp.bare_account = account_1.clone(); + qp + }, + { + let mut qp = make_meaningless_qualified_payable(6789); + qp.bare_account = account_2.clone(); + qp + }, ]; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_params( @@ -1522,7 +1530,8 @@ mod tests { let system = System::new("test"); let expected_agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default().set_arbitrary_id_stamp(expected_agent_id_stamp); - let protected_qualified_payables = protect_qualified_payables_in_test(qualified_payables.clone()); + let protected_qualified_payables = + protect_qualified_payables_in_test(qualified_payables.clone()); let msg = BlockchainAgentWithContextMessage { protected_qualified_payables, agent: Box::new(agent), @@ -1579,15 +1588,17 @@ mod tests { .recipient(); let mut subject = AccountantBuilder::default().build(); let prepare_unadjusted_and_adjusted_payable = |n: u64| { - let unadjusted_account = make_non_guaranteed_qualified_payable(n); - let adjusted_account = PayableAccount { + let unadjusted_account = make_meaningless_qualified_payable(n); + let adjusted_account = PayableAccount { balance_wei: gwei_to_wei(n / 3), ..unadjusted_account.bare_account.clone() }; (unadjusted_account, adjusted_account) }; - let (unadjusted_account_1, adjusted_account_1) = prepare_unadjusted_and_adjusted_payable(12345678); - let (unadjusted_account_2, adjusted_account_2) = prepare_unadjusted_and_adjusted_payable(33445566); + let (unadjusted_account_1, adjusted_account_1) = + prepare_unadjusted_and_adjusted_payable(12345678); + let (unadjusted_account_2, adjusted_account_2) = + prepare_unadjusted_and_adjusted_payable(33445566); let response_skeleton = ResponseSkeleton { client_id: 12, context_id: 55, @@ -1738,7 +1749,8 @@ mod tests { #[test] fn payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check() { init_test_logging(); - let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; + let test_name = + "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Err( PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { @@ -1755,20 +1767,17 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Insolvency detected led to an analysis of feasibility for making \ - payments adjustment, however, giving no satisfactory solution. Please be advised that \ - your balances can cover neither reasonable portion of those payables recently \ - qualified for an imminent payment. You must add more funds into your consuming wallet \ - in order to stay off delinquency bans that your creditors may apply against you \ - otherwise. Details: Current transaction fee balance is not enough to pay a single \ - payment. Number of canceled payments: 1. Transaction fee per payment: \ - 3,300,000,000,000,000 wei, while the wallet contains: 123,000,000,000 wei." + "WARN: {test_name}: Add more funds into your consuming wallet in order to become able \ + to repay already expired liabilities as the creditors would respond by delinquency bans \ + otherwise. Details: Current transaction fee balance is not enough to pay a single payment. \ + Number of canceled payments: 1. Transaction fee per payment: 3,300,000,000,000,000 wei, \ + while the wallet contains: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - If matured payables are not repaid in time, creditors may treat you with a ban" + The cause appears to be in competence of the user." )); } @@ -1780,7 +1789,7 @@ mod tests { let payment_adjuster = PaymentAdjusterMock::default() .search_for_indispensable_adjustment_result(Ok(Either::Right(AdjustmentAnalysis::new( Adjustment::ByServiceFee, - vec![make_analyzed_account(123)], + vec![make_meaningless_analyzed_account(123)], )))) .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); @@ -1788,16 +1797,17 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Payment adjustment has not produced any executable payments. Please \ - add funds into your consuming wallet in order to avoid bans from your creditors. Details: \ - The adjustment algorithm had to eliminate each payable from the recently urged payment due \ - to lack of resources" + "WARN: {test_name}: Payment adjustment has not produced any executable payments. Add \ + more funds into your consuming wallet in order to become able to repay already expired \ + liabilities as the creditors would respond by delinquency bans otherwise. Details: \ + The adjustment algorithm had to eliminate each payable from the recently urged payment \ + due to lack of resources" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - If matured payables are not repaid in time, creditors may treat you with a ban" + The cause appears to be in competence of the user" )); } @@ -1823,7 +1833,7 @@ mod tests { .build(); subject.logger = Logger::new(test_name); subject.scanners.payable = Box::new(payable_scanner); - let qualified_payable = make_non_guaranteed_qualified_payable(111_111); + let qualified_payable = make_meaningless_qualified_payable(111_111); let protected_payables = protect_qualified_payables_in_test(vec![qualified_payable]); let blockchain_agent = BlockchainAgentMock::default(); let msg = BlockchainAgentWithContextMessage::new( @@ -1834,11 +1844,11 @@ mod tests { subject.send_outbound_payments_instructions(msg); - // Test didn't blow up while the subject was unbound to other actors - // therefore we didn't attempt to send the NodeUiMessage + // No NodeUiMessage was sent because there is no `response_skeleton`. It is evident by + // the fact that the test didn't blow up even though UIGateway is unbound TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - If matured payables are not repaid in time, creditors may treat you with a ban" + The cause appears to be in competence of the user" )); } @@ -2041,7 +2051,7 @@ mod tests { let now = SystemTime::now(); let payment_thresholds = PaymentThresholds::default(); let (qualified_payables, _, all_non_pending_payables) = - make_unqualified_and_qualified_payables(now, &payment_thresholds); + make_qualified_and_unqualified_payables(now, &payment_thresholds); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let system = System::new( @@ -2406,7 +2416,7 @@ mod tests { .begin_scan_result(Err(BeginScanError::NothingToProcess)) .begin_scan_result(Ok(QualifiedPayablesMessage { protected_qualified_payables: protect_qualified_payables_in_test(vec![ - make_non_guaranteed_qualified_payable(123), + make_meaningless_qualified_payable(123), ]), response_skeleton_opt: None, })) @@ -2615,7 +2625,7 @@ mod tests { }, ]; let qualified_payables = - make_guaranteed_qualified_payables(payables.clone(), &DEFAULT_PAYMENT_THRESHOLDS, now); + make_qualified_payables(payables.clone(), &DEFAULT_PAYMENT_THRESHOLDS, now); let payable_dao = PayableDaoMock::default().non_pending_payables_result(payables); let (blockchain_bridge, _, blockchain_bridge_recordings_arc) = make_recorder(); let blockchain_bridge = blockchain_bridge @@ -3427,15 +3437,15 @@ mod tests { #[test] fn pending_transaction_is_registered_and_monitored_until_it_gets_confirmed_or_canceled() { init_test_logging(); + let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); let build_blockchain_agent_params = Arc::new(Mutex::new(vec![])); let mark_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - let transactions_confirmed_params_arc = Arc::new(Mutex::new(vec![])); - let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); let return_all_errorless_fingerprints_params_arc = Arc::new(Mutex::new(vec![])); - let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); + let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); let update_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); let mark_failure_params_arc = Arc::new(Mutex::new(vec![])); let delete_record_params_arc = Arc::new(Mutex::new(vec![])); + let transactions_confirmed_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_scan_for_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_scan_for_pending_payable_arc_cloned = notify_later_scan_for_pending_payable_params_arc.clone(); // because it moves into a closure @@ -3514,7 +3524,7 @@ mod tests { last_paid_timestamp: past_payable_timestamp_2, pending_payable_opt: None, }; - let qualified_payables = make_guaranteed_qualified_payables( + let qualified_payables = make_qualified_payables( vec![account_1.clone(), account_2.clone()], &DEFAULT_PAYMENT_THRESHOLDS, now, diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index a02a0cee2..179b47b35 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -84,7 +84,7 @@ mod tests { use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; use crate::accountant::test_utils::{ - make_analyzed_account, make_non_guaranteed_qualified_payable, + make_meaningless_analyzed_account, make_meaningless_qualified_payable, }; use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; @@ -106,7 +106,8 @@ mod tests { } fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { - let mut payable = WeightedPayable::new(make_analyzed_account(111), n as u128 * 1234); + let mut payable = + WeightedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); payable .analyzed_account .qualified_as @@ -215,7 +216,7 @@ mod tests { #[test] fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { let balance = 5_000_000_000; - let mut account = make_non_guaranteed_qualified_payable(111); + let mut account = make_meaningless_qualified_payable(111); account.bare_account.balance_wei = balance; let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index cc37d0d61..928081639 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -35,7 +35,7 @@ mod tests { use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; - use crate::accountant::test_utils::make_analyzed_account; + use crate::accountant::test_utils::make_meaningless_analyzed_account; use std::time::SystemTime; #[test] @@ -54,7 +54,7 @@ mod tests { .into_iter() .enumerate() .map(|(idx, n)| { - let mut basic_analyzed_payable = make_analyzed_account(idx as u64); + let mut basic_analyzed_payable = make_meaningless_analyzed_account(idx as u64); basic_analyzed_payable.qualified_as.bare_account.balance_wei = multiple_by_billion(n); basic_analyzed_payable diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index d26ba4368..018e46b3b 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -209,7 +209,7 @@ mod tests { use crate::accountant::payment_adjuster::test_utils::{ make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, }; - use crate::accountant::test_utils::make_guaranteed_qualified_payables; + use crate::accountant::test_utils::make_qualified_payables; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use masq_lib::logger::Logger; @@ -449,8 +449,7 @@ mod tests { pending_payable_opt: None, }; let accounts = vec![account_1, account_2, account_3, account_4]; - let qualified_payables = - make_guaranteed_qualified_payables(accounts, &payment_thresholds, now); + let qualified_payables = make_qualified_payables(accounts, &payment_thresholds, now); let analyzed_accounts = convert_collection(qualified_payables); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); let payment_adjuster = make_initialized_subject( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index ee27101b7..37ae86c97 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -81,9 +81,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, }; - use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, make_payable_account, - }; + use crate::accountant::test_utils::{make_meaningless_qualified_payable, make_payable_account}; use crate::accountant::AnalyzedPayableAccount; #[test] @@ -106,7 +104,7 @@ mod tests { let garbage_disqualification_limit = 333_333_333; let garbage_weight = 777_777_777; let mut analyzed_account = AnalyzedPayableAccount::new( - make_non_guaranteed_qualified_payable(111), + make_meaningless_qualified_payable(111), garbage_disqualification_limit, ); analyzed_account.qualified_as.bare_account = payable_account; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index b8a3d4c27..fb753ad6e 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -201,7 +201,7 @@ mod tests { zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; - use crate::accountant::test_utils::{make_analyzed_account, make_payable_account}; + use crate::accountant::test_utils::{make_meaningless_analyzed_account, make_payable_account}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; @@ -268,10 +268,10 @@ mod tests { #[test] fn find_largest_exceeding_balance_works() { - let mut account_1 = make_analyzed_account(111); + let mut account_1 = make_meaningless_analyzed_account(111); account_1.qualified_as.bare_account.balance_wei = 5_000_000_000; account_1.qualified_as.payment_threshold_intercept_minor = 2_000_000_000; - let mut account_2 = make_analyzed_account(222); + let mut account_2 = make_meaningless_analyzed_account(222); account_2.qualified_as.bare_account.balance_wei = 4_000_000_000; account_2.qualified_as.payment_threshold_intercept_minor = 800_000_000; let qualified_accounts = &[account_1, account_2]; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 878bd9721..4705cd63d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -563,10 +563,10 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ - make_analyzed_account_by_wallet, make_extreme_payables, make_initialized_subject, - multiple_by_billion, CriterionCalculatorMock, DisqualificationGaugeMock, - ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, - PRESERVED_TEST_PAYMENT_THRESHOLDS, + make_extreme_payables, make_initialized_subject, + make_meaningless_analyzed_account_by_wallet, multiple_by_billion, CriterionCalculatorMock, + DisqualificationGaugeMock, ServiceFeeAdjusterMock, + MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -576,7 +576,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ - make_guaranteed_analyzed_payables, make_guaranteed_qualified_payables, make_payable_account, + make_analyzed_payables, make_payable_account, make_qualified_payables, }; use crate::accountant::{ gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, @@ -1046,12 +1046,12 @@ mod tests { ) { let cw_service_fee_balance_minor = multiple_by_billion(4_200_000); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let mut account_1 = make_analyzed_account_by_wallet("abc"); + let mut account_1 = make_meaningless_analyzed_account_by_wallet("abc"); let balance_1 = multiple_by_billion(3_000_000); let disqualification_limit_1 = multiple_by_billion(2_300_000); account_1.qualified_as.bare_account.balance_wei = balance_1; account_1.disqualification_limit_minor = disqualification_limit_1; - let mut account_2 = make_analyzed_account_by_wallet("def"); + let mut account_2 = make_meaningless_analyzed_account_by_wallet("def"); let wallet_2 = account_2.qualified_as.bare_account.wallet.clone(); let balance_2 = multiple_by_billion(2_500_000); let disqualification_limit_2 = multiple_by_billion(1_800_000); @@ -1193,7 +1193,7 @@ mod tests { }; let payables = vec![account_1, account_2, account_3]; let qualified_payables = - make_guaranteed_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + make_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiple_by_billion(2_000_000_000)) .calculate_result(0) @@ -1287,7 +1287,7 @@ mod tests { }; let payables = vec![account_1, account_2.clone(), account_3.clone()]; let analyzed_accounts = - make_guaranteed_analyzed_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); + make_analyzed_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(0) .calculate_result(multiple_by_billion(50_000_000_000)) @@ -1339,11 +1339,8 @@ mod tests { now, ) }; - let analyzed_payables = make_guaranteed_analyzed_payables( - extreme_payables, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - ); + let analyzed_payables = + make_analyzed_payables(extreme_payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); // In turn, tiny cw balance diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index c7a14b9fb..561f3863b 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -382,7 +382,7 @@ mod tests { }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::test_utils::{ - make_analyzed_account, make_non_guaranteed_qualified_payable, + make_meaningless_analyzed_account, make_meaningless_qualified_payable, }; use crate::accountant::QualifiedPayableAccount; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; @@ -457,9 +457,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); + let mut account_1 = make_meaningless_qualified_payable(111); account_1.bare_account.balance_wei = 1_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(333); + let mut account_2 = make_meaningless_qualified_payable(333); account_2.bare_account.balance_wei = 2_000_000_000; let cw_service_fee_balance = 750_000_001; let disqualification_gauge = DisqualificationGaugeMock::default() @@ -477,9 +477,9 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { - let mut account_1 = make_non_guaranteed_qualified_payable(111); + let mut account_1 = make_meaningless_qualified_payable(111); account_1.bare_account.balance_wei = 2_000_000_000; - let mut account_2 = make_non_guaranteed_qualified_payable(333); + let mut account_2 = make_meaningless_qualified_payable(333); account_2.bare_account.balance_wei = 1_000_000_000; let cw_service_fee_balance = 750_000_000; let disqualification_gauge = DisqualificationGaugeMock::default() @@ -503,13 +503,13 @@ mod tests { ErrorFactory: ServiceFeeSingleTXErrorFactory, Error: Debug + PartialEq, { - let mut account_1 = make_analyzed_account(111); + let mut account_1 = make_meaningless_analyzed_account(111); account_1.qualified_as.bare_account.balance_wei = 2_000_000_000; account_1.disqualification_limit_minor = 1_500_000_000; - let mut account_2 = make_analyzed_account(222); + let mut account_2 = make_meaningless_analyzed_account(222); account_2.qualified_as.bare_account.balance_wei = 1_000_050_000; account_2.disqualification_limit_minor = 1_000_000_101; - let mut account_3 = make_analyzed_account(333); + let mut account_3 = make_meaningless_analyzed_account(333); account_3.qualified_as.bare_account.balance_wei = 1_000_111_111; account_3.disqualification_limit_minor = 1_000_000_222; let cw_service_fee_balance = 1_000_000_100; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index cec292475..e91704991 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -13,7 +13,9 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use crate::accountant::test_utils::{make_analyzed_account, make_non_guaranteed_qualified_payable}; +use crate::accountant::test_utils::{ + make_meaningless_analyzed_account, make_meaningless_qualified_payable, +}; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; @@ -92,7 +94,7 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO }; pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { - let qualified_account = make_non_guaranteed_qualified_payable(n); + let qualified_account = make_meaningless_qualified_payable(n); let proposed_adjusted_balance_minor = (qualified_account.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; @@ -217,16 +219,18 @@ impl ServiceFeeAdjusterMock { pub fn multiple_by_billion(num: u128) -> u128 { num * 10_u128.pow(9) } -pub fn make_analyzed_account_by_wallet(wallet_address_segment: &str) -> AnalyzedPayableAccount { +pub fn make_meaningless_analyzed_account_by_wallet( + wallet_address_segment: &str, +) -> AnalyzedPayableAccount { let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); let wallet = make_wallet(wallet_address_segment); - let mut account = make_analyzed_account(num); + let mut account = make_meaningless_analyzed_account(num); account.qualified_as.bare_account.wallet = wallet; account } pub fn make_weighed_account(n: u64) -> WeightedPayable { - WeightedPayable::new(make_analyzed_account(n), 123456789) + WeightedPayable::new(make_meaningless_analyzed_account(n), 123456789) } // Should stay test only! diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 78173a744..bfcc100a8 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -300,16 +300,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { } Err(e) => { if e.insolvency_detected() { - warning!( - logger, - "Insolvency detected led to an analysis of feasibility for making payments \ - adjustment, however, giving no satisfactory solution. Please be advised that \ - your balances can cover neither reasonable portion of those payables \ - recently qualified for an imminent payment. You must add more funds into your \ - consuming wallet in order to stay off delinquency bans that your creditors may \ - apply against you otherwise. Details: {}.", - e - ) + warning!(logger, "{}. Details: {}.", Self::ADD_MORE_FUNDS_URGE, e) } else { unimplemented!("This situation is not possible yet, but may be in the future") } @@ -333,8 +324,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { Err(e) => { warning!( logger, - "Payment adjustment has not produced any executable payments. Please add funds \ - into your consuming wallet in order to avoid bans from your creditors. Details: {}", + "Payment adjustment has not produced any executable payments. {}. Details: {}", + Self::ADD_MORE_FUNDS_URGE, e ); None @@ -345,8 +336,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { fn scan_canceled_by_payment_instructor(&mut self, logger: &Logger) { error!( logger, - "Payable scanner is blocked from preparing instructions for payments. If matured payables \ - are not repaid in time, creditors may treat you with a ban" + "Payable scanner is blocked from preparing instructions for payments. The cause appears \ + to be in competence of the user." ); self.mark_as_ended(logger) } @@ -471,9 +462,9 @@ impl PayableScanner { fn is_symmetrical( sent_payables_hashes: HashSet, - fingerptint_hashes: HashSet, + fingerprints_hashes: HashSet, ) -> bool { - sent_payables_hashes == fingerptint_hashes + sent_payables_hashes == fingerprints_hashes } fn mark_pending_payable(&self, sent_payments: &[&PendingPayable], logger: &Logger) { @@ -593,6 +584,11 @@ impl PayableScanner { fn expose_payables(&self, obfuscated: Obfuscated) -> Vec { obfuscated.expose_vector() } + + const ADD_MORE_FUNDS_URGE: &'static str = + "Add more funds into your consuming wallet in order to \ + become able to repay already expired liabilities as the creditors would respond by delinquency \ + bans otherwise"; } pub struct PendingPayableScanner { @@ -1156,7 +1152,7 @@ mod tests { }; use crate::accountant::test_utils::{ make_custom_payment_thresholds, make_payable_account, make_pending_payable_fingerprint, - make_receivable_account, make_unqualified_and_qualified_payables, BannedDaoFactoryMock, + make_qualified_and_unqualified_payables, make_receivable_account, BannedDaoFactoryMock, BannedDaoMock, ConfigDaoFactoryMock, PayableDaoFactoryMock, PayableDaoMock, PayableScannerBuilder, PayableThresholdsGaugeMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, PendingPayableScannerBuilder, ReceivableDaoFactoryMock, @@ -1334,7 +1330,7 @@ mod tests { let test_name = "payable_scanner_can_initiate_a_scan"; let now = SystemTime::now(); let (qualified_payable_accounts, _, all_non_pending_payables) = - make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); + make_qualified_and_unqualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() @@ -1367,7 +1363,7 @@ mod tests { fn payable_scanner_throws_error_when_a_scan_is_already_running() { let now = SystemTime::now(); let (_, _, all_non_pending_payables) = - make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); + make_qualified_and_unqualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() @@ -1389,7 +1385,7 @@ mod tests { fn payable_scanner_throws_error_in_case_no_qualified_payable_is_found() { let now = SystemTime::now(); let (_, unqualified_payable_accounts, _) = - make_unqualified_and_qualified_payables(now, &PaymentThresholds::default()); + make_qualified_and_unqualified_payables(now, &PaymentThresholds::default()); let payable_dao = PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); let mut subject = PayableScannerBuilder::new() diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 4689cc67c..00a6b3d9f 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1276,7 +1276,7 @@ pub fn make_pending_payable_fingerprint() -> PendingPayableFingerprint { } } -pub fn make_unqualified_and_qualified_payables( +pub fn make_qualified_and_unqualified_payables( now: SystemTime, payment_thresholds: &PaymentThresholds, ) -> ( @@ -1314,11 +1314,8 @@ pub fn make_unqualified_and_qualified_payables( pending_payable_opt: None, }, ]; - let qualified_payable_accounts = make_guaranteed_qualified_payables( - payable_accounts_to_qualify.clone(), - payment_thresholds, - now, - ); + let qualified_payable_accounts = + make_qualified_payables(payable_accounts_to_qualify.clone(), payment_thresholds, now); let mut all_non_pending_payables = Vec::new(); all_non_pending_payables.extend(payable_accounts_to_qualify); @@ -1739,7 +1736,7 @@ impl ScanSchedulers { } } -pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount { +pub fn make_meaningless_qualified_payable(n: u64) -> QualifiedPayableAccount { // Without guarantee that the generated payable would cross the given thresholds QualifiedPayableAccount::new( make_payable_account(n), @@ -1748,11 +1745,11 @@ pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount ) } -pub fn make_analyzed_account(n: u64) -> AnalyzedPayableAccount { - AnalyzedPayableAccount::new(make_non_guaranteed_qualified_payable(n), 123456789) +pub fn make_meaningless_analyzed_account(n: u64) -> AnalyzedPayableAccount { + AnalyzedPayableAccount::new(make_meaningless_qualified_payable(n), 123456789) } -pub fn make_guaranteed_qualified_payables( +pub fn make_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, now: SystemTime, @@ -1760,16 +1757,12 @@ pub fn make_guaranteed_qualified_payables( try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) } -pub fn make_guaranteed_analyzed_payables( +pub fn make_analyzed_payables( payables: Vec, payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - convert_collection(make_guaranteed_qualified_payables( - payables, - payment_thresholds, - now, - )) + convert_collection(make_qualified_payables(payables, payment_thresholds, now)) } pub fn try_making_guaranteed_qualified_payables( diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 2b71af003..07b8ea50d 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -521,7 +521,7 @@ mod tests { use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::test_utils::{ - make_non_guaranteed_qualified_payable, make_pending_payable_fingerprint, + make_meaningless_qualified_payable, make_pending_payable_fingerprint, }; use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::blockchain_interface::blockchain_interface_null::BlockchainInterfaceNull; @@ -664,8 +664,8 @@ mod tests { let persistent_configuration = PersistentConfigurationMock::default() .set_arbitrary_id_stamp(persistent_config_id_stamp); let qualified_payables = vec![ - make_non_guaranteed_qualified_payable(111), - make_non_guaranteed_qualified_payable(222), + make_meaningless_qualified_payable(111), + make_meaningless_qualified_payable(222), ]; let subject = BlockchainBridge::new( Box::new(blockchain_interface), @@ -739,7 +739,7 @@ mod tests { subject.scan_error_subs_opt = Some(scan_error_recipient); let request = QualifiedPayablesMessage { protected_qualified_payables: protect_qualified_payables_in_test(vec![ - make_non_guaranteed_qualified_payable(1234), + make_meaningless_qualified_payable(1234), ]), response_skeleton_opt: Some(ResponseSkeleton { client_id: 11, @@ -786,7 +786,7 @@ mod tests { ); let request = QualifiedPayablesMessage { protected_qualified_payables: protect_qualified_payables_in_test(vec![ - make_non_guaranteed_qualified_payable(12345), + make_meaningless_qualified_payable(12345), ]), response_skeleton_opt: None, }; From 5561d3d6a84a760203e1f57c9051dd068b85a3fe Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 7 Oct 2024 01:29:52 +0200 Subject: [PATCH 208/250] GH-711-review-one: more comments swept off after work done --- .../balance_calculator.rs | 20 +- node/src/accountant/payment_adjuster/inner.rs | 2 +- .../logging_and_diagnostics/log_functions.rs | 180 ++++++++++-------- node/src/accountant/payment_adjuster/mod.rs | 4 +- 4 files changed, 113 insertions(+), 93 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index 928081639..5e114c5d8 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -59,7 +59,7 @@ mod tests { multiple_by_billion(n); basic_analyzed_payable .qualified_as - .payment_threshold_intercept_minor = (multiple_by_billion(2) / 5) * 3; + .payment_threshold_intercept_minor = multiple_by_billion(2) * (idx as u128 + 1); basic_analyzed_payable }) .collect::>(); @@ -75,18 +75,12 @@ mod tests { }) .collect::>(); - let zipped = analyzed_accounts + let expected_values = vec![4_384_000_000_000, 4_336_000_000_000, 2_216_000_000_000]; + computed_criteria .into_iter() - .zip(computed_criteria.into_iter()); - zipped.into_iter().for_each(|(account, actual_criterion)| { - let expected_criterion = { - let exceeding_balance_on_this_account = - account.qualified_as.bare_account.balance_wei - - account.qualified_as.payment_threshold_intercept_minor; - let diff = largest_exceeding_balance - exceeding_balance_on_this_account; - largest_exceeding_balance + diff - }; - assert_eq!(actual_criterion, expected_criterion) - }) + .zip(expected_values.into_iter()) + .for_each(|(actual_criterion, expected_criterion)| { + assert_eq!(actual_criterion, expected_criterion) + }) } } diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 82ff80c7e..f87942bc3 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -69,7 +69,7 @@ pub struct PaymentAdjusterInnerNull {} impl PaymentAdjusterInnerNull { fn panicking_operation(operation: &str) -> ! { panic!( - "Broken code: Broken code: Called the null implementation of the {} method in PaymentAdjusterInner", + "Broken code: Called the null implementation of the {} method in PaymentAdjusterInner", operation ) } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index a644584be..ede9b19aa 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -8,7 +8,6 @@ use itertools::Itertools; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::logger::Logger; use std::collections::HashMap; -use std::iter::once; use std::ops::Not; use thousands::Separable; use web3::types::U256; @@ -21,72 +20,122 @@ Passed successfully adjustment by transaction fee, then rechecked the service fe applied on the adjusted set, but discovered a shortage of MASQ not to suffice even for a single \ transaction. Operation is aborting."; -const BLANK_SPACE: &str = ""; +const EMPTY_STR: &str = ""; -pub fn format_brief_adjustment_summary( +pub fn accounts_before_and_after_debug( original_account_balances_mapped: HashMap, adjusted_accounts: &[PayableAccount], ) -> String { - fn format_summary_for_included_accounts( - original_account_balances_mapped: &HashMap, - adjusted_accounts: &[PayableAccount], - ) -> String { - adjusted_accounts - .iter() - .sorted_by(|account_a, account_b| { - Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) - }) - .map(|account| { - format!( - "{} {}\n{:^length$} {}", - account.wallet, - original_account_balances_mapped - .get(&account.wallet) - .expectv("initial balance") - .separate_with_commas(), - BLANK_SPACE, - account.balance_wei.separate_with_commas(), - length = WALLET_ADDRESS_LENGTH - ) - }) - .join("\n") - } - fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { - let title = once(format!( - "\n{: String { + format!( + "{:, + adjusted_accounts: &[PayableAccount], +) -> String { + adjusted_accounts + .iter() + .sorted_by(|account_a, account_b| { + // Sorting in descending order + Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) + }) + .map(|account| { + let original_balance = original_account_balances_mapped + .get(&account.wallet) + .expectv(""); + (account, *original_balance) + }) + .map(format_single_included_account) + .join("\n") +} +fn format_single_included_account( + (processed_account, original_balance): (&PayableAccount, u128), +) -> String { + format!( + "{} {}\n{:^length$} {}", + processed_account.wallet, + original_balance.separate_with_commas(), + EMPTY_STR, + processed_account.balance_wei.separate_with_commas(), + length = WALLET_ADDRESS_LENGTH + ) +} + +fn excluded_accounts_title() -> String { + format!( + "{:( + original_account_balances_mapped: &'a HashMap, + adjusted_accounts: &'a [PayableAccount], +) -> Vec<(&'a Wallet, u128)> { let adjusted_accounts_wallets: Vec<&Wallet> = adjusted_accounts .iter() .map(|account| &account.wallet) .collect(); - let excluded: Vec<(&Wallet, u128)> = original_account_balances_mapped.iter().fold( - vec![], - |mut acc, (wallet, original_balance)| { + original_account_balances_mapped + .iter() + .fold(vec![], |mut acc, (wallet, original_balance)| { if !adjusted_accounts_wallets.contains(&wallet) { acc.push((wallet, *original_balance)); } acc - }, - ); - let adjusted_accounts_summary = - format_summary_for_included_accounts(&original_account_balances_mapped, adjusted_accounts); - let excluded_accounts_summary_opt = excluded - .is_empty() - .not() - .then(|| format_summary_for_excluded_accounts(&excluded)); + }) +} + +fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { + excluded + .iter() + .sorted_by(|(_, balance_account_a), (_, balance_account_b)| { + Ord::cmp(&balance_account_b, &balance_account_a) + }) + .map(|(wallet, original_balance)| { + format!("{} {}", wallet, original_balance.separate_with_commas()) + }) + .join("\n") +} + +fn write_title_and_summary(title: &str, summary: &str) -> String { + format!("\n{}\n\n{}", title, summary) +} + +fn concatenate_summaries( + adjusted_accounts_summary: String, + excluded_accounts_summary_opt: Option, +) -> String { vec![ Some(adjusted_accounts_summary), excluded_accounts_summary_opt, @@ -96,29 +145,6 @@ pub fn format_brief_adjustment_summary( .join("\n") } -pub fn accounts_before_and_after_debug( - original_account_balances_mapped: HashMap, - adjusted_accounts: &[PayableAccount], -) -> String { - format!( - "\n\ - {:().unwrap(); assert_eq!( panic_msg, - "Broken code: Broken code: Called the null implementation of \ - the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + "Broken code: Called the null implementation of \ + the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" ) } From 5e397f4a3cf0dc753cf01634d3193a7831a81315 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 25 Oct 2024 00:44:28 +0200 Subject: [PATCH 209/250] GH-711-review-one: another chunk of repairs (logging fns, type conversions, adjustment runners) --- .../payment_adjuster/adjustment_runners.rs | 151 +++++++----------- .../balance_calculator.rs | 2 +- .../disqualification_arbiter.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 30 ++-- .../logging_and_diagnostics/diagnostics.rs | 10 +- .../logging_and_diagnostics/log_functions.rs | 53 ++---- .../account_stages_conversions.rs | 36 +++-- node/src/accountant/payment_adjuster/mod.rs | 81 +++++----- .../preparatory_analyser/mod.rs | 9 +- .../accountant/payment_adjuster/test_utils.rs | 4 +- 10 files changed, 164 insertions(+), 216 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 179b47b35..7fea32c47 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -32,13 +32,10 @@ impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { payment_adjuster: &mut PaymentAdjusterReal, weighted_accounts: Vec, ) -> Self::ReturnType { - match payment_adjuster.inner.transaction_fee_count_limit_opt() { - Some(limit) => { - return payment_adjuster - .begin_with_adjustment_by_transaction_fee(weighted_accounts, limit) - } - None => (), - }; + if let Some(limit) = payment_adjuster.inner.transaction_fee_count_limit_opt() { + return payment_adjuster + .begin_with_adjustment_by_transaction_fee(weighted_accounts, limit); + } Ok(Either::Left( payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts), @@ -86,26 +83,27 @@ mod tests { use crate::accountant::test_utils::{ make_meaningless_analyzed_account, make_meaningless_qualified_payable, }; - use crate::accountant::{AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount}; + use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; + use itertools::Itertools; use std::time::SystemTime; fn initialize_payment_adjuster( now: SystemTime, service_fee_balance: u128, - largest_exceeding_balance_recently_qualified: u128, + max_portion_of_balance_over_threshold_in_qualified_payables: u128, ) -> PaymentAdjusterReal { make_initialized_subject( Some(now), Some(service_fee_balance), None, - Some(largest_exceeding_balance_recently_qualified), + Some(max_portion_of_balance_over_threshold_in_qualified_payables), None, ) } - fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { + fn make_weighted_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { let mut payable = WeightedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); payable @@ -118,129 +116,104 @@ mod tests { fn test_surplus_incurred_after_disqualification_in_previous_iteration( subject: ServiceFeeOnlyAdjustmentRunner, - payable_1: WeightedPayable, - payable_2: WeightedPayable, - cw_service_fee_balance_minor: u128, - expected_proposed_balance_1: u128, - expected_proposed_balance_2: u128, + initial_balance_for_each_account: u128, + untaken_cw_service_fee_balance_minor: u128, ) { // Explanation: The hypothesis is that the previous iteration disqualified an account after // which the remaining means are enough for the other accounts. - // We could assign the accounts all they initially requested but a fairer way to do that - // is to give out only that much up to the disqualification limit of these accounts. Later - // on, the accounts that deserves it more, according to their ordering based on their former - // weights, will split the rest of the means among them (Their weights were higher). + // We could assign the accounts the same as they initially requested but a fairer way to + // do sp is to give out only up to the disqualification limit of these accounts. Later + // on, the accounts that deserves it the most, according to the ordering they gain by their + // weights, will gradually get hold of the rest of the money. let now = SystemTime::now(); + let mut payable_1 = make_weighted_payable(111, initial_balance_for_each_account); + payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; + let mut payable_2 = make_weighted_payable(222, initial_balance_for_each_account); + payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; + let weighted_payables = vec![payable_1, payable_2]; let mut payment_adjuster = - initialize_payment_adjuster(now, cw_service_fee_balance_minor, 12345678); + initialize_payment_adjuster(now, untaken_cw_service_fee_balance_minor, 12345678); - let result = subject.adjust_accounts( - &mut payment_adjuster, - vec![payable_1.clone(), payable_2.clone()], - ); + let result = subject.adjust_accounts(&mut payment_adjuster, weighted_payables.clone()); - assert_eq!( - result, - vec![ - AdjustedAccountBeforeFinalization::new( - payable_1.analyzed_account.qualified_as.bare_account, - payable_1.weight, - expected_proposed_balance_1 - ), + let expected_result = weighted_payables + .into_iter() + .map(|weighted_payable| { AdjustedAccountBeforeFinalization::new( - payable_2.analyzed_account.qualified_as.bare_account, - payable_2.weight, - expected_proposed_balance_2 + weighted_payable.analyzed_account.qualified_as.bare_account, + weighted_payable.weight, + // Here, this is the proposed balance at the moment + weighted_payable + .analyzed_account + .disqualification_limit_minor, ) - ] - ) - } - - fn weighted_payable_setup_for_surplus_test( - n: u64, - initial_balance_minor: u128, - ) -> WeightedPayable { - let mut account = make_weighed_payable(n, initial_balance_minor); - account - .analyzed_account - .qualified_as - .payment_threshold_intercept_minor = 3_000_000_000; - account.analyzed_account.qualified_as.creditor_thresholds = - CreditorThresholds::new(1_000_000_000); - account + }) + .collect_vec(); + assert_eq!(result, expected_result) } #[test] - fn means_equal_requested_money_after_dsq_in_previous_iteration_to_return_capped_accounts() { + fn untaken_cw_balance_equals_full_two_debts_after_loosing_an_account_results_in_constrained_balances( + ) { let subject = ServiceFeeOnlyAdjustmentRunner {}; - let cw_service_fee_balance_minor = 10_000_000_000; - let mut payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); - payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; - let mut payable_2 = weighted_payable_setup_for_surplus_test(222, 5_000_000_000); - payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; - let expected_proposed_balance_1 = 3_444_333_444; - let expected_proposed_balance_2 = 3_555_333_555; + let initial_balance_for_each_account = 5_000_000_000; + let untaken_cw_service_fee_balance_minor = + initial_balance_for_each_account + initial_balance_for_each_account; test_surplus_incurred_after_disqualification_in_previous_iteration( subject, - payable_1, - payable_2, - cw_service_fee_balance_minor, - expected_proposed_balance_1, - expected_proposed_balance_2, + initial_balance_for_each_account, + untaken_cw_service_fee_balance_minor, ) } #[test] - fn means_become_bigger_than_requested_after_dsq_in_previous_iteration_to_return_capped_accounts( + fn untaken_cw_balance_is_more_than_full_two_debts_after_loosing_an_account_results_in_constrained_balances( ) { let subject = ServiceFeeOnlyAdjustmentRunner {}; - let cw_service_fee_balance_minor = 10_000_000_000; - let mut payable_1 = weighted_payable_setup_for_surplus_test(111, 5_000_000_000); - payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; - let mut payable_2 = weighted_payable_setup_for_surplus_test(222, 4_999_999_999); - payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; - let expected_proposed_balance_1 = 3_444_333_444; - let expected_proposed_balance_2 = 3_555_333_555; + let initial_balance_for_each_account = 5_000_000_000; + let untaken_cw_service_fee_balance_minor = + initial_balance_for_each_account + initial_balance_for_each_account + 1; test_surplus_incurred_after_disqualification_in_previous_iteration( subject, - payable_1, - payable_2, - cw_service_fee_balance_minor, - expected_proposed_balance_1, - expected_proposed_balance_2, + initial_balance_for_each_account, + untaken_cw_service_fee_balance_minor, ) } #[test] fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { - let balance = 5_000_000_000; + let common_balance = 5_000_000_000; let mut account = make_meaningless_qualified_payable(111); - account.bare_account.balance_wei = balance; + account.bare_account.balance_wei = common_balance; let wallet_1 = make_wallet("abc"); let wallet_2 = make_wallet("def"); let mut account_1 = account.clone(); account_1.bare_account.wallet = wallet_1.clone(); let mut account_2 = account; account_2.bare_account.wallet = wallet_2.clone(); - let adjustment = Adjustment::TransactionFeeInPriority { + let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { + analyzed_account: AnalyzedPayableAccount::new(account, 3_000_000_000), + weight: 4_000_000_000, + }; + let weighted_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; + // We instruct a performance of adjustment by the transaction fee, as if there were + // two transaction, but we had enough fee just for one. Still, you can see at the end of + // the test that this reduction didn't take place which shows that we used the kind of + // runner which ignore this instruction. + let adjustment_type = Adjustment::TransactionFeeInPriority { affordable_transaction_count: 1, }; - let service_fee_balance_minor = (10 * balance) / 8; + let cw_service_fee_balance_minor = (10 * common_balance) / 8; let mut payment_adjuster = PaymentAdjusterReal::new(); payment_adjuster.initialize_inner( - service_fee_balance_minor, - adjustment, + cw_service_fee_balance_minor, + adjustment_type, 123456789, SystemTime::now(), ); let subject = ServiceFeeOnlyAdjustmentRunner {}; - let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { - analyzed_account: AnalyzedPayableAccount::new(account, 3_000_000_000), - weight: 4_000_000_000, - }; - let weighted_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; let result = subject.adjust_accounts(&mut payment_adjuster, weighted_accounts); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index 5e114c5d8..69c7c81e3 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -13,7 +13,7 @@ impl CriterionCalculator for BalanceCriterionCalculator { account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner, ) -> u128 { - let largest = context.largest_exceeding_balance_recently_qualified(); + let largest = context.max_portion_of_balance_over_threshold_in_qualified_payables(); let this_account = account.bare_account.balance_wei - account.payment_threshold_intercept_minor; diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 018e46b3b..815a569bd 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -120,9 +120,10 @@ impl DisqualificationArbiter { pub struct DisqualificationSuspectedAccount<'account> { pub wallet: &'account Wallet, pub weight: u128, - // The rest is for an INFO log + // The rest is for diagnostics and logging pub proposed_adjusted_balance_minor: u128, pub disqualification_limit_minor: u128, + pub initial_account_balance_minor: u128, } impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> @@ -134,6 +135,7 @@ impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> weight: unconfirmed_account.weighted_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor(), + initial_account_balance_minor: unconfirmed_account.balance_minor(), } } } diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index f87942bc3..ab3d7bd27 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -4,7 +4,7 @@ use std::time::SystemTime; pub trait PaymentAdjusterInner { fn now(&self) -> SystemTime; - fn largest_exceeding_balance_recently_qualified(&self) -> u128; + fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128; fn transaction_fee_count_limit_opt(&self) -> Option; fn original_cw_service_fee_balance_minor(&self) -> u128; fn unallocated_cw_service_fee_balance_minor(&self) -> u128; @@ -14,7 +14,7 @@ pub trait PaymentAdjusterInner { pub struct PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, - largest_exceeding_balance_recently_qualified: u128, + max_portion_of_balance_over_threshold_in_qualified_payables: u128, original_cw_service_fee_balance_minor: u128, unallocated_cw_service_fee_balance_minor: u128, } @@ -24,12 +24,12 @@ impl PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, cw_service_fee_balance_minor: u128, - largest_exceeding_balance_recently_qualified: u128, + max_portion_of_balance_over_threshold_in_qualified_payables: u128, ) -> Self { Self { now, transaction_fee_count_limit_opt, - largest_exceeding_balance_recently_qualified, + max_portion_of_balance_over_threshold_in_qualified_payables, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } @@ -41,8 +41,8 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { self.now } - fn largest_exceeding_balance_recently_qualified(&self) -> u128 { - self.largest_exceeding_balance_recently_qualified + fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128 { + self.max_portion_of_balance_over_threshold_in_qualified_payables } fn transaction_fee_count_limit_opt(&self) -> Option { @@ -80,9 +80,9 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull { PaymentAdjusterInnerNull::panicking_operation("now()") } - fn largest_exceeding_balance_recently_qualified(&self) -> u128 { + fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation( - "largest_exceeding_balance_recently_qualified()", + "max_portion_of_balance_over_threshold_in_qualified_payables()", ) } @@ -114,12 +114,12 @@ mod tests { let now = SystemTime::now(); let transaction_fee_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; - let largest_exceeding_balance_recently_qualified = 44_555_666; + let max_portion_of_balance_over_threshold_in_qualified_payables = 44_555_666; let result = PaymentAdjusterInnerReal::new( now, transaction_fee_count_limit_opt, cw_service_fee_balance, - largest_exceeding_balance_recently_qualified, + max_portion_of_balance_over_threshold_in_qualified_payables, ); assert_eq!(result.now, now); @@ -136,8 +136,8 @@ mod tests { cw_service_fee_balance ); assert_eq!( - result.largest_exceeding_balance_recently_qualified, - largest_exceeding_balance_recently_qualified + result.max_portion_of_balance_over_threshold_in_qualified_payables, + max_portion_of_balance_over_threshold_in_qualified_payables ) } @@ -153,13 +153,13 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the largest_exceeding_balance_recently_qualified() \ + expected = "Broken code: Called the null implementation of the max_portion_of_balance_over_threshold_in_qualified_payables() \ method in PaymentAdjusterInner" )] - fn inner_null_calling_largest_exceeding_balance_recently_qualified() { + fn inner_null_calling_max_portion_of_balance_over_threshold_in_qualified_payables() { let subject = PaymentAdjusterInnerNull::default(); - let _ = subject.largest_exceeding_balance_recently_qualified(); + let _ = subject.max_portion_of_balance_over_threshold_in_qualified_payables(); } #[test] diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index c1a6b272c..38184ad8b 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -3,7 +3,7 @@ use masq_lib::constants::WALLET_ADDRESS_LENGTH; use std::fmt::Debug; -const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false; +const RUN_DIAGNOSTICS_FOR_DEVS: bool = false; pub const DIAGNOSTICS_MIDDLE_COLUMN_WIDTH: usize = 58; @@ -49,7 +49,7 @@ pub fn diagnostics( F1: FnOnce() -> String, F2: FnOnce() -> String, { - if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { + if RUN_DIAGNOSTICS_FOR_DEVS { let subject_column_length = if subject_renderer_opt.is_some() { WALLET_ADDRESS_LENGTH + 2 } else { @@ -82,7 +82,7 @@ pub fn collection_diagnostics( label: &str, accounts: &[DebuggableAccount], ) { - if PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS { + if RUN_DIAGNOSTICS_FOR_DEVS { eprintln!("{}", label); accounts .iter() @@ -212,10 +212,10 @@ pub mod ordinary_diagnostic_functions { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS; + use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::RUN_DIAGNOSTICS_FOR_DEVS; #[test] fn constants_are_correct() { - assert_eq!(PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS, false); + assert_eq!(RUN_DIAGNOSTICS_FOR_DEVS, false); } } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index ede9b19aa..4ea3bc695 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -151,14 +151,10 @@ pub fn info_log_for_disqualified_account( ) { info!( logger, - "Shortage of MASQ in your consuming wallet will impact payable {}, ruled out from this \ - round of payments. The proposed adjustment {} wei was below the disqualification limit \ - {} wei", + "Ready payment to {} was eliminated to spare MASQ for those higher prioritized. {} wei owed \ + at the moment.", account.wallet, - account - .proposed_adjusted_balance_minor - .separate_with_commas(), - account.disqualification_limit_minor.separate_with_commas() + account.initial_account_balance_minor.separate_with_commas(), ) } @@ -169,8 +165,8 @@ pub fn log_adjustment_by_service_fee_is_required( ) { warning!( logger, - "Total of {} wei in MASQ was ordered while the consuming wallet held only {} wei of MASQ \ - token. Adjustment of their count or balances is required.", + "Mature payables amount to {} MASQ wei while the consuming wallet holds only {} wei. \ + Adjustment in their count or balances is necessary.", payables_sum.separate_with_commas(), cw_service_fee_balance.separate_with_commas() ); @@ -186,12 +182,12 @@ pub fn log_insufficient_transaction_fee_balance( ) { warning!( logger, - "Transaction fee balance of {} wei is not going to cover the anticipated fee to send {} \ - transactions, with {} wei required per one. Maximum count is set to {}. Adjustment must be \ - performed.", + "Transaction fee balance of {} wei cannot cover the anticipated {} wei for {} \ + transactions. Maximal count is set to {}. Adjustment must be performed.", transaction_fee_minor.separate_with_commas(), + (cw_required_transactions_count as u128 * txn_fee_required_per_txn_minor) + .separate_with_commas(), cw_required_transactions_count, - txn_fee_required_per_txn_minor.separate_with_commas(), limiting_count ); info!(logger, "{}", REFILL_RECOMMENDATION) @@ -203,14 +199,9 @@ pub fn log_transaction_fee_adjustment_ok_but_by_service_fee_undoable(logger: &Lo #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ - info_log_for_disqualified_account, LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, - REFILL_RECOMMENDATION, + LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY, REFILL_RECOMMENDATION, }; - use crate::test_utils::make_wallet; - use masq_lib::logger::Logger; - use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; #[test] fn constants_are_correct() { @@ -227,28 +218,4 @@ mod tests { MASQ not to suffice even for a single transaction. Operation is aborting." ) } - - #[test] - fn disqualification_log_properly_formatted() { - init_test_logging(); - let test_name = "disqualification_log_properly_formatted"; - let logger = Logger::new(test_name); - let wallet = make_wallet("aaa"); - let disqualified_account = DisqualificationSuspectedAccount { - wallet: &wallet, - weight: 0, - proposed_adjusted_balance_minor: 1_555_666_777, - disqualification_limit_minor: 2_000_000_000, - }; - - info_log_for_disqualified_account(&logger, &disqualified_account); - - TestLogHandler::new().exists_log_containing(&format!( - "INFO: {}: Shortage of MASQ in your consuming wallet will impact payable \ - 0x0000000000000000000000000000000000616161, ruled out from this round of payments. \ - The proposed adjustment 1,555,666,777 wei was below the disqualification limit \ - 2,000,000,000 wei", - test_name - )); - } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 37ae86c97..eb86e4289 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -8,21 +8,23 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; use crate::accountant::QualifiedPayableAccount; -// If passing along without PA just to BlockchainBridge +// Accounts that pass through the checks in PA and dart to BlockchainBridge right away impl From for PayableAccount { fn from(qualified_payable: QualifiedPayableAccount) -> Self { qualified_payable.bare_account } } -// After transaction fee adjustment while no need to go off with the other fee, and so we keep -// the original balance, drop the weights etc. +// Transaction fee adjustment just done, but no need to go off with the other fee, so we only +// extract the original payable accounts of those retained after the adjustment. PA is done and can +// begin to return. impl From for PayableAccount { fn from(weighted_account: WeightedPayable) -> Self { weighted_account.analyzed_account.qualified_as.bare_account } } +// When the consuming balance is being exhausted to zero. This represents the PA's resulted values. impl From for PayableAccount { fn from(non_finalized_adjustment: AdjustedAccountBeforeFinalization) -> Self { let mut account = non_finalized_adjustment.original_account; @@ -31,15 +33,15 @@ impl From for PayableAccount { } } -// Preparing "remaining, unresolved accounts" for another iteration that always begins with -// WeightedPayable types +// Makes "remaining unresolved accounts" ready for another recursion that always begins with +// structures in the type of WeightedPayable impl From for WeightedPayable { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { unconfirmed_adjustment.weighted_account } } -// Used after the unconfirmed adjustment pass through all confirmations +// Used if an unconfirmed adjustment passes the confirmation impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { let proposed_adjusted_balance_minor = @@ -59,9 +61,9 @@ impl From for AdjustedAccountBeforeFinalization { } } -// This is used when we detect that the upcoming iterations begins with a surplus in the remaining -// unallocated CW service fee, and therefore we grant the remaining accounts with the full balance -// they requested +// When we detect that the upcoming iterations will begin with a surplus in the remaining +// unallocated CW service fee, therefore the remaining accounts' balances are automatically granted +// an amount that equals to their disqualification limit (and can be later provided with even more) impl From for AdjustedAccountBeforeFinalization { fn from(weighted_account: WeightedPayable) -> Self { let limited_adjusted_balance = weighted_account.disqualification_limit(); @@ -85,7 +87,7 @@ mod tests { use crate::accountant::AnalyzedPayableAccount; #[test] - fn conversion_between_non_finalized_account_and_payable_account_is_implemented() { + fn conversion_between_non_finalized_account_and_payable_account() { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; let non_finalized_account = AdjustedAccountBeforeFinalization::new( @@ -142,13 +144,19 @@ mod tests { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; let mut weighted_account = prepare_weighted_account(original_payable_account.clone()); - weighted_account.weight = 321654; - let unconfirmed_adjustment = UnconfirmedAdjustment::new(weighted_account, 111_222_333); + let weight = 321654; + weighted_account.weight = weight; + let proposed_adjusted_balance_minor = 111_222_333; + let unconfirmed_adjustment = + UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance_minor); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); - let expected_result = - AdjustedAccountBeforeFinalization::new(original_payable_account, 321654, 111_222_333); + let expected_result = AdjustedAccountBeforeFinalization::new( + original_payable_account, + weight, + proposed_adjusted_balance_minor, + ); assert_eq!(result, expected_result) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c5c05252b..221c5ab23 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -116,13 +116,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment_analysis.adjustment; - let largest_exceeding_balance_recently_qualified = + let max_portion_of_balance_over_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); self.initialize_inner( initial_service_fee_balance_minor, required_adjustment, - largest_exceeding_balance_recently_qualified, + max_portion_of_balance_over_threshold_in_qualified_payables, now, ); @@ -166,7 +166,7 @@ impl PaymentAdjusterReal { &mut self, cw_service_fee_balance: u128, required_adjustment: Adjustment, - largest_exceeding_balance_recently_qualified: u128, + max_portion_of_balance_over_threshold_in_qualified_payables: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { @@ -180,7 +180,7 @@ impl PaymentAdjusterReal { now, transaction_fee_limitation_opt, cw_service_fee_balance, - largest_exceeding_balance_recently_qualified, + max_portion_of_balance_over_threshold_in_qualified_payables, ); self.inner = Box::new(inner); @@ -612,13 +612,13 @@ mod tests { ) { let mut subject = PaymentAdjusterReal::default(); let cw_service_fee_balance = 111_222_333_444; - let largest_exceeding_balance_recently_qualified = 3_555_666; + let max_portion_of_balance_over_threshold_in_qualified_payables = 3_555_666; let now = SystemTime::now(); subject.initialize_inner( cw_service_fee_balance, required_adjustment, - largest_exceeding_balance_recently_qualified, + max_portion_of_balance_over_threshold_in_qualified_payables, now, ); @@ -636,8 +636,10 @@ mod tests { cw_service_fee_balance ); assert_eq!( - subject.inner.largest_exceeding_balance_recently_qualified(), - largest_exceeding_balance_recently_qualified + subject + .inner + .max_portion_of_balance_over_threshold_in_qualified_payables(), + max_portion_of_balance_over_threshold_in_qualified_payables ) } @@ -735,7 +737,7 @@ mod tests { agreed_transaction_fee_per_computed_unit_major: 100, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: 100 * 3 * 55_000 - 1, + cw_transaction_fee_balance_major: (((100 * 3 * 55_000) * 115) / 100) - 1, }), ); let analyzed_payables = convert_collection(qualified_payables.clone()); @@ -753,9 +755,9 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Transaction fee balance of 16,499,999,000,000,000 wei is not \ - going to cover the anticipated fee to send 3 transactions, with 6,325,000,000,000,000 \ - wei required per one. Maximum count is set to 2. Adjustment must be performed." + "WARN: {test_name}: Transaction fee balance of 18,974,999,000,000,000 wei cannot cover \ + the anticipated 18,975,000,000,000,000 wei for 3 transactions. Maximal count is set to 2. \ + Adjustment must be performed." )); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ @@ -793,9 +795,11 @@ mod tests { ))) ); let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing(&format!("WARN: {test_name}: Total of 100,000,\ - 000,001 wei in MASQ was ordered while the consuming wallet held only 100,000,000,000 wei of \ - MASQ token. Adjustment of their count or balances is required.")); + log_handler.exists_log_containing(&format!( + "WARN: {test_name}: Mature payables \ + amount to 100,000,000,001 MASQ wei while the consuming wallet holds only 100,000,000,000 \ + wei. Adjustment in their count or balances is necessary." + )); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ delinquency bans. In order to consume services without limitations, you will need to \ @@ -1132,12 +1136,12 @@ mod tests { wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, ) { - let garbage_largest_exceeding_balance_recently_qualified = 123456789; + let garbage_max_portion_of_balance_over_threshold_in_qualified_payables = 123456789; subject.inner = Box::new(PaymentAdjusterInnerReal::new( SystemTime::now(), None, cw_service_fee_balance_minor, - garbage_largest_exceeding_balance_recently_qualified, + garbage_max_portion_of_balance_over_threshold_in_qualified_payables, )); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); @@ -1247,7 +1251,7 @@ mod tests { let err = match result { Err(e) => e, Ok(ok) => panic!( - "we expected to get an error but it was ok: {:?}", + "we expected to get an error, but it was ok: {:?}", ok.affordable_accounts ), }; @@ -1367,29 +1371,23 @@ mod tests { Err(e) => e, }; assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated); - let expected_log = |wallet: &str, proposed_adjusted_balance_in_this_iteration: u64| { + let expected_log = |wallet: &str| { format!( - "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ - {wallet}, ruled out from this round of payments. The proposed adjustment {} wei was \ - below the disqualification limit {} wei", - proposed_adjusted_balance_in_this_iteration.separate_with_commas(), + "INFO: {test_name}: Ready payment to {wallet} was eliminated to spare MASQ for \ + those higher prioritized. {} wei owed at the moment.", (*MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR).separate_with_commas() ) }; let log_handler = TestLogHandler::new(); - // Notice that the proposals grow as one disqualified account drops out in each iteration - log_handler.exists_log_containing(&expected_log( + [ "0x000000000000000000000000000000626c616830", - 333, - )); - log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616831", - 499, - )); - log_handler.exists_log_containing(&expected_log( "0x000000000000000000000000000000626c616832", - 999, - )); + ] + .into_iter() + .for_each(|address| { + let _ = log_handler.exists_log_containing(&expected_log(address)); + }); } fn meaningless_timestamp() -> SystemTime { @@ -1716,10 +1714,9 @@ mod tests { ); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); TestLogHandler::new().exists_log_containing(&format!( - "INFO: {test_name}: Shortage of MASQ in your consuming wallet will impact payable \ - 0x0000000000000000000000000000000000676869, ruled out from this round of payments. \ - The proposed adjustment 189,999,999,999,999,944 wei was below the disqualification \ - limit 300,000,000,000,000,000 wei" + "INFO: {test_name}: Ready payment to 0x0000000000000000000000000000000000676869 was \ + eliminated to spare MASQ for those higher prioritized. 600,000,000,000,000,000 wei owed \ + at the moment." )); test_inner_was_reset_to_null(subject) } @@ -1852,9 +1849,9 @@ mod tests { ); TestLogHandler::new().assert_logs_contain_in_order(vec![ &format!( - "WARN: {test_name}: Total of 411,000,000,000,000,000,000 wei in MASQ was \ - ordered while the consuming wallet held only 70,999,999,999,999,999,999 wei of MASQ \ - token. Adjustment of their count or balances is required." + "WARN: {test_name}: Mature payables amount to 411,000,000,000,000,000,000 MASQ \ + wei while the consuming wallet holds only 70,999,999,999,999,999,999 wei. \ + Adjustment in their count or balances is necessary." ), &format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to \ @@ -2191,13 +2188,13 @@ mod tests { cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = convert_collection(qualified_payables); - let largest_exceeding_balance_recently_qualified = + let max_portion_of_balance_over_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); let mut subject = make_initialized_subject( Some(now), Some(cw_service_fee_balance_minor), None, - Some(largest_exceeding_balance_recently_qualified), + Some(max_portion_of_balance_over_threshold_in_qualified_payables), None, ); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 561f3863b..6263d4748 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -448,8 +448,8 @@ mod tests { ]; assert_eq!(&determine_limit_params[0..2], expected_params); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet \ - held only {} wei of MASQ token. Adjustment of their count or balances is required.", + "WARN: {test_name}: Mature payables amount to {} MASQ wei while the consuming wallet \ + holds only {} wei. Adjustment in their count or balances is necessary.", total_amount_required.separate_with_commas(), cw_service_fee_balance.separate_with_commas() )); @@ -615,8 +615,9 @@ mod tests { }, ); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: Total of {} wei in MASQ was ordered while the consuming wallet held \ - only {}", service_fee_totally_required_minor.separate_with_commas(), + "WARN: {test_name}: Mature payables amount to {} MASQ wei while the consuming wallet \ + holds only {}", + service_fee_totally_required_minor.separate_with_commas(), (cw_service_fee_balance_minor - 2).separate_with_commas() )); } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index e91704991..b76e97648 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -37,7 +37,7 @@ pub fn make_initialized_subject( now_opt: Option, cw_service_fee_balance_minor_opt: Option, criterion_calculator_mock_opt: Option, - largest_exceeding_balance_recently_qualified: Option, + max_portion_of_balance_over_threshold_in_qualified_payables: Option, logger_opt: Option, ) -> PaymentAdjusterReal { let cw_service_fee_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); @@ -48,7 +48,7 @@ pub fn make_initialized_subject( now_opt.unwrap_or(SystemTime::now()), None, cw_service_fee_balance_minor, - largest_exceeding_balance_recently_qualified.unwrap_or(0), + max_portion_of_balance_over_threshold_in_qualified_payables.unwrap_or(0), )); if let Some(calculator) = criterion_calculator_mock_opt { subject.calculators = vec![Box::new(calculator)] From 719d701dcb5ac84ef83d10809d5827c3cf9b6e32 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 26 Oct 2024 00:28:56 +0200 Subject: [PATCH 210/250] GH-711-review-one: more repairs in data_structures, accounts_abstraction, disqualification_arbiter and helper_functions --- .../payment_adjuster/adjustment_runners.rs | 2 +- .../disqualification_arbiter.rs | 109 +++++++------- .../miscellaneous/data_structures.rs | 37 +++-- .../miscellaneous/helper_functions.rs | 137 +++++++----------- node/src/accountant/payment_adjuster/mod.rs | 16 +- .../payment_adjuster/non_unit_tests/mod.rs | 9 +- .../accounts_abstraction.rs | 12 +- .../preparatory_analyser/mod.rs | 32 ++-- .../payment_adjuster/service_fee_adjuster.rs | 15 +- 9 files changed, 168 insertions(+), 201 deletions(-) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs index 7fea32c47..427a571ad 100644 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ b/node/src/accountant/payment_adjuster/adjustment_runners.rs @@ -54,7 +54,7 @@ impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { weighted_accounts: Vec, ) -> Self::ReturnType { let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.balance_minor() + weighted_account.initial_balance_minor() }); let unallocated_cw_balance = payment_adjuster diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 815a569bd..a29ea4a1e 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -9,7 +9,6 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::Unconfi use crate::accountant::QualifiedPayableAccount; use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; -use std::cmp::Ordering; pub struct DisqualificationArbiter { disqualification_gauge: Box, @@ -101,18 +100,10 @@ impl DisqualificationArbiter { fn find_account_with_smallest_weight<'accounts>( accounts: &'accounts [DisqualificationSuspectedAccount], ) -> &'accounts DisqualificationSuspectedAccount<'accounts> { - let first_account = accounts.first().expect("collection was empty"); - accounts.iter().fold( - first_account, - |with_smallest_weight_so_far, current| match Ord::cmp( - ¤t.weight, - &with_smallest_weight_so_far.weight, - ) { - Ordering::Less => current, - Ordering::Greater => with_smallest_weight_so_far, - Ordering::Equal => with_smallest_weight_so_far, - }, - ) + accounts + .iter() + .min_by_key(|account| account.weight) + .expect("an empty collection of accounts") } } @@ -135,7 +126,7 @@ impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> weight: unconfirmed_account.weighted_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor(), - initial_account_balance_minor: unconfirmed_account.balance_minor(), + initial_account_balance_minor: unconfirmed_account.initial_balance_minor(), } } } @@ -159,41 +150,58 @@ impl DisqualificationGauge for DisqualificationGaugeReal { threshold_intercept_minor: u128, permanent_debt_allowed_minor: u128, ) -> u128 { + // This signs that the debt lies in the horizontal area of the payment thresholds. + // Such are mandatory to be paid in their whole size. if threshold_intercept_minor == permanent_debt_allowed_minor { return account_balance_minor; } - let exceeding_debt_part = account_balance_minor - threshold_intercept_minor; - if DisqualificationGaugeReal::qualifies_for_double_margin( + Self::determine_adequate_minimal_payment( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, - ) { - exceeding_debt_part + 2 * permanent_debt_allowed_minor - } else { - exceeding_debt_part + permanent_debt_allowed_minor - } + ) } } impl DisqualificationGaugeReal { - const FIRST_CONDITION_COEFFICIENT: u128 = 2; - const SECOND_CONDITION_COEFFICIENT: u128 = 2; - fn qualifies_for_double_margin( + const FIRST_QUAL_COND_COEF: u128 = 2; + const SECOND_QUAL_COND_COEF: u128 = 2; + const MULTIPLIER_FOR_THICKER_MARGIN: u128 = 2; + + fn qualifies_for_thicker_margin( account_balance_minor: u128, threshold_intercept_minor: u128, permanent_debt_allowed_minor: u128, ) -> bool { let exceeding_threshold = account_balance_minor - threshold_intercept_minor; let considered_forgiven = threshold_intercept_minor - permanent_debt_allowed_minor; - let minimal_payment_accepted = exceeding_threshold + permanent_debt_allowed_minor; + let minimal_acceptable_payment = exceeding_threshold + permanent_debt_allowed_minor; - let first_condition = - minimal_payment_accepted >= Self::FIRST_CONDITION_COEFFICIENT * considered_forgiven; + let condition_of_debt_fast_growth = + minimal_acceptable_payment >= Self::FIRST_QUAL_COND_COEF * considered_forgiven; - let second_condition = considered_forgiven - >= Self::SECOND_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; + let condition_of_position_on_rather_the_left_half_of_the_slope = + considered_forgiven >= Self::SECOND_QUAL_COND_COEF * permanent_debt_allowed_minor; - first_condition && second_condition + condition_of_debt_fast_growth && condition_of_position_on_rather_the_left_half_of_the_slope + } + + fn determine_adequate_minimal_payment( + account_balance_minor: u128, + threshold_intercept_minor: u128, + permanent_debt_allowed_minor: u128, + ) -> u128 { + let debt_part_over_the_threshold = account_balance_minor - threshold_intercept_minor; + if DisqualificationGaugeReal::qualifies_for_thicker_margin( + account_balance_minor, + threshold_intercept_minor, + permanent_debt_allowed_minor, + ) { + debt_part_over_the_threshold + + Self::MULTIPLIER_FOR_THICKER_MARGIN * permanent_debt_allowed_minor + } else { + debt_part_over_the_threshold + permanent_debt_allowed_minor + } } } @@ -220,17 +228,18 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(DisqualificationGaugeReal::FIRST_CONDITION_COEFFICIENT, 2); - assert_eq!(DisqualificationGaugeReal::SECOND_CONDITION_COEFFICIENT, 2) + assert_eq!(DisqualificationGaugeReal::FIRST_QUAL_COND_COEF, 2); + assert_eq!(DisqualificationGaugeReal::SECOND_QUAL_COND_COEF, 2); + assert_eq!(DisqualificationGaugeReal::MULTIPLIER_FOR_THICKER_MARGIN, 2) } #[test] - fn qualifies_for_double_margin_granted_on_both_conditions_returning_equals() { + fn qualifies_for_thicker_margin_granted_on_both_conditions_returning_equals() { let account_balance_minor = 6_000_000_000; let threshold_intercept_minor = 3_000_000_000; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -240,12 +249,12 @@ mod tests { } #[test] - fn qualifies_for_double_margin_granted_on_first_condition_bigger_second_equal() { + fn qualifies_for_thicker_margin_granted_on_first_condition_bigger_second_equal() { let account_balance_minor = 6_000_000_001; let threshold_intercept_minor = 3_000_000_000; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -255,12 +264,12 @@ mod tests { } #[test] - fn qualifies_for_double_margin_granted_on_first_condition_equal_second_bigger() { + fn qualifies_for_thicker_margin_granted_on_first_condition_equal_second_bigger() { let account_balance_minor = 6_000_000_003; let threshold_intercept_minor = 3_000_000_001; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -270,12 +279,12 @@ mod tests { } #[test] - fn qualifies_for_double_margin_granted_on_both_conditions_returning_bigger() { + fn qualifies_for_thicker_margin_granted_on_both_conditions_returning_bigger() { let account_balance_minor = 6_000_000_004; let threshold_intercept_minor = 3_000_000_001; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -285,12 +294,12 @@ mod tests { } #[test] - fn qualifies_for_double_margin_declined_on_first_condition() { + fn qualifies_for_thicker_margin_declined_on_first_condition() { let account_balance_minor = 5_999_999_999; let threshold_intercept_minor = 3_000_000_000; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -300,12 +309,12 @@ mod tests { } #[test] - fn qualifies_for_double_margin_declined_on_second_condition() { + fn qualifies_for_thicker_margin_declined_on_second_condition() { let account_balance_minor = 6_000_000_000; let threshold_intercept_minor = 2_999_999_999; let permanent_debt_allowed_minor = 1_000_000_000; - let result = DisqualificationGaugeReal::qualifies_for_double_margin( + let result = DisqualificationGaugeReal::qualifies_for_thicker_margin( account_balance_minor, threshold_intercept_minor, permanent_debt_allowed_minor, @@ -417,6 +426,7 @@ mod tests { payment_thresholds.threshold_interval_sec = 10_000; let logger = Logger::new(test_name); let wallet_1 = make_wallet("abc"); + // Meaning we're operating in the horizontal area defined by "permanent debt allowed" let common_timestamp = from_time_t( (payment_thresholds.maturity_threshold_sec + payment_thresholds.threshold_interval_sec @@ -470,8 +480,10 @@ mod tests { .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); unconfirmed_adjustments.iter().for_each(|payable| { - // Condition of disqualification at the horizontal threshold - assert!(payable.proposed_adjusted_balance_minor < 120_000_000_000) + // In the horizontal area, the disqualification limit equals to the entire debt size. + // This check says that every account qualified for disqualification but only one + // will eventually be chosen + assert!(payable.proposed_adjusted_balance_minor < payable.initial_balance_minor()) }); assert_eq!(result, wallet_3); } @@ -489,10 +501,9 @@ mod tests { } fn make_dsq_suspected_accounts( - accounts_and_dsq_edges: &[UnconfirmedAdjustment], + accounts: &[UnconfirmedAdjustment], ) -> Vec { - let with_referred_accounts: Vec<&UnconfirmedAdjustment> = - accounts_and_dsq_edges.iter().collect(); + let with_referred_accounts: Vec<&UnconfirmedAdjustment> = accounts.iter().collect(); convert_collection(with_referred_accounts) } } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 0f3fd705f..6de4591c7 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -23,14 +23,14 @@ impl WeightedPayable { &self.analyzed_account.qualified_as.bare_account.wallet } - pub fn balance_minor(&self) -> u128 { + pub fn initial_balance_minor(&self) -> u128 { self.analyzed_account.qualified_as.bare_account.balance_wei } } #[derive(Debug, PartialEq, Eq)] pub struct AdjustmentIterationResult { - pub decided_accounts: DecidedAccounts, + pub decided_accounts_opt: Option>, pub remaining_undecided_accounts: Vec, } @@ -58,12 +58,6 @@ impl RecursionResults { } } -#[derive(Debug, PartialEq, Eq)] -pub enum DecidedAccounts { - LowGainingAccountEliminated, - SomeAccountsProcessed(Vec), -} - #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { pub original_account: PayableAccount, @@ -103,8 +97,8 @@ impl UnconfirmedAdjustment { self.weighted_account.wallet() } - pub fn balance_minor(&self) -> u128 { - self.weighted_account.balance_minor() + pub fn initial_balance_minor(&self) -> u128 { + self.weighted_account.initial_balance_minor() } pub fn disqualification_limit_minor(&self) -> u128 { @@ -114,14 +108,14 @@ impl UnconfirmedAdjustment { } } -pub struct TransactionCountsBy16bits { +pub struct AffordableAndRequiredTxCounts { pub affordable: u16, pub required: u16, } -impl TransactionCountsBy16bits { +impl AffordableAndRequiredTxCounts { pub fn new(max_possible_tx_count: U256, number_of_accounts: usize) -> Self { - TransactionCountsBy16bits { + AffordableAndRequiredTxCounts { affordable: u16::try_from(max_possible_tx_count).unwrap_or(u16::MAX), required: u16::try_from(number_of_accounts).unwrap_or(u16::MAX), } @@ -131,7 +125,7 @@ impl TransactionCountsBy16bits { #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, RecursionResults, TransactionCountsBy16bits, + AdjustedAccountBeforeFinalization, AffordableAndRequiredTxCounts, RecursionResults, }; use crate::accountant::test_utils::make_payable_account; use ethereum_types::U256; @@ -167,12 +161,14 @@ mod tests { #[test] fn there_is_u16_ceiling_for_possible_tx_count() { let corrections_from_u16_max = [-3_i8, -1, 0, 1, 10]; - let result = corrections_from_u16_max + let prepared_input_numbers = corrections_from_u16_max .into_iter() .map(plus_minus_correction_for_u16_max) - .map(U256::from) + .map(U256::from); + let result = prepared_input_numbers .map(|max_possible_tx_count| { - let detected_tx_counts = TransactionCountsBy16bits::new(max_possible_tx_count, 123); + let detected_tx_counts = + AffordableAndRequiredTxCounts::new(max_possible_tx_count, 123); detected_tx_counts.affordable }) .collect::>(); @@ -186,12 +182,13 @@ mod tests { #[test] fn there_is_u16_ceiling_for_required_number_of_accounts() { let corrections_from_u16_max = [-9_i8, -1, 0, 1, 5]; - let result = corrections_from_u16_max + let right_input_numbers = corrections_from_u16_max .into_iter() - .map(plus_minus_correction_for_u16_max) + .map(plus_minus_correction_for_u16_max); + let result = right_input_numbers .map(|required_tx_count_usize| { let detected_tx_counts = - TransactionCountsBy16bits::new(U256::from(123), required_tx_count_usize); + AffordableAndRequiredTxCounts::new(U256::from(123), required_tx_count_usize); detected_tx_counts.required }) .collect::>(); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index fb753ad6e..0d40652ff 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -1,5 +1,6 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use std::iter::Sum; use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ @@ -9,7 +10,7 @@ use crate::accountant::payment_adjuster::miscellaneous::data_structures::{Adjust use crate::accountant::{AnalyzedPayableAccount}; use itertools::{Either, Itertools}; -pub fn zero_affordable_accounts_found( +pub fn no_affordable_accounts_found( accounts: &Either, Vec>, ) -> bool { match accounts { @@ -20,23 +21,17 @@ pub fn zero_affordable_accounts_found( pub fn sum_as(collection: &[T], arranger: F) -> N where - N: From, - F: Fn(&T) -> u128, + N: Sum, + F: Fn(&T) -> N, { - collection.iter().map(arranger).sum::().into() -} - -pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 { - sum_as(weights_and_accounts, |weighted_account| { - weighted_account.weight - }) + collection.iter().map(arranger).sum::() } pub fn dump_unaffordable_accounts_by_transaction_fee( weighted_accounts: Vec, affordable_transaction_count: u16, ) -> Vec { - let sorted_accounts = sort_in_descendant_order_by_weights(weighted_accounts); + let sorted_accounts = sort_in_descending_order_by_weights(weighted_accounts); diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", @@ -54,7 +49,7 @@ pub fn dump_unaffordable_accounts_by_transaction_fee( .collect() } -fn sort_in_descendant_order_by_weights(unsorted: Vec) -> Vec { +fn sort_in_descending_order_by_weights(unsorted: Vec) -> Vec { unsorted .into_iter() .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) @@ -79,19 +74,7 @@ pub fn find_largest_exceeding_balance(qualified_accounts: &[AnalyzedPayableAccou .expect("should be: balance > intercept!") }) .collect::>(); - find_largest_u128(&diffs) -} - -fn find_largest_u128(slice: &[u128]) -> u128 { - slice - .iter() - .fold(0, |largest_so_far, num| largest_so_far.max(*num)) -} - -pub fn find_smallest_u128(slice: &[u128]) -> u128 { - slice - .iter() - .fold(u128::MAX, |smallest_so_far, num| smallest_so_far.min(*num)) + *diffs.iter().max().expect("No account found") } pub fn exhaust_cw_balance_entirely( @@ -102,7 +85,7 @@ pub fn exhaust_cw_balance_entirely( account_info.proposed_adjusted_balance_minor }); - let cw_reminder = original_cw_service_fee_balance_minor + let cw_remaining = original_cw_service_fee_balance_minor .checked_sub(adjusted_balances_total) .unwrap_or_else(|| { panic!( @@ -111,7 +94,7 @@ pub fn exhaust_cw_balance_entirely( ) }); - let init = ConsumingWalletExhaustingStatus::new(cw_reminder); + let init = ConsumingWalletExhaustingStatus::new(cw_remaining); approved_accounts .into_iter() .sorted_by(|info_a, info_b| Ord::cmp(&info_b.weight, &info_a.weight)) @@ -126,7 +109,7 @@ fn run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances( status: ConsumingWalletExhaustingStatus, non_finalized_account: AdjustedAccountBeforeFinalization, ) -> ConsumingWalletExhaustingStatus { - if status.remainder != 0 { + if !status.is_cw_exhausted_to_0() { let balance_gap_minor = non_finalized_account .original_account .balance_wei @@ -139,15 +122,20 @@ fn run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances( non_finalized_account.original_account.balance_wei ) }); - let possible_extra_addition = if balance_gap_minor < status.remainder { + let possible_extra_addition = if balance_gap_minor < status.remaining_cw_balance { balance_gap_minor } else { - status.remainder + status.remaining_cw_balance }; exhausting_cw_balance_diagnostics(&non_finalized_account, possible_extra_addition); - status.handle_balance_update_and_add(non_finalized_account, possible_extra_addition) + let updated_non_finalized_account = ConsumingWalletExhaustingStatus::update_account_balance( + non_finalized_account, + possible_extra_addition, + ); + let updated_status = status.reduce_cw_balance_remaining(possible_extra_addition); + updated_status.add(updated_non_finalized_account) } else { not_exhausting_cw_balance_diagnostics(&non_finalized_account); @@ -156,32 +144,36 @@ fn run_cw_exhausting_on_possibly_sub_optimal_adjusted_balances( } struct ConsumingWalletExhaustingStatus { - remainder: u128, + remaining_cw_balance: u128, accounts_finalized_so_far: Vec, } impl ConsumingWalletExhaustingStatus { - fn new(remainder: u128) -> Self { + fn new(remaining_cw_balance: u128) -> Self { Self { - remainder, + remaining_cw_balance, accounts_finalized_so_far: vec![], } } - fn handle_balance_update_and_add( - mut self, - mut non_finalized_account_info: AdjustedAccountBeforeFinalization, - possible_extra_addition: u128, - ) -> Self { - let corrected_adjusted_account_before_finalization = { - non_finalized_account_info.proposed_adjusted_balance_minor += possible_extra_addition; - non_finalized_account_info - }; - self.remainder = self - .remainder - .checked_sub(possible_extra_addition) + fn is_cw_exhausted_to_0(&self) -> bool { + self.remaining_cw_balance == 0 + } + + fn reduce_cw_balance_remaining(mut self, subtrahend: u128) -> Self { + self.remaining_cw_balance = self + .remaining_cw_balance + .checked_sub(subtrahend) .expect("we hit zero"); - self.add(corrected_adjusted_account_before_finalization) + self + } + + fn update_account_balance( + mut non_finalized_account: AdjustedAccountBeforeFinalization, + addition: u128, + ) -> AdjustedAccountBeforeFinalization { + non_finalized_account.proposed_adjusted_balance_minor += addition; + non_finalized_account } fn add(mut self, non_finalized_account_info: AdjustedAccountBeforeFinalization) -> Self { @@ -197,8 +189,8 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, - find_largest_exceeding_balance, find_largest_u128, find_smallest_u128, - zero_affordable_accounts_found, ConsumingWalletExhaustingStatus, + find_largest_exceeding_balance, no_affordable_accounts_found, + ConsumingWalletExhaustingStatus, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; use crate::accountant::test_utils::{make_meaningless_analyzed_account, make_payable_account}; @@ -208,15 +200,15 @@ mod tests { use std::time::SystemTime; #[test] - fn found_zero_affordable_accounts_found_returns_true_for_non_finalized_accounts() { - let result = zero_affordable_accounts_found(&Either::Left(vec![])); + fn no_affordable_accounts_found_found_returns_true_for_non_finalized_accounts() { + let result = no_affordable_accounts_found(&Either::Left(vec![])); assert_eq!(result, true) } #[test] - fn zero_affordable_accounts_found_returns_false_for_non_finalized_accounts() { - let result = zero_affordable_accounts_found(&Either::Left(vec![ + fn no_affordable_accounts_found_returns_false_for_non_finalized_accounts() { + let result = no_affordable_accounts_found(&Either::Left(vec![ AdjustedAccountBeforeFinalization::new(make_payable_account(456), 5678, 1234), ])); @@ -224,48 +216,19 @@ mod tests { } #[test] - fn found_zero_affordable_accounts_returns_true_for_finalized_accounts() { - let result = zero_affordable_accounts_found(&Either::Right(vec![])); + fn no_affordable_accounts_found_returns_true_for_finalized_accounts() { + let result = no_affordable_accounts_found(&Either::Right(vec![])); assert_eq!(result, true) } #[test] - fn found_zero_affordable_accounts_returns_false_for_finalized_accounts() { - let result = - zero_affordable_accounts_found(&Either::Right(vec![make_payable_account(123)])); + fn no_affordable_accounts_found_returns_false_for_finalized_accounts() { + let result = no_affordable_accounts_found(&Either::Right(vec![make_payable_account(123)])); assert_eq!(result, false) } - #[test] - fn find_largest_u128_begins_with_zero() { - let result = find_largest_u128(&[]); - - assert_eq!(result, 0) - } - - #[test] - fn find_largest_u128_works() { - let result = find_largest_u128(&[45, 2, 456565, 0, 2, 456565, 456564]); - - assert_eq!(result, 456565) - } - - #[test] - fn find_smallest_u128_begins_with_u128_max() { - let result = find_smallest_u128(&[]); - - assert_eq!(result, u128::MAX) - } - - #[test] - fn find_smallest_u128_works() { - let result = find_smallest_u128(&[45, 1112, 456565, 3, 7, 456565, 456564]); - - assert_eq!(result, 3) - } - #[test] fn find_largest_exceeding_balance_works() { let mut account_1 = make_meaningless_analyzed_account(111); @@ -357,7 +320,7 @@ mod tests { let result = ConsumingWalletExhaustingStatus::new(cw_balance_remainder); - assert_eq!(result.remainder, cw_balance_remainder); + assert_eq!(result.remaining_cw_balance, cw_balance_remainder); assert_eq!(result.accounts_finalized_so_far, vec![]) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 221c5ab23..8274c15e0 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -31,14 +31,11 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ - LowGainingAccountEliminated, SomeAccountsProcessed, -}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, find_largest_exceeding_balance, - sum_as, zero_affordable_accounts_found, + sum_as, no_affordable_accounts_found, }; use crate::accountant::payment_adjuster::preparatory_analyser::{LateServiceFeeSingleTxErrorFactory, PreparatoryAnalyzer}; use crate::accountant::payment_adjuster::service_fee_adjuster::{ @@ -200,7 +197,7 @@ impl PaymentAdjusterReal { TransactionAndServiceFeeAdjustmentRunner {}, )?; - if zero_affordable_accounts_found(&processed_accounts) { + if no_affordable_accounts_found(&processed_accounts) { return Err(PaymentAdjusterError::AllAccountsEliminated); } @@ -307,15 +304,15 @@ impl PaymentAdjusterReal { adjustment_iteration_result: AdjustmentIterationResult, ) -> RecursionResults { let remaining_undecided_accounts = adjustment_iteration_result.remaining_undecided_accounts; - let here_decided_accounts = match adjustment_iteration_result.decided_accounts { - LowGainingAccountEliminated => { + let here_decided_accounts = match adjustment_iteration_result.decided_accounts_opt { + None => { if remaining_undecided_accounts.is_empty() { return RecursionResults::new(vec![], vec![]); } vec![] } - SomeAccountsProcessed(decided_accounts) => { + Some(decided_accounts) => { if remaining_undecided_accounts.is_empty() { return RecursionResults::new(decided_accounts, vec![]); } @@ -556,7 +553,6 @@ mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::SomeAccountsProcessed; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, WeightedPayable, }; @@ -2204,7 +2200,7 @@ mod tests { // This is just a sentinel that allows us to shorten the adjustment execution. // We care only for the params captured inside the container from above .perform_adjustment_by_service_fee_result(AdjustmentIterationResult { - decided_accounts: SomeAccountsProcessed(vec![]), + decided_accounts_opt: Some(vec![]), remaining_undecided_accounts: vec![], }); subject.service_fee_adjuster = Box::new(service_fee_adjuster_mock); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 4b0d405bb..fd2e689d4 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -313,7 +313,9 @@ fn try_generating_qualified_payables_and_cw_balance( try_make_qualified_payables_by_applied_thresholds(payables, &thresholds_to_be_used, now); let balance_average = { - let sum: u128 = sum_as(&qualified_payables, |account| account.balance_minor()); + let sum: u128 = sum_as(&qualified_payables, |account| { + account.initial_balance_minor() + }); sum / accounts_count as u128 }; let cw_service_fee_balance_minor = { @@ -322,8 +324,9 @@ fn try_generating_qualified_payables_and_cw_balance( let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; balance_average / multiplier as u128 * number_of_pieces }; - let required_service_fee_total: u128 = - sum_as(&qualified_payables, |account| account.balance_minor()); + let required_service_fee_total: u128 = sum_as(&qualified_payables, |account| { + account.initial_balance_minor() + }); if required_service_fee_total <= cw_service_fee_balance_minor { None } else { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs index fde0c6a7a..9c6ef091e 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs @@ -13,7 +13,7 @@ where } pub trait BalanceProvidingAccount { - fn balance_minor(&self) -> u128; + fn initial_balance_minor(&self) -> u128; } pub trait DisqualificationLimitProvidingAccount { @@ -30,8 +30,8 @@ impl DisqualificationAnalysableAccount for WeightedPayable { } impl BalanceProvidingAccount for WeightedPayable { - fn balance_minor(&self) -> u128 { - self.analyzed_account.balance_minor() + fn initial_balance_minor(&self) -> u128 { + self.analyzed_account.initial_balance_minor() } } @@ -48,8 +48,8 @@ impl DisqualificationLimitProvidingAccount for AnalyzedPayableAccount { } impl BalanceProvidingAccount for AnalyzedPayableAccount { - fn balance_minor(&self) -> u128 { - self.qualified_as.balance_minor() + fn initial_balance_minor(&self) -> u128 { + self.qualified_as.initial_balance_minor() } } @@ -64,7 +64,7 @@ impl DisqualificationAnalysableAccount for QualifiedPaya } impl BalanceProvidingAccount for QualifiedPayableAccount { - fn balance_minor(&self) -> u128 { + fn initial_balance_minor(&self) -> u128 { self.bare_account.balance_wei } } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 6263d4748..f417be55e 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -8,11 +8,9 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - TransactionCountsBy16bits, WeightedPayable, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - find_smallest_u128, sum_as, + AffordableAndRequiredTxCounts, WeightedPayable, }; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; @@ -208,11 +206,11 @@ impl PreparatoryAnalyzer { cw_transaction_fee_balance_minor: U256, txn_fee_required_per_txn_minor: u128, number_of_qualified_accounts: usize, - ) -> TransactionCountsBy16bits { + ) -> AffordableAndRequiredTxCounts { let max_possible_tx_count_u256 = cw_transaction_fee_balance_minor / U256::from(txn_fee_required_per_txn_minor); - TransactionCountsBy16bits::new(max_possible_tx_count_u256, number_of_qualified_accounts) + AffordableAndRequiredTxCounts::new(max_possible_tx_count_u256, number_of_qualified_accounts) } fn check_adjustment_possibility( @@ -264,7 +262,7 @@ impl PreparatoryAnalyzer { where Account: BalanceProvidingAccount, { - sum_as(payables, |account| account.balance_minor()) + sum_as(payables, |account| account.initial_balance_minor()) } fn is_service_fee_adjustment_needed( @@ -292,12 +290,11 @@ impl PreparatoryAnalyzer { where Account: DisqualificationLimitProvidingAccount, { - find_smallest_u128( - &accounts - .iter() - .map(|account| account.disqualification_limit()) - .collect::>(), - ) + accounts + .iter() + .map(|account| account.disqualification_limit()) + .min() + .expect("No account to consider") } } @@ -338,8 +335,9 @@ pub struct LateServiceFeeSingleTxErrorFactory { impl LateServiceFeeSingleTxErrorFactory { pub fn new(unadjusted_accounts: &[WeightedPayable]) -> Self { let original_number_of_accounts = unadjusted_accounts.len(); - let original_service_fee_required_total_minor = - sum_as(unadjusted_accounts, |account| account.balance_minor()); + let original_service_fee_required_total_minor = sum_as(unadjusted_accounts, |account| { + account.initial_balance_minor() + }); Self { original_number_of_accounts, original_service_fee_required_total_minor, @@ -558,7 +556,9 @@ mod tests { make_weighed_account(1011), ]; let original_number_of_accounts = original_accounts.len(); - let initial_sum = sum_as(&original_accounts, |account| account.balance_minor()); + let initial_sum = sum_as(&original_accounts, |account| { + account.initial_balance_minor() + }); let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); let expected_error_preparer = |number_of_accounts, _, cw_service_fee_balance_minor| { PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 5954a224d..85fdd6268 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -1,18 +1,13 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::DecidedAccounts::{ - LowGainingAccountEliminated, SomeAccountsProcessed, -}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: ordinary_diagnostic_functions::{proposed_adjusted_balance_diagnostics}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_mul_coefficient_preventing_fractional_numbers, weights_total, -}; +use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{compute_mul_coefficient_preventing_fractional_numbers, sum_as}; use itertools::Either; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; @@ -104,7 +99,7 @@ impl ServiceFeeAdjusterReal { let pre_processed_decided_accounts: Vec = convert_collection(thriving_competitors); Either::Right(AdjustmentIterationResult { - decided_accounts: SomeAccountsProcessed(pre_processed_decided_accounts), + decided_accounts_opt: Some(pre_processed_decided_accounts), remaining_undecided_accounts, }) } @@ -126,7 +121,7 @@ impl ServiceFeeAdjusterReal { let remaining_reverted = convert_collection(remaining); AdjustmentIterationResult { - decided_accounts: LowGainingAccountEliminated, + decided_accounts_opt: None, remaining_undecided_accounts: remaining_reverted, } } @@ -173,7 +168,9 @@ impl AdjustmentComputer { weighted_accounts: Vec, unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { - let weights_total = weights_total(&weighted_accounts); + let weights_total = sum_as(&weighted_accounts, |weighted_account| { + weighted_account.weight + }); let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; let multiplication_coefficient = From bc15ac938d764d6e65533dffd2ba8c1b21707498 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:50:43 +0100 Subject: [PATCH 211/250] GH-711-review-one: interim commit --- .../miscellaneous/helper_functions.rs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 0d40652ff..5ff7fd329 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -233,15 +233,21 @@ mod tests { fn find_largest_exceeding_balance_works() { let mut account_1 = make_meaningless_analyzed_account(111); account_1.qualified_as.bare_account.balance_wei = 5_000_000_000; - account_1.qualified_as.payment_threshold_intercept_minor = 2_000_000_000; + account_1.qualified_as.payment_threshold_intercept_minor = 2_000_000_001; let mut account_2 = make_meaningless_analyzed_account(222); - account_2.qualified_as.bare_account.balance_wei = 4_000_000_000; - account_2.qualified_as.payment_threshold_intercept_minor = 800_000_000; - let qualified_accounts = &[account_1, account_2]; + account_2.qualified_as.bare_account.balance_wei = 5_000_000_000; + account_2.qualified_as.payment_threshold_intercept_minor = 2_000_000_001; + let mut account_3 = make_meaningless_analyzed_account(333); + account_3.qualified_as.bare_account.balance_wei = 5_000_000_000; + account_3.qualified_as.payment_threshold_intercept_minor = 1_999_999_999; + let mut account_4 = make_meaningless_analyzed_account(444); + account_4.qualified_as.bare_account.balance_wei = 5_000_000_000; + account_4.qualified_as.payment_threshold_intercept_minor = 2_000_000_000; + let qualified_accounts = &[account_1, account_2, account_3, account_4]; let result = find_largest_exceeding_balance(qualified_accounts); - assert_eq!(result, 4_000_000_000 - 800_000_000) + assert_eq!(result, 5_000_000_000 - 1_999_999_999) } #[test] @@ -272,8 +278,10 @@ mod tests { let result = compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance_minor); - let expected_result = u128::MAX / cw_service_fee_balance_minor; - assert_eq!(result, expected_result) + let expected_result_conceptually = u128::MAX / cw_service_fee_balance_minor; + let expected_result_exact = 27562873980751681962171264100016; + assert_eq!(result, expected_result_exact); + assert_eq!(expected_result_exact, expected_result_conceptually) } fn make_non_finalized_adjusted_account( From b73f96e16176b6c05bfbafc5d78b5d10d20f4722 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:50:28 +0100 Subject: [PATCH 212/250] GH-711-review-one: helper functions repaired --- .../miscellaneous/helper_functions.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 5ff7fd329..c4c5396ff 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -324,23 +324,24 @@ mod tests { #[test] fn exhaustive_status_is_constructed_properly() { - let cw_balance_remainder = 45678; + let cw_remaining_balance = 45678; - let result = ConsumingWalletExhaustingStatus::new(cw_balance_remainder); + let result = ConsumingWalletExhaustingStatus::new(cw_remaining_balance); - assert_eq!(result.remaining_cw_balance, cw_balance_remainder); + assert_eq!(result.remaining_cw_balance, cw_remaining_balance); assert_eq!(result.accounts_finalized_so_far, vec![]) } #[test] - fn three_non_exhaustive_accounts_all_refilled() { - // A seemingly irrational situation, this can happen when some of those originally qualified - // payables could get disqualified. Those would free some means that could be used for - // the other accounts. In the end, we have a final set with suboptimal balances, despite - // the unallocated cw balance is larger than the entire sum of the original balances for - // this few resulting accounts. We can pay every account fully, so, why did we need to call - // the PaymentAdjuster in first place? The detail is in the loss of some accounts, allowing - // to pay more for the others. + fn proposed_balance_refills_up_to_original_balance_for_all_three_non_exhaustive_accounts() { + // Despite looking irrational, this can happen if some of those originally qualified + // payables were eliminated. That would free some assets to be eventually used for + // the accounts left. Going forward, we've got a confirmed final accounts but with + // suboptimal balances caused by, so far, declaring them by their disqualification limits + // and no more. Therefore, we can live on a situation where the consuming wallet balance is + // more than the final, already reduced, set of accounts. This tested operation should + // ensure that the available assets will be given out maximally, resulting in a total + // pay-off on those selected accounts. let wallet_1 = make_wallet("abc"); let original_requested_balance_1 = 45_000_000_000; let proposed_adjusted_balance_1 = 44_999_897_000; From a1f97de1a54ab4a96eb61e2caaaeb3dedebb4921 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:36:46 +0100 Subject: [PATCH 213/250] GH-711-review-one: interim commit --- node/src/accountant/mod.rs | 30 ++++---- node/src/accountant/payment_adjuster/mod.rs | 74 ++++++++++--------- .../payment_adjuster/non_unit_tests/mod.rs | 6 +- .../preparatory_analyser/mod.rs | 10 +-- .../payable_scanner/mod.rs | 6 +- node/src/accountant/scanners/mod.rs | 2 +- node/src/accountant/test_utils.rs | 26 +++---- 7 files changed, 81 insertions(+), 73 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 05687b3ab..28af97c2d 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1081,7 +1081,7 @@ mod tests { use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; @@ -1492,7 +1492,7 @@ mod tests { ) { init_test_logging(); let test_name = "received_balances_and_qualified_payables_under_our_money_limit_thus_all_forwarded_to_blockchain_bridge"; - let search_for_indispensable_adjustment_params_arc = Arc::new(Mutex::new(vec![])); + let consider_adjustment_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let instructions_recipient = blockchain_bridge .system_stop_conditions(match_every_type_id!(OutboundPaymentsInstructions)) @@ -1514,10 +1514,10 @@ mod tests { }, ]; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_params( - &search_for_indispensable_adjustment_params_arc, + .consider_adjustment_params( + &consider_adjustment_params_arc, ) - .search_for_indispensable_adjustment_result(Ok(Either::Left( + .consider_adjustment_result(Ok(Either::Left( qualified_payables.clone(), ))); let payable_scanner = PayableScannerBuilder::new() @@ -1544,15 +1544,15 @@ mod tests { subject_addr.try_send(msg).unwrap(); system.run(); - let mut search_for_indispensable_adjustment_params = - search_for_indispensable_adjustment_params_arc + let mut consider_adjustment_params = + consider_adjustment_params_arc .lock() .unwrap(); let (actual_qualified_payables, actual_agent_id_stamp) = - search_for_indispensable_adjustment_params.remove(0); + consider_adjustment_params.remove(0); assert_eq!(actual_qualified_payables, qualified_payables); assert_eq!(actual_agent_id_stamp, expected_agent_id_stamp); - assert!(search_for_indispensable_adjustment_params.is_empty()); + assert!(consider_adjustment_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); let payments_instructions = blockchain_bridge_recording.get_record::(0); @@ -1627,9 +1627,9 @@ mod tests { }; let analyzed_accounts = convert_collection(unadjusted_qualified_accounts.clone()); let adjustment_analysis = - AdjustmentAnalysis::new(Adjustment::ByServiceFee, analyzed_accounts.clone()); + AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts.clone()); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Either::Right(adjustment_analysis))) + .consider_adjustment_result(Ok(Either::Right(adjustment_analysis))) .adjust_payments_params(&adjust_payments_params_arc) .adjust_payments_result(Ok(payments_instructions)); let payable_scanner = PayableScannerBuilder::new() @@ -1752,7 +1752,7 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Err( + .consider_adjustment_result(Err( PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 1, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { @@ -1787,7 +1787,7 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_during_stage_two_adjustment_went_wrong"; let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Either::Right(AdjustmentAnalysis::new( + .consider_adjustment_result(Ok(Either::Right(AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, vec![make_meaningless_analyzed_account(123)], )))) @@ -1818,7 +1818,7 @@ mod tests { "payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Err( + .consider_adjustment_result(Err( PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 20, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { @@ -3637,7 +3637,7 @@ mod tests { .build(); subject.scanners.receivable = Box::new(NullScanner::new()); let payment_adjuster = PaymentAdjusterMock::default() - .search_for_indispensable_adjustment_result(Ok(Either::Left( + .consider_adjustment_result(Ok(Either::Left( qualified_payables, ))); let payable_scanner = PayableScannerBuilder::new() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8274c15e0..b6ed3ef7f 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -57,16 +57,24 @@ use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; -// PaymentAdjuster is a very efficient recursive and scalable algorithm that inspects payments under -// the condition of an acute insolvency. You can expand the scope of the evaluation by writing your -// own CriterionCalculator, that should be specialized on a distinct parameter of a payable account, -// and sticking it inside the vector that stores them. +// PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions +// of an acute insolvency. You can easily expand the range of evaluated parameters to determine +// an optimized allocation of scarce assets by writing your own CriterionCalculator. The calculator +// is supposed to be dedicated to a single parameter that can be tracked for each payable account. +// +// For parameters that can't be derived from each account, or even one at all, there is a way to +// provide such data up into the calculator. This can be achieved via the PaymentAdjusterInner. +// +// Once the new calculator exists, its place belongs in the vector of calculators which is the heart +// of this module. pub type AdjustmentAnalysisResult = - Result, AdjustmentAnalysis>, PaymentAdjusterError>; + Result, PaymentAdjusterError>; + +pub type IntactOriginalAccounts = Vec; pub trait PaymentAdjuster { - fn search_for_indispensable_adjustment( + fn consider_adjustment( &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, @@ -91,7 +99,7 @@ pub struct PaymentAdjusterReal { } impl PaymentAdjuster for PaymentAdjusterReal { - fn search_for_indispensable_adjustment( + fn consider_adjustment( &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, @@ -423,14 +431,14 @@ pub enum Adjustment { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct AdjustmentAnalysis { +pub struct AdjustmentAnalysisReport { pub adjustment: Adjustment, pub accounts: Vec, } -impl AdjustmentAnalysis { +impl AdjustmentAnalysisReport { pub fn new(adjustment: Adjustment, accounts: Vec) -> Self { - AdjustmentAnalysis { + AdjustmentAnalysisReport { adjustment, accounts, } @@ -565,7 +573,7 @@ mod tests { MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -651,9 +659,9 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_happy_path() { + fn consider_adjustment_happy_path() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_gives_negative_answer"; + let test_name = "consider_adjustment_gives_negative_answer"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); // Service fee balance > payments @@ -709,7 +717,7 @@ mod tests { .for_each(|(idx, (qualified_payables, agent))| { assert_eq!( subject - .search_for_indispensable_adjustment(qualified_payables.clone(), &*agent), + .consider_adjustment(qualified_payables.clone(), &*agent), Ok(Either::Left(qualified_payables)), "failed for tested input number {:?}", idx + 1 @@ -720,9 +728,9 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_sad_path_for_transaction_fee() { + fn consider_adjustment_sad_path_for_transaction_fee() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_sad_path_positive_for_transaction_fee"; + let test_name = "consider_adjustment_sad_path_positive_for_transaction_fee"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let number_of_accounts = 3; @@ -738,11 +746,11 @@ mod tests { ); let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &*agent); assert_eq!( result, - Ok(Either::Right(AdjustmentAnalysis::new( + Ok(Either::Right(AdjustmentAnalysisReport::new( Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2 }, @@ -763,9 +771,9 @@ mod tests { } #[test] - fn search_for_indispensable_adjustment_sad_path_for_service_fee_balance() { + fn consider_adjustment_sad_path_for_service_fee_balance() { init_test_logging(); - let test_name = "search_for_indispensable_adjustment_positive_for_service_fee_balance"; + let test_name = "consider_adjustment_positive_for_service_fee_balance"; let logger = Logger::new(test_name); let mut subject = PaymentAdjusterReal::new(); subject.logger = logger; @@ -781,11 +789,11 @@ mod tests { ); let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &*agent); assert_eq!( result, - Ok(Either::Right(AdjustmentAnalysis::new( + Ok(Either::Right(AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, analyzed_payables ))) @@ -820,7 +828,7 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(100)); @@ -854,7 +862,7 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &*agent); assert_eq!( result, @@ -888,7 +896,7 @@ mod tests { }), ); - let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(123)); @@ -1306,7 +1314,7 @@ mod tests { }; let adjustment_setup = PreparedAdjustment { agent, - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, analyzed_accounts, ), @@ -1352,7 +1360,7 @@ mod tests { }; let adjustment_setup = PreparedAdjustment { agent, - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, analyzed_payables, ), @@ -1450,7 +1458,7 @@ mod tests { .service_fee_balance_minor_result(cw_service_fee_balance_minor); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, analyzed_payables, ), @@ -1545,7 +1553,7 @@ mod tests { .service_fee_balance_minor_result(u128::MAX); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1622,7 +1630,7 @@ mod tests { }); // Just hardening, not so important let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1685,7 +1693,7 @@ mod tests { }); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, analyzed_payables, ), @@ -1752,7 +1760,7 @@ mod tests { .service_fee_balance_minor_result(service_fee_balance_in_minor); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, @@ -1819,7 +1827,7 @@ mod tests { .service_fee_balance_minor_result(cw_service_fee_balance_minor); let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), - adjustment_analysis: AdjustmentAnalysis::new( + adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::TransactionFeeInPriority { affordable_transaction_count: 2, }, diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index fd2e689d4..f1df88924 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -8,7 +8,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::BalanceProvidingAccount; use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -125,7 +125,7 @@ fn loading_test_with_randomized_params() { .iter() .map(|account| account.qualified_as.clone()) .collect(); - let initial_check_result = subject.search_for_indispensable_adjustment( + let initial_check_result = subject.consider_adjustment( qualified_payables, &*scenario.prepared_adjustment.agent, ); @@ -218,7 +218,7 @@ fn try_making_single_valid_scenario( let prepared_adjustment = PreparedAdjustment::new( Box::new(agent), None, - AdjustmentAnalysis::new(adjustment, analyzed_accounts), + AdjustmentAnalysisReport::new(adjustment, analyzed_accounts), ); Some(PreparedAdjustmentAndThresholds { prepared_adjustment, diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index f417be55e..c37efee48 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -15,7 +15,7 @@ use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstract BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -38,7 +38,7 @@ impl PreparatoryAnalyzer { disqualification_arbiter: &DisqualificationArbiter, qualified_payables: Vec, logger: &Logger, - ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> + ) -> Result, AdjustmentAnalysisReport>, PaymentAdjusterError> { let number_of_accounts = qualified_payables.len(); let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); @@ -96,7 +96,7 @@ impl PreparatoryAnalyzer { }, }; - Ok(Either::Right(AdjustmentAnalysis::new( + Ok(Either::Right(AdjustmentAnalysisReport::new( adjustment, prepared_accounts, ))) @@ -376,7 +376,7 @@ mod tests { make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysis, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::test_utils::{ @@ -426,7 +426,7 @@ mod tests { original_accounts.to_vec(), &disqualification_arbiter, ); - AdjustmentAnalysis::new(Adjustment::ByServiceFee, analyzed_accounts) + AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts) }; assert_eq!(result, Ok(Either::Right(expected_adjustment_analysis))); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index cfa265d3a..e0488eea5 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -6,7 +6,7 @@ pub mod blockchain_agent; pub mod msgs; pub mod test_utils; -use crate::accountant::payment_adjuster::AdjustmentAnalysis; +use crate::accountant::payment_adjuster::AdjustmentAnalysisReport; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::BlockchainAgentWithContextMessage; use crate::accountant::scanners::Scanner; @@ -43,14 +43,14 @@ pub trait SolvencySensitivePaymentInstructor { pub struct PreparedAdjustment { pub agent: Box, pub response_skeleton_opt: Option, - pub adjustment_analysis: AdjustmentAnalysis, + pub adjustment_analysis: AdjustmentAnalysisReport, } impl PreparedAdjustment { pub fn new( agent: Box, response_skeleton_opt: Option, - adjustment_analysis: AdjustmentAnalysis, + adjustment_analysis: AdjustmentAnalysisReport, ) -> Self { Self { agent, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index bfcc100a8..2d30d293b 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -275,7 +275,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { match self .payment_adjuster .borrow() - .search_for_indispensable_adjustment(unprotected, &*msg.agent) + .consider_adjustment(unprotected, &*msg.agent) { Ok(processed) => { let either = match processed { diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 00a6b3d9f..78bc7d94f 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -14,7 +14,7 @@ use crate::accountant::db_access_objects::receivable_dao::{ }; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{ - AdjustmentAnalysis, PaymentAdjuster, PaymentAdjusterError, + AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ @@ -1459,10 +1459,10 @@ pub fn trick_rusqlite_with_read_only_conn( #[derive(Default)] pub struct PaymentAdjusterMock { - search_for_indispensable_adjustment_params: + consider_adjustment_params: Arc, ArbitraryIdStamp)>>>, - search_for_indispensable_adjustment_results: RefCell< - Vec, AdjustmentAnalysis>, PaymentAdjusterError>>, + consider_adjustment_results: RefCell< + Vec, AdjustmentAnalysisReport>, PaymentAdjusterError>>, >, adjust_payments_params: Arc>>, adjust_payments_results: @@ -1470,17 +1470,17 @@ pub struct PaymentAdjusterMock { } impl PaymentAdjuster for PaymentAdjusterMock { - fn search_for_indispensable_adjustment( + fn consider_adjustment( &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, AdjustmentAnalysis>, PaymentAdjusterError> + ) -> Result, AdjustmentAnalysisReport>, PaymentAdjusterError> { - self.search_for_indispensable_adjustment_params + self.consider_adjustment_params .lock() .unwrap() .push((qualified_payables, agent.arbitrary_id_stamp())); - self.search_for_indispensable_adjustment_results + self.consider_adjustment_results .borrow_mut() .remove(0) } @@ -1499,22 +1499,22 @@ impl PaymentAdjuster for PaymentAdjusterMock { } impl PaymentAdjusterMock { - pub fn search_for_indispensable_adjustment_params( + pub fn consider_adjustment_params( mut self, params: &Arc, ArbitraryIdStamp)>>>, ) -> Self { - self.search_for_indispensable_adjustment_params = params.clone(); + self.consider_adjustment_params = params.clone(); self } - pub fn search_for_indispensable_adjustment_result( + pub fn consider_adjustment_result( self, result: Result< - Either, AdjustmentAnalysis>, + Either, AdjustmentAnalysisReport>, PaymentAdjusterError, >, ) -> Self { - self.search_for_indispensable_adjustment_results + self.consider_adjustment_results .borrow_mut() .push(result); self From 2a369198839953d0f2fbaa3bb9ca3698a8ffed8a Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 5 Nov 2024 00:16:47 +0100 Subject: [PATCH 214/250] GH-711-review-one: big refactoring in the mod.rs file, also getting rid of the adjustment runners --- node/src/accountant/mod.rs | 62 ++-- .../payment_adjuster/adjustment_runners.rs | 228 -------------- .../balance_calculator.rs | 2 +- node/src/accountant/payment_adjuster/inner.rs | 30 +- .../miscellaneous/data_structures.rs | 59 +--- .../miscellaneous/helper_functions.rs | 11 +- node/src/accountant/payment_adjuster/mod.rs | 290 +++++++++++------- .../payment_adjuster/non_unit_tests/mod.rs | 15 +- .../preparatory_analyser/mod.rs | 5 +- .../payment_adjuster/service_fee_adjuster.rs | 4 +- .../accountant/payment_adjuster/test_utils.rs | 4 +- node/src/accountant/test_utils.rs | 18 +- 12 files changed, 257 insertions(+), 471 deletions(-) delete mode 100644 node/src/accountant/payment_adjuster/adjustment_runners.rs diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 28af97c2d..049c2fff3 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1081,7 +1081,8 @@ mod tests { use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, + TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; @@ -1514,12 +1515,8 @@ mod tests { }, ]; let payment_adjuster = PaymentAdjusterMock::default() - .consider_adjustment_params( - &consider_adjustment_params_arc, - ) - .consider_adjustment_result(Ok(Either::Left( - qualified_payables.clone(), - ))); + .consider_adjustment_params(&consider_adjustment_params_arc) + .consider_adjustment_result(Ok(Either::Left(qualified_payables.clone()))); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -1544,10 +1541,7 @@ mod tests { subject_addr.try_send(msg).unwrap(); system.run(); - let mut consider_adjustment_params = - consider_adjustment_params_arc - .lock() - .unwrap(); + let mut consider_adjustment_params = consider_adjustment_params_arc.lock().unwrap(); let (actual_qualified_payables, actual_agent_id_stamp) = consider_adjustment_params.remove(0); assert_eq!(actual_qualified_payables, qualified_payables); @@ -1751,17 +1745,16 @@ mod tests { init_test_logging(); let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; - let payment_adjuster = PaymentAdjusterMock::default() - .consider_adjustment_result(Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts: 1, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), - cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), - }), - service_fee_opt: None, - }, - )); + let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 1, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), + cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), + }), + service_fee_opt: None, + }, + )); test_payment_adjuster_error_during_different_stages(test_name, payment_adjuster); @@ -1817,17 +1810,16 @@ mod tests { let test_name = "payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); - let payment_adjuster = PaymentAdjusterMock::default() - .consider_adjustment_result(Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts: 20, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: 40_000_000_000, - cw_transaction_fee_balance_minor: U256::from(123), - }), - service_fee_opt: None, - }, - )); + let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( + PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + number_of_accounts: 20, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 40_000_000_000, + cw_transaction_fee_balance_minor: U256::from(123), + }), + service_fee_opt: None, + }, + )); let payable_scanner = PayableScannerBuilder::new() .payment_adjuster(payment_adjuster) .build(); @@ -3637,9 +3629,7 @@ mod tests { .build(); subject.scanners.receivable = Box::new(NullScanner::new()); let payment_adjuster = PaymentAdjusterMock::default() - .consider_adjustment_result(Ok(Either::Left( - qualified_payables, - ))); + .consider_adjustment_result(Ok(Either::Left(qualified_payables))); let payable_scanner = PayableScannerBuilder::new() .payable_dao(payable_dao_for_payable_scanner) .pending_payable_dao(pending_payable_dao_for_payable_scanner) diff --git a/node/src/accountant/payment_adjuster/adjustment_runners.rs b/node/src/accountant/payment_adjuster/adjustment_runners.rs deleted file mode 100644 index 427a571ad..000000000 --- a/node/src/accountant/payment_adjuster/adjustment_runners.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, WeightedPayable, -}; -use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; -use crate::accountant::payment_adjuster::{PaymentAdjusterError, PaymentAdjusterReal}; -use itertools::Either; -use masq_lib::utils::convert_collection; - -pub trait AdjustmentRunner { - type ReturnType; - - fn adjust_accounts( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts: Vec, - ) -> Self::ReturnType; -} - -pub struct TransactionAndServiceFeeAdjustmentRunner {} - -impl AdjustmentRunner for TransactionAndServiceFeeAdjustmentRunner { - type ReturnType = Result< - Either, Vec>, - PaymentAdjusterError, - >; - - fn adjust_accounts( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts: Vec, - ) -> Self::ReturnType { - if let Some(limit) = payment_adjuster.inner.transaction_fee_count_limit_opt() { - return payment_adjuster - .begin_with_adjustment_by_transaction_fee(weighted_accounts, limit); - } - - Ok(Either::Left( - payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts), - )) - } -} - -pub struct ServiceFeeOnlyAdjustmentRunner {} - -impl AdjustmentRunner for ServiceFeeOnlyAdjustmentRunner { - type ReturnType = Vec; - - fn adjust_accounts( - &self, - payment_adjuster: &mut PaymentAdjusterReal, - weighted_accounts: Vec, - ) -> Self::ReturnType { - let check_sum: u128 = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.initial_balance_minor() - }); - - let unallocated_cw_balance = payment_adjuster - .inner - .unallocated_cw_service_fee_balance_minor(); - - if check_sum <= unallocated_cw_balance { - // Fast return after a direct conversion into the expected type - return convert_collection(weighted_accounts); - } - - payment_adjuster.propose_possible_adjustment_recursively(weighted_accounts) - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, - }; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, WeightedPayable, - }; - use crate::accountant::payment_adjuster::test_utils::make_initialized_subject; - use crate::accountant::payment_adjuster::{Adjustment, PaymentAdjusterReal}; - use crate::accountant::test_utils::{ - make_meaningless_analyzed_account, make_meaningless_qualified_payable, - }; - use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; - use crate::sub_lib::wallet::Wallet; - use crate::test_utils::make_wallet; - use itertools::Itertools; - use std::time::SystemTime; - - fn initialize_payment_adjuster( - now: SystemTime, - service_fee_balance: u128, - max_portion_of_balance_over_threshold_in_qualified_payables: u128, - ) -> PaymentAdjusterReal { - make_initialized_subject( - Some(now), - Some(service_fee_balance), - None, - Some(max_portion_of_balance_over_threshold_in_qualified_payables), - None, - ) - } - - fn make_weighted_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { - let mut payable = - WeightedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); - payable - .analyzed_account - .qualified_as - .bare_account - .balance_wei = initial_balance_minor; - payable - } - - fn test_surplus_incurred_after_disqualification_in_previous_iteration( - subject: ServiceFeeOnlyAdjustmentRunner, - initial_balance_for_each_account: u128, - untaken_cw_service_fee_balance_minor: u128, - ) { - // Explanation: The hypothesis is that the previous iteration disqualified an account after - // which the remaining means are enough for the other accounts. - // We could assign the accounts the same as they initially requested but a fairer way to - // do sp is to give out only up to the disqualification limit of these accounts. Later - // on, the accounts that deserves it the most, according to the ordering they gain by their - // weights, will gradually get hold of the rest of the money. - let now = SystemTime::now(); - let mut payable_1 = make_weighted_payable(111, initial_balance_for_each_account); - payable_1.analyzed_account.disqualification_limit_minor = 3_444_333_444; - let mut payable_2 = make_weighted_payable(222, initial_balance_for_each_account); - payable_2.analyzed_account.disqualification_limit_minor = 3_555_333_555; - let weighted_payables = vec![payable_1, payable_2]; - let mut payment_adjuster = - initialize_payment_adjuster(now, untaken_cw_service_fee_balance_minor, 12345678); - - let result = subject.adjust_accounts(&mut payment_adjuster, weighted_payables.clone()); - - let expected_result = weighted_payables - .into_iter() - .map(|weighted_payable| { - AdjustedAccountBeforeFinalization::new( - weighted_payable.analyzed_account.qualified_as.bare_account, - weighted_payable.weight, - // Here, this is the proposed balance at the moment - weighted_payable - .analyzed_account - .disqualification_limit_minor, - ) - }) - .collect_vec(); - assert_eq!(result, expected_result) - } - - #[test] - fn untaken_cw_balance_equals_full_two_debts_after_loosing_an_account_results_in_constrained_balances( - ) { - let subject = ServiceFeeOnlyAdjustmentRunner {}; - let initial_balance_for_each_account = 5_000_000_000; - let untaken_cw_service_fee_balance_minor = - initial_balance_for_each_account + initial_balance_for_each_account; - - test_surplus_incurred_after_disqualification_in_previous_iteration( - subject, - initial_balance_for_each_account, - untaken_cw_service_fee_balance_minor, - ) - } - - #[test] - fn untaken_cw_balance_is_more_than_full_two_debts_after_loosing_an_account_results_in_constrained_balances( - ) { - let subject = ServiceFeeOnlyAdjustmentRunner {}; - let initial_balance_for_each_account = 5_000_000_000; - let untaken_cw_service_fee_balance_minor = - initial_balance_for_each_account + initial_balance_for_each_account + 1; - - test_surplus_incurred_after_disqualification_in_previous_iteration( - subject, - initial_balance_for_each_account, - untaken_cw_service_fee_balance_minor, - ) - } - - #[test] - fn adjust_accounts_for_service_fee_only_runner_is_not_supposed_to_care_about_transaction_fee() { - let common_balance = 5_000_000_000; - let mut account = make_meaningless_qualified_payable(111); - account.bare_account.balance_wei = common_balance; - let wallet_1 = make_wallet("abc"); - let wallet_2 = make_wallet("def"); - let mut account_1 = account.clone(); - account_1.bare_account.wallet = wallet_1.clone(); - let mut account_2 = account; - account_2.bare_account.wallet = wallet_2.clone(); - let weighted_account = |account: QualifiedPayableAccount| WeightedPayable { - analyzed_account: AnalyzedPayableAccount::new(account, 3_000_000_000), - weight: 4_000_000_000, - }; - let weighted_accounts = vec![weighted_account(account_1), weighted_account(account_2)]; - // We instruct a performance of adjustment by the transaction fee, as if there were - // two transaction, but we had enough fee just for one. Still, you can see at the end of - // the test that this reduction didn't take place which shows that we used the kind of - // runner which ignore this instruction. - let adjustment_type = Adjustment::TransactionFeeInPriority { - affordable_transaction_count: 1, - }; - let cw_service_fee_balance_minor = (10 * common_balance) / 8; - let mut payment_adjuster = PaymentAdjusterReal::new(); - payment_adjuster.initialize_inner( - cw_service_fee_balance_minor, - adjustment_type, - 123456789, - SystemTime::now(), - ); - let subject = ServiceFeeOnlyAdjustmentRunner {}; - - let result = subject.adjust_accounts(&mut payment_adjuster, weighted_accounts); - - let returned_accounts = result - .into_iter() - .map(|account| account.original_account.wallet) - .collect::>(); - assert_eq!(returned_accounts, vec![wallet_1, wallet_2]) - // If the transaction fee adjustment had been available to be performed, only one account - // would've been returned. This test passes - } -} diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index 69c7c81e3..db9f4cfc8 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -13,7 +13,7 @@ impl CriterionCalculator for BalanceCriterionCalculator { account: &QualifiedPayableAccount, context: &dyn PaymentAdjusterInner, ) -> u128 { - let largest = context.max_portion_of_balance_over_threshold_in_qualified_payables(); + let largest = context.max_debt_above_threshold_in_qualified_payables(); let this_account = account.bare_account.balance_wei - account.payment_threshold_intercept_minor; diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index ab3d7bd27..f07b3fc37 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -4,7 +4,7 @@ use std::time::SystemTime; pub trait PaymentAdjusterInner { fn now(&self) -> SystemTime; - fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128; + fn max_debt_above_threshold_in_qualified_payables(&self) -> u128; fn transaction_fee_count_limit_opt(&self) -> Option; fn original_cw_service_fee_balance_minor(&self) -> u128; fn unallocated_cw_service_fee_balance_minor(&self) -> u128; @@ -14,7 +14,7 @@ pub trait PaymentAdjusterInner { pub struct PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, - max_portion_of_balance_over_threshold_in_qualified_payables: u128, + max_debt_above_threshold_in_qualified_payables: u128, original_cw_service_fee_balance_minor: u128, unallocated_cw_service_fee_balance_minor: u128, } @@ -24,12 +24,12 @@ impl PaymentAdjusterInnerReal { now: SystemTime, transaction_fee_count_limit_opt: Option, cw_service_fee_balance_minor: u128, - max_portion_of_balance_over_threshold_in_qualified_payables: u128, + max_debt_above_threshold_in_qualified_payables: u128, ) -> Self { Self { now, transaction_fee_count_limit_opt, - max_portion_of_balance_over_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } @@ -41,8 +41,8 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerReal { self.now } - fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128 { - self.max_portion_of_balance_over_threshold_in_qualified_payables + fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { + self.max_debt_above_threshold_in_qualified_payables } fn transaction_fee_count_limit_opt(&self) -> Option { @@ -80,9 +80,9 @@ impl PaymentAdjusterInner for PaymentAdjusterInnerNull { PaymentAdjusterInnerNull::panicking_operation("now()") } - fn max_portion_of_balance_over_threshold_in_qualified_payables(&self) -> u128 { + fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { PaymentAdjusterInnerNull::panicking_operation( - "max_portion_of_balance_over_threshold_in_qualified_payables()", + "max_debt_above_threshold_in_qualified_payables()", ) } @@ -114,12 +114,12 @@ mod tests { let now = SystemTime::now(); let transaction_fee_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; - let max_portion_of_balance_over_threshold_in_qualified_payables = 44_555_666; + let max_debt_above_threshold_in_qualified_payables = 44_555_666; let result = PaymentAdjusterInnerReal::new( now, transaction_fee_count_limit_opt, cw_service_fee_balance, - max_portion_of_balance_over_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables, ); assert_eq!(result.now, now); @@ -136,8 +136,8 @@ mod tests { cw_service_fee_balance ); assert_eq!( - result.max_portion_of_balance_over_threshold_in_qualified_payables, - max_portion_of_balance_over_threshold_in_qualified_payables + result.max_debt_above_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables ) } @@ -153,13 +153,13 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the max_portion_of_balance_over_threshold_in_qualified_payables() \ + expected = "Broken code: Called the null implementation of the max_debt_above_threshold_in_qualified_payables() \ method in PaymentAdjusterInner" )] - fn inner_null_calling_max_portion_of_balance_over_threshold_in_qualified_payables() { + fn inner_null_calling_max_debt_above_threshold_in_qualified_payables() { let subject = PaymentAdjusterInnerNull::default(); - let _ = subject.max_portion_of_balance_over_threshold_in_qualified_payables(); + let _ = subject.max_debt_above_threshold_in_qualified_payables(); } #[test] diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 6de4591c7..a4453dc39 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -30,34 +30,10 @@ impl WeightedPayable { #[derive(Debug, PartialEq, Eq)] pub struct AdjustmentIterationResult { - pub decided_accounts_opt: Option>, + pub decided_accounts: Vec, pub remaining_undecided_accounts: Vec, } -pub struct RecursionResults { - pub here_decided_accounts: Vec, - pub downstream_decided_accounts: Vec, -} - -impl RecursionResults { - pub fn new( - here_decided_accounts: Vec, - downstream_decided_accounts: Vec, - ) -> Self { - Self { - here_decided_accounts, - downstream_decided_accounts, - } - } - - pub fn merge_results_from_recursion(self) -> Vec { - self.here_decided_accounts - .into_iter() - .chain(self.downstream_decided_accounts.into_iter()) - .collect() - } -} - #[derive(Debug, PartialEq, Eq, Clone)] pub struct AdjustedAccountBeforeFinalization { pub original_account: PayableAccount, @@ -124,40 +100,9 @@ impl AffordableAndRequiredTxCounts { #[cfg(test)] mod tests { - use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, AffordableAndRequiredTxCounts, RecursionResults, - }; - use crate::accountant::test_utils::make_payable_account; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::AffordableAndRequiredTxCounts; use ethereum_types::U256; - #[test] - fn merging_results_from_recursion_works() { - let non_finalized_account_1 = - AdjustedAccountBeforeFinalization::new(make_payable_account(111), 12345, 1234); - let non_finalized_account_2 = - AdjustedAccountBeforeFinalization::new(make_payable_account(222), 543, 5555); - let non_finalized_account_3 = - AdjustedAccountBeforeFinalization::new(make_payable_account(333), 789987, 6789); - let subject = RecursionResults { - here_decided_accounts: vec![non_finalized_account_1.clone()], - downstream_decided_accounts: vec![ - non_finalized_account_2.clone(), - non_finalized_account_3.clone(), - ], - }; - - let result = subject.merge_results_from_recursion(); - - assert_eq!( - result, - vec![ - non_finalized_account_1, - non_finalized_account_2, - non_finalized_account_3 - ] - ) - } - #[test] fn there_is_u16_ceiling_for_possible_tx_count() { let corrections_from_u16_max = [-3_i8, -1, 0, 1, 10]; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index c4c5396ff..911d721a6 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -27,7 +27,7 @@ where collection.iter().map(arranger).sum::() } -pub fn dump_unaffordable_accounts_by_transaction_fee( +pub fn eliminate_accounts_by_tx_fee_limit( weighted_accounts: Vec, affordable_transaction_count: u16, ) -> Vec { @@ -187,9 +187,8 @@ mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - compute_mul_coefficient_preventing_fractional_numbers, - dump_unaffordable_accounts_by_transaction_fee, exhaust_cw_balance_entirely, - find_largest_exceeding_balance, no_affordable_accounts_found, + compute_mul_coefficient_preventing_fractional_numbers, eliminate_accounts_by_tx_fee_limit, + exhaust_cw_balance_entirely, find_largest_exceeding_balance, no_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; use crate::accountant::payment_adjuster::test_utils::make_weighed_account; @@ -251,7 +250,7 @@ mod tests { } #[test] - fn dump_unaffordable_accounts_by_transaction_fee_works() { + fn eliminate_accounts_by_tx_fee_limit_works() { let mut account_1 = make_weighed_account(123); account_1.weight = 1_000_000_000; let mut account_2 = make_weighed_account(456); @@ -262,7 +261,7 @@ mod tests { account_4.weight = 1_000_000_001; let affordable_transaction_count = 2; - let result = dump_unaffordable_accounts_by_transaction_fee( + let result = eliminate_accounts_by_tx_fee_limit( vec![account_1.clone(), account_2, account_3, account_4.clone()], affordable_transaction_count, ); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b6ed3ef7f..07955952d 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -1,7 +1,6 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -// If possible, let these modules be private -mod adjustment_runners; +// If possible, keep these modules private mod criterion_calculators; mod disqualification_arbiter; mod inner; @@ -15,9 +14,6 @@ mod service_fee_adjuster; mod test_utils; use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::adjustment_runners::{ - AdjustmentRunner, ServiceFeeOnlyAdjustmentRunner, TransactionAndServiceFeeAdjustmentRunner, -}; use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::calculated_criterion_and_weight_diagnostics; @@ -31,9 +27,9 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, AdjustmentIterationResult, RecursionResults, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeightedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ - dump_unaffordable_accounts_by_transaction_fee, + eliminate_accounts_by_tx_fee_limit, exhaust_cw_balance_entirely, find_largest_exceeding_balance, sum_as, no_affordable_accounts_found, }; @@ -56,6 +52,7 @@ use thousands::Separable; use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; +use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; // PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions // of an acute insolvency. You can easily expand the range of evaluated parameters to determine @@ -85,8 +82,6 @@ pub trait PaymentAdjuster { setup: PreparedAdjustment, now: SystemTime, ) -> Result; - - as_any_ref_in_trait!(); } pub struct PaymentAdjusterReal { @@ -121,21 +116,21 @@ impl PaymentAdjuster for PaymentAdjusterReal { let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment_analysis.adjustment; - let max_portion_of_balance_over_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); self.initialize_inner( initial_service_fee_balance_minor, required_adjustment, - max_portion_of_balance_over_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables, now, ); - let sketched_debug_info_opt = self.sketch_debug_info_opt(&analyzed_payables); + let sketched_debug_log_opt = self.sketch_debug_log_opt(&analyzed_payables); let affordable_accounts = self.run_adjustment(analyzed_payables)?; - self.complete_debug_info_if_enabled(sketched_debug_info_opt, &affordable_accounts); + self.complete_debug_log_if_enabled(sketched_debug_log_opt, &affordable_accounts); self.reset_inner(); @@ -145,8 +140,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { response_skeleton_opt, )) } - - as_any_ref_in_trait_impl!(); } impl Default for PaymentAdjusterReal { @@ -171,11 +164,11 @@ impl PaymentAdjusterReal { &mut self, cw_service_fee_balance: u128, required_adjustment: Adjustment, - max_portion_of_balance_over_threshold_in_qualified_payables: u128, + max_debt_above_threshold_in_qualified_payables: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count, } => Some(affordable_transaction_count), Adjustment::ByServiceFee => None, @@ -185,7 +178,7 @@ impl PaymentAdjusterReal { now, transaction_fee_limitation_opt, cw_service_fee_balance, - max_portion_of_balance_over_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables, ); self.inner = Box::new(inner); @@ -200,10 +193,7 @@ impl PaymentAdjusterReal { analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { let weighted_accounts = self.calculate_weights(analyzed_accounts); - let processed_accounts = self.propose_adjustments_recursively( - weighted_accounts, - TransactionAndServiceFeeAdjustmentRunner {}, - )?; + let processed_accounts = self.resolve_initial_adjustment_dispatch(weighted_accounts)?; if no_affordable_accounts_found(&processed_accounts) { return Err(PaymentAdjusterError::AllAccountsEliminated); @@ -223,37 +213,41 @@ impl PaymentAdjusterReal { } } - fn propose_adjustments_recursively( + fn resolve_initial_adjustment_dispatch( &mut self, - unresolved_accounts: Vec, - adjustment_runner: AR, - ) -> RT - where - AR: AdjustmentRunner, - { - diagnostics!( - "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:", - &unresolved_accounts - ); + weighted_payables: Vec, + ) -> Result< + Either, Vec>, + PaymentAdjusterError, + > { + if let Some(limit) = self.inner.transaction_fee_count_limit_opt() { + return self.begin_with_adjustment_by_transaction_fee(weighted_payables, limit); + } - adjustment_runner.adjust_accounts(self, unresolved_accounts) + Ok(Either::Left(self.propose_possible_adjustment_recursively( + weighted_payables, + ))) } fn begin_with_adjustment_by_transaction_fee( &mut self, - weighted_accounts: Vec, + weighed_accounts: Vec, already_known_affordable_transaction_count: u16, ) -> Result< Either, Vec>, PaymentAdjusterError, > { - let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighted_accounts); + diagnostics!( + "\nBEGINNING WITH ADJUSTMENT BY TRANSACTION FEE FOR ACCOUNTS:", + &weighed_accounts + ); - let weighted_accounts_affordable_by_transaction_fee = - dump_unaffordable_accounts_by_transaction_fee( - weighted_accounts, - already_known_affordable_transaction_count, - ); + let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); + + let weighted_accounts_affordable_by_transaction_fee = eliminate_accounts_by_tx_fee_limit( + weighed_accounts, + already_known_affordable_transaction_count, + ); let cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); @@ -263,14 +257,12 @@ impl PaymentAdjusterReal { error_factory, &self.logger, )? { - diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES"); - - let adjustment_result_before_verification = self + let final_set_before_exhausting_cw_balance = self .propose_possible_adjustment_recursively( weighted_accounts_affordable_by_transaction_fee, ); - Ok(Either::Left(adjustment_result_before_verification)) + Ok(Either::Left(final_set_before_exhausting_cw_balance)) } else { let accounts_not_needing_adjustment = convert_collection(weighted_accounts_affordable_by_transaction_fee); @@ -283,6 +275,11 @@ impl PaymentAdjusterReal { &mut self, weighed_accounts: Vec, ) -> Vec { + diagnostics!( + "\nUNRESOLVED ACCOUNTS IN CURRENT ITERATION:", + &weighed_accounts + ); + let disqualification_arbiter = &self.disqualification_arbiter; let unallocated_cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); @@ -295,9 +292,29 @@ impl PaymentAdjusterReal { logger, ); - let recursion_results = self.resolve_current_iteration_result(current_iteration_result); + let decided_accounts = current_iteration_result.decided_accounts; + let remaining_undecided_accounts = current_iteration_result.remaining_undecided_accounts; - let merged = recursion_results.merge_results_from_recursion(); + if remaining_undecided_accounts.is_empty() { + return decided_accounts; + } + + if !decided_accounts.is_empty() { + self.adjust_remaining_unallocated_cw_balance_down(&decided_accounts) + } + + let merged = + if self.is_cw_balance_enough_to_remaining_accounts(&remaining_undecided_accounts) { + Self::merge_accounts( + decided_accounts, + convert_collection(remaining_undecided_accounts), + ) + } else { + Self::merge_accounts( + decided_accounts, + self.propose_possible_adjustment_recursively(remaining_undecided_accounts), + ) + }; diagnostics!( "\nFINAL SET OF ADJUSTED ACCOUNTS IN CURRENT ITERATION:", @@ -307,35 +324,24 @@ impl PaymentAdjusterReal { merged } - fn resolve_current_iteration_result( - &mut self, - adjustment_iteration_result: AdjustmentIterationResult, - ) -> RecursionResults { - let remaining_undecided_accounts = adjustment_iteration_result.remaining_undecided_accounts; - let here_decided_accounts = match adjustment_iteration_result.decided_accounts_opt { - None => { - if remaining_undecided_accounts.is_empty() { - return RecursionResults::new(vec![], vec![]); - } - - vec![] - } - Some(decided_accounts) => { - if remaining_undecided_accounts.is_empty() { - return RecursionResults::new(decided_accounts, vec![]); - } - - self.adjust_remaining_unallocated_cw_balance_down(&decided_accounts); - decided_accounts - } - }; - - let down_stream_decided_accounts = self.propose_adjustments_recursively( - remaining_undecided_accounts, - ServiceFeeOnlyAdjustmentRunner {}, - ); + fn is_cw_balance_enough_to_remaining_accounts( + &self, + remaining_undecided_accounts: &[WeightedPayable], + ) -> bool { + let unallocated_cw_service_fee_balance = + self.inner.unallocated_cw_service_fee_balance_minor(); + let minimum_sum_required: u128 = sum_as(remaining_undecided_accounts, |weighted_account| { + weighted_account.disqualification_limit() + }); + minimum_sum_required <= unallocated_cw_service_fee_balance + } - RecursionResults::new(here_decided_accounts, down_stream_decided_accounts) + fn merge_accounts( + mut previously_decided_accounts: Vec, + newly_decided_accounts: Vec, + ) -> Vec { + previously_decided_accounts.extend(newly_decided_accounts); + previously_decided_accounts } fn calculate_weights(&self, accounts: Vec) -> Vec { @@ -376,9 +382,9 @@ impl PaymentAdjusterReal { fn adjust_remaining_unallocated_cw_balance_down( &mut self, - processed_outweighed: &[AdjustedAccountBeforeFinalization], + decided_accounts: &[AdjustedAccountBeforeFinalization], ) { - let subtrahend_total: u128 = sum_as(processed_outweighed, |account| { + let subtrahend_total: u128 = sum_as(decided_accounts, |account| { account.proposed_adjusted_balance_minor }); self.inner @@ -394,7 +400,7 @@ impl PaymentAdjusterReal { ) } - fn sketch_debug_info_opt( + fn sketch_debug_log_opt( &self, qualified_payables: &[AnalyzedPayableAccount], ) -> Option> { @@ -411,7 +417,7 @@ impl PaymentAdjusterReal { }) } - fn complete_debug_info_if_enabled( + fn complete_debug_log_if_enabled( &self, sketched_debug_info_opt: Option>, affordable_accounts: &[PayableAccount], @@ -427,7 +433,7 @@ impl PaymentAdjusterReal { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Adjustment { ByServiceFee, - TransactionFeeInPriority { affordable_transaction_count: u16 }, + BeginByTransactionFee { affordable_transaction_count: u16 }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -557,7 +563,6 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::adjustment_runners::TransactionAndServiceFeeAdjustmentRunner; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; @@ -573,14 +578,16 @@ mod tests { MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, - ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, + PaymentAdjusterReal, ServiceFeeImmoderateInsufficiency, + TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; use crate::accountant::test_utils::{ - make_analyzed_payables, make_payable_account, make_qualified_payables, + make_analyzed_payables, make_meaningless_analyzed_account, make_payable_account, + make_qualified_payables, }; use crate::accountant::{ gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, @@ -616,13 +623,13 @@ mod tests { ) { let mut subject = PaymentAdjusterReal::default(); let cw_service_fee_balance = 111_222_333_444; - let max_portion_of_balance_over_threshold_in_qualified_payables = 3_555_666; + let max_debt_above_threshold_in_qualified_payables = 3_555_666; let now = SystemTime::now(); subject.initialize_inner( cw_service_fee_balance, required_adjustment, - max_portion_of_balance_over_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables, now, ); @@ -642,8 +649,8 @@ mod tests { assert_eq!( subject .inner - .max_portion_of_balance_over_threshold_in_qualified_payables(), - max_portion_of_balance_over_threshold_in_qualified_payables + .max_debt_above_threshold_in_qualified_payables(), + max_debt_above_threshold_in_qualified_payables ) } @@ -651,7 +658,7 @@ mod tests { fn initialize_inner_processes_works() { test_initialize_inner_works(Adjustment::ByServiceFee, None); test_initialize_inner_works( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 5, }, Some(5), @@ -716,8 +723,7 @@ mod tests { .enumerate() .for_each(|(idx, (qualified_payables, agent))| { assert_eq!( - subject - .consider_adjustment(qualified_payables.clone(), &*agent), + subject.consider_adjustment(qualified_payables.clone(), &*agent), Ok(Either::Left(qualified_payables)), "failed for tested input number {:?}", idx + 1 @@ -751,7 +757,7 @@ mod tests { assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 2 }, analyzed_payables @@ -1088,10 +1094,7 @@ mod tests { ]; let mut result = subject - .propose_adjustments_recursively( - weighted_payables.clone(), - TransactionAndServiceFeeAdjustmentRunner {}, - ) + .resolve_initial_adjustment_dispatch(weighted_payables.clone()) .unwrap() .left() .unwrap(); @@ -1140,12 +1143,12 @@ mod tests { wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, ) { - let garbage_max_portion_of_balance_over_threshold_in_qualified_payables = 123456789; + let garbage_max_debt_above_threshold_in_qualified_payables = 123456789; subject.inner = Box::new(PaymentAdjusterInnerReal::new( SystemTime::now(), None, cw_service_fee_balance_minor, - garbage_max_portion_of_balance_over_threshold_in_qualified_payables, + garbage_max_debt_above_threshold_in_qualified_payables, )); let unconfirmed_adjustments = AdjustmentComputer::default() .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); @@ -1394,6 +1397,83 @@ mod tests { }); } + fn make_weighted_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { + let mut payable = + WeightedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); + payable + .analyzed_account + .qualified_as + .bare_account + .balance_wei = initial_balance_minor; + payable + } + + fn test_is_cw_balance_enough_to_remaining_accounts( + initial_disqualification_limit_for_each_account: u128, + untaken_cw_service_fee_balance_minor: u128, + expected_result: bool, + ) { + let mut subject = PaymentAdjusterReal::new(); + subject.initialize_inner( + untaken_cw_service_fee_balance_minor, + Adjustment::ByServiceFee, + 1234567, + SystemTime::now(), + ); + let mut payable_1 = + make_weighted_payable(111, 2 * initial_disqualification_limit_for_each_account); + payable_1.analyzed_account.disqualification_limit_minor = + initial_disqualification_limit_for_each_account; + let mut payable_2 = + make_weighted_payable(222, 3 * initial_disqualification_limit_for_each_account); + payable_2.analyzed_account.disqualification_limit_minor = + initial_disqualification_limit_for_each_account; + let weighted_payables = vec![payable_1, payable_2]; + + let result = subject.is_cw_balance_enough_to_remaining_accounts(&weighted_payables); + + assert_eq!(result, expected_result) + } + + #[test] + fn untaken_balance_is_equal_to_sum_of_disqualification_limits_in_remaining_accounts() { + let disqualification_limit_for_each_account = 5_000_000_000; + let untaken_cw_service_fee_balance_minor = + disqualification_limit_for_each_account + disqualification_limit_for_each_account; + + test_is_cw_balance_enough_to_remaining_accounts( + disqualification_limit_for_each_account, + untaken_cw_service_fee_balance_minor, + true, + ) + } + + #[test] + fn untaken_balance_is_more_than_sum_of_disqualification_limits_in_remaining_accounts() { + let disqualification_limit_for_each_account = 5_000_000_000; + let untaken_cw_service_fee_balance_minor = + disqualification_limit_for_each_account + disqualification_limit_for_each_account + 1; + + test_is_cw_balance_enough_to_remaining_accounts( + disqualification_limit_for_each_account, + untaken_cw_service_fee_balance_minor, + true, + ) + } + + #[test] + fn untaken_balance_is_less_than_sum_of_disqualification_limits_in_remaining_accounts() { + let disqualification_limit_for_each_account = 5_000_000_000; + let untaken_cw_service_fee_balance_minor = + disqualification_limit_for_each_account + disqualification_limit_for_each_account - 1; + + test_is_cw_balance_enough_to_remaining_accounts( + disqualification_limit_for_each_account, + untaken_cw_service_fee_balance_minor, + false, + ) + } + fn meaningless_timestamp() -> SystemTime { SystemTime::now() } @@ -1554,7 +1634,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 2, }, analyzed_payables, @@ -1631,7 +1711,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 2, }, analyzed_payables, @@ -1761,7 +1841,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 2, }, analyzed_payables, @@ -1828,7 +1908,7 @@ mod tests { let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count: 2, }, analyzed_payables, @@ -2192,13 +2272,13 @@ mod tests { cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = convert_collection(qualified_payables); - let max_portion_of_balance_over_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); let mut subject = make_initialized_subject( Some(now), Some(cw_service_fee_balance_minor), None, - Some(max_portion_of_balance_over_threshold_in_qualified_payables), + Some(max_debt_above_threshold_in_qualified_payables), None, ); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); @@ -2208,7 +2288,7 @@ mod tests { // This is just a sentinel that allows us to shorten the adjustment execution. // We care only for the params captured inside the container from above .perform_adjustment_by_service_fee_result(AdjustmentIterationResult { - decided_accounts_opt: Some(vec![]), + decided_accounts: vec![], remaining_undecided_accounts: vec![], }); subject.service_fee_adjuster = Box::new(service_fee_adjuster_mock); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index f1df88924..636bfa43a 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -8,7 +8,8 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::BalanceProvidingAccount; use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, + Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, + PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::PreparedAdjustment; @@ -125,10 +126,8 @@ fn loading_test_with_randomized_params() { .iter() .map(|account| account.qualified_as.clone()) .collect(); - let initial_check_result = subject.consider_adjustment( - qualified_payables, - &*scenario.prepared_adjustment.agent, - ); + let initial_check_result = subject + .consider_adjustment(qualified_payables, &*scenario.prepared_adjustment.agent); let allowed_scenario_opt = match initial_check_result { Ok(check_factual_output) => { match check_factual_output { @@ -452,7 +451,7 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { if also_by_transaction_fee && accounts_count > 2 { let affordable_transaction_count = u16::try_from(generate_non_zero_usize(gn, accounts_count)).unwrap(); - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count, } } else { @@ -690,7 +689,7 @@ fn do_final_processing_of_single_scenario( } if matches!( positive.common.required_adjustment, - Adjustment::TransactionFeeInPriority { .. } + Adjustment::BeginByTransactionFee { .. } ) { test_overall_output .fulfillment_distribution_for_transaction_fee_adjustments @@ -907,7 +906,7 @@ fn write_error(file: &mut File, error: PaymentAdjusterError) { fn resolve_affordable_transaction_count(adjustment: &Adjustment) -> String { match adjustment { Adjustment::ByServiceFee => "UNLIMITED".to_string(), - Adjustment::TransactionFeeInPriority { + Adjustment::BeginByTransactionFee { affordable_transaction_count, } => affordable_transaction_count.to_string(), } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index c37efee48..efb89cdf8 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -91,7 +91,7 @@ impl PreparatoryAnalyzer { let adjustment = match transaction_fee_limitation_opt { None => Adjustment::ByServiceFee, - Some(affordable_transaction_count) => Adjustment::TransactionFeeInPriority { + Some(affordable_transaction_count) => Adjustment::BeginByTransactionFee { affordable_transaction_count, }, }; @@ -376,7 +376,8 @@ mod tests { make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, + ServiceFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; use crate::accountant::test_utils::{ diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 85fdd6268..b0ab1996c 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -99,7 +99,7 @@ impl ServiceFeeAdjusterReal { let pre_processed_decided_accounts: Vec = convert_collection(thriving_competitors); Either::Right(AdjustmentIterationResult { - decided_accounts_opt: Some(pre_processed_decided_accounts), + decided_accounts: pre_processed_decided_accounts, remaining_undecided_accounts, }) } @@ -121,7 +121,7 @@ impl ServiceFeeAdjusterReal { let remaining_reverted = convert_collection(remaining); AdjustmentIterationResult { - decided_accounts_opt: None, + decided_accounts: vec![], remaining_undecided_accounts: remaining_reverted, } } diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index b76e97648..dc35f548e 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -37,7 +37,7 @@ pub fn make_initialized_subject( now_opt: Option, cw_service_fee_balance_minor_opt: Option, criterion_calculator_mock_opt: Option, - max_portion_of_balance_over_threshold_in_qualified_payables: Option, + max_debt_above_threshold_in_qualified_payables: Option, logger_opt: Option, ) -> PaymentAdjusterReal { let cw_service_fee_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); @@ -48,7 +48,7 @@ pub fn make_initialized_subject( now_opt.unwrap_or(SystemTime::now()), None, cw_service_fee_balance_minor, - max_portion_of_balance_over_threshold_in_qualified_payables.unwrap_or(0), + max_debt_above_threshold_in_qualified_payables.unwrap_or(0), )); if let Some(calculator) = criterion_calculator_mock_opt { subject.calculators = vec![Box::new(calculator)] diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 78bc7d94f..d243b76d1 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1459,10 +1459,14 @@ pub fn trick_rusqlite_with_read_only_conn( #[derive(Default)] pub struct PaymentAdjusterMock { - consider_adjustment_params: - Arc, ArbitraryIdStamp)>>>, + consider_adjustment_params: Arc, ArbitraryIdStamp)>>>, consider_adjustment_results: RefCell< - Vec, AdjustmentAnalysisReport>, PaymentAdjusterError>>, + Vec< + Result< + Either, AdjustmentAnalysisReport>, + PaymentAdjusterError, + >, + >, >, adjust_payments_params: Arc>>, adjust_payments_results: @@ -1480,9 +1484,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { .lock() .unwrap() .push((qualified_payables, agent.arbitrary_id_stamp())); - self.consider_adjustment_results - .borrow_mut() - .remove(0) + self.consider_adjustment_results.borrow_mut().remove(0) } fn adjust_payments( @@ -1514,9 +1516,7 @@ impl PaymentAdjusterMock { PaymentAdjusterError, >, ) -> Self { - self.consider_adjustment_results - .borrow_mut() - .push(result); + self.consider_adjustment_results.borrow_mut().push(result); self } From e6c817c90e4d93b844ac44b849fc75f7c369f85c Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:26:25 +0100 Subject: [PATCH 215/250] GH-711-review-one: interim commit --- dns_utility/dns_utility.iml | 2 - node/src/accountant/payment_adjuster/inner.rs | 21 ++- .../miscellaneous/data_structures.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 147 +++++++++--------- .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../preparatory_analyser/mod.rs | 10 +- .../payable_scanner/agent_null.rs | 12 +- .../payable_scanner/agent_web3.rs | 10 +- .../payable_scanner/blockchain_agent.rs | 2 +- .../payable_scanner/test_utils.rs | 10 +- .../blockchain_interface_web3/mod.rs | 2 +- 11 files changed, 114 insertions(+), 106 deletions(-) diff --git a/dns_utility/dns_utility.iml b/dns_utility/dns_utility.iml index 7c3466207..7bcd61c17 100644 --- a/dns_utility/dns_utility.iml +++ b/dns_utility/dns_utility.iml @@ -5,10 +5,8 @@ - - diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index f07b3fc37..2dc3c8a4a 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -69,7 +69,7 @@ pub struct PaymentAdjusterInnerNull {} impl PaymentAdjusterInnerNull { fn panicking_operation(operation: &str) -> ! { panic!( - "Broken code: Called the null implementation of the {} method in PaymentAdjusterInner", + "The PaymentAdjuster Inner is uninitialised. It was detected while executing {}", operation ) } @@ -143,7 +143,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the now() method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + now()" )] fn inner_null_calling_now() { let subject = PaymentAdjusterInnerNull::default(); @@ -153,8 +154,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the max_debt_above_threshold_in_qualified_payables() \ - method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + max_debt_above_threshold_in_qualified_payables()" )] fn inner_null_calling_max_debt_above_threshold_in_qualified_payables() { let subject = PaymentAdjusterInnerNull::default(); @@ -164,7 +165,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the transaction_fee_count_limit_opt() method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + transaction_fee_count_limit_opt()" )] fn inner_null_calling_transaction_fee_count_limit_opt() { let subject = PaymentAdjusterInnerNull::default(); @@ -174,7 +176,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + original_cw_service_fee_balance_minor()" )] fn inner_null_calling_original_cw_service_fee_balance_minor() { let subject = PaymentAdjusterInnerNull::default(); @@ -184,7 +187,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + unallocated_cw_service_fee_balance_minor()" )] fn inner_null_calling_unallocated_cw_balance() { let subject = PaymentAdjusterInnerNull::default(); @@ -194,7 +198,8 @@ mod tests { #[test] #[should_panic( - expected = "Broken code: Called the null implementation of the subtract_from_unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + subtract_from_unallocated_cw_service_fee_balance_minor()" )] fn inner_null_calling_subtract_from_unallocated_cw_service_fee_balance_minor() { let mut subject = PaymentAdjusterInnerNull::default(); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index a4453dc39..7d9cdf147 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -146,7 +146,7 @@ mod tests { fn plus_minus_correction_for_u16_max(correction: i8) -> usize { if correction < 0 { - (u16::MAX - correction.abs() as u16) as usize + u16::MAX as usize - (correction.abs() as usize) } else { u16::MAX as usize + correction as usize } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 07955952d..9c3176911 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -232,7 +232,7 @@ impl PaymentAdjusterReal { fn begin_with_adjustment_by_transaction_fee( &mut self, weighed_accounts: Vec, - already_known_affordable_transaction_count: u16, + transaction_count_limit: u16, ) -> Result< Either, Vec>, PaymentAdjusterError, @@ -246,7 +246,7 @@ impl PaymentAdjusterReal { let weighted_accounts_affordable_by_transaction_fee = eliminate_accounts_by_tx_fee_limit( weighed_accounts, - already_known_affordable_transaction_count, + transaction_count_limit, ); let cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); @@ -420,12 +420,12 @@ impl PaymentAdjusterReal { fn complete_debug_log_if_enabled( &self, sketched_debug_info_opt: Option>, - affordable_accounts: &[PayableAccount], + fully_processed_accounts: &[PayableAccount], ) { self.logger.debug(|| { let sketched_debug_info = sketched_debug_info_opt.expect("debug is enabled, so info should exist"); - accounts_before_and_after_debug(sketched_debug_info, affordable_accounts) + accounts_before_and_after_debug(sketched_debug_info, fully_processed_accounts) }) } } @@ -485,11 +485,12 @@ impl PaymentAdjusterError { PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => true, PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => true, PaymentAdjusterError::AllAccountsEliminated => true, - // We haven't needed to worry so yet, but adding an error not implying that - // an insolvency was found out, might become relevant in the future. Then, it'll - // be important to check for those consequences (Hint: It is anticipated to affect - // the wording of error announcements that take place back nearer to the Accountant's - // general area) + // We haven't needed to worry in this matter yet, this is rather a future alarm that + // will draw attention after somebody adds a possibility for an error not necessarily + // implying that an insolvency was detected before. At the moment, each error occurs + // only alongside an actual insolvency. (Hint: There might be consequences for + // the wording of the error message whose forming takes place back out, nearer to the + // Accountant's general area) } } } @@ -589,9 +590,7 @@ mod tests { make_analyzed_payables, make_meaningless_analyzed_account, make_payable_account, make_qualified_payables, }; - use crate::accountant::{ - gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, - }; + use crate::accountant::{gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, AnalyzedPayableAccount}; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -609,12 +608,12 @@ mod tests { use web3::types::U256; #[test] - #[should_panic(expected = "Broken code: Called the null implementation of \ - the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner")] + #[should_panic(expected = "The PaymentAdjuster Inner is uninitialised. It was detected while \ + executing unallocated_cw_service_fee_balance_minor()")] fn payment_adjuster_new_is_created_with_inner_null() { - let result = PaymentAdjusterReal::new(); + let subject = PaymentAdjusterReal::new(); - let _ = result.inner.unallocated_cw_service_fee_balance_minor(); + let _ = subject.inner.unallocated_cw_service_fee_balance_minor(); } fn test_initialize_inner_works( @@ -668,15 +667,15 @@ mod tests { #[test] fn consider_adjustment_happy_path() { init_test_logging(); - let test_name = "consider_adjustment_gives_negative_answer"; + let test_name = "consider_adjustment_happy_path"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); // Service fee balance > payments let input_1 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Right(vec![ - gwei_to_wei::(85), - gwei_to_wei::(15) - 1, + gwei_to_wei(85_u64), + gwei_to_wei::(15) - 1, ]), cw_balance_minor: gwei_to_wei(100_u64), }), @@ -690,31 +689,29 @@ mod tests { }), None, ); + let transaction_fee_balance_exactly_required_minor: u128 = { + let base_value = (100 * 6 * 53_000) as u128; + let with_margin = TRANSACTION_FEE_MARGIN.add_percent_to(base_value); + gwei_to_wei(with_margin) + }; // Transaction fee balance > payments let input_3 = make_input_for_initial_check_tests( None, Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 100, + gas_price_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_per_transaction: 53_000, - cw_transaction_fee_balance_major: { - let base_value = 100 * 6 * 53_000; - let exact_equality = TRANSACTION_FEE_MARGIN.add_percent_to(base_value); - exact_equality + 1 - }, + cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor + 1, }), ); // Transaction fee balance == payments let input_4 = make_input_for_initial_check_tests( None, Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 100, + gas_price_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_per_transaction: 53_000, - cw_transaction_fee_balance_major: { - let base_value = 100 * 6 * 53_000; - TRANSACTION_FEE_MARGIN.add_percent_to(base_value) - }, + cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor, }), ); @@ -736,24 +733,23 @@ mod tests { #[test] fn consider_adjustment_sad_path_for_transaction_fee() { init_test_logging(); - let test_name = "consider_adjustment_sad_path_positive_for_transaction_fee"; + let test_name = "consider_adjustment_sad_path_for_transaction_fee"; let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); let number_of_accounts = 3; - let service_fee_balances_config_opt = None; let (qualified_payables, agent) = make_input_for_initial_check_tests( - service_fee_balances_config_opt, + None, Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 100, + gas_price_major: 100, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: (((100 * 3 * 55_000) * 115) / 100) - 1, + cw_transaction_fee_balance_minor: TRANSACTION_FEE_MARGIN.add_percent_to(gwei_to_wei::(100 * 3 * 55_000)) - 1, }), ); - let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.consider_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); + let analyzed_payables = convert_collection(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -765,7 +761,7 @@ mod tests { ); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Transaction fee balance of 18,974,999,000,000,000 wei cannot cover \ + "WARN: {test_name}: Transaction fee balance of 18,974,999,999,999,999 wei cannot cover \ the anticipated 18,975,000,000,000,000 wei for 3 transactions. Maximal count is set to 2. \ Adjustment must be performed." )); @@ -786,17 +782,17 @@ mod tests { let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Right(vec![ - gwei_to_wei::(85), - gwei_to_wei::(15) + 1, + gwei_to_wei(85_u64), + gwei_to_wei::(15) + 1, ]), cw_balance_minor: gwei_to_wei(100_u64), }), None, ); - let analyzed_payables = convert_collection(qualified_payables.clone()); - let result = subject.consider_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); + let analyzed_payables = convert_collection(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -818,27 +814,33 @@ mod tests { } #[test] - fn transaction_fee_balance_is_unbearably_low_but_service_fee_balance_is_fine() { + fn service_fee_balance_is_fine_but_transaction_fee_balance_throws_error() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; + let tx_fee_exactly_required_for_single_tx = { + let base_minor = gwei_to_wei::(55_000 * 100); + TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) + }; + let cw_transaction_fee_balance_minor = tx_fee_exactly_required_for_single_tx - 1; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Left(vec![123]), - cw_balance_minor: gwei_to_wei::(444), + cw_balance_minor: gwei_to_wei(444_u64), }), Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 100, + gas_price_major: 100, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: 54_000 * 100, + cw_transaction_fee_balance_minor, }), ); let result = subject.consider_adjustment(qualified_payables, &*agent); - let per_transaction_requirement_minor = - TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(100)); - let cw_transaction_fee_balance_minor = U256::from(54_000 * gwei_to_wei::(100)); + let per_transaction_requirement_minor = { + let base_minor = gwei_to_wei::(55_000 * 100); + TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) + }; assert_eq!( result, Err( @@ -846,7 +848,7 @@ mod tests { number_of_accounts, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { per_transaction_requirement_minor, - cw_transaction_fee_balance_minor, + cw_transaction_fee_balance_minor: cw_transaction_fee_balance_minor.into(), }), service_fee_opt: None } @@ -855,16 +857,20 @@ mod tests { } #[test] - fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low() + fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error() { - let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_is_unbearably_low"; - let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; // this would normally kick a serious error + let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error"; + let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; + nmnnbnmbn + // TODO BIG TROUBLES, fix me let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { account_balances: Either::Left(vec![120, 300, 500]), cw_balance_minor: cw_service_fee_balance_minor, }); let (qualified_payables, agent) = make_input_for_initial_check_tests(service_fee_balances_config_opt, None); + let analyzed_accounts: Vec = convert_collection(qualified_payables); + let minimal_disqualification_limit = analyzed_accounts.iter().map(|account|account.disqualification_limit_minor).min(); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); @@ -886,7 +892,7 @@ mod tests { } #[test] - fn both_balances_are_unbearably_low() { + fn both_balances_are_not_enough_even_for_single_transaction() { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 2; let (qualified_payables, agent) = make_input_for_initial_check_tests( @@ -895,10 +901,10 @@ mod tests { cw_balance_minor: 0, }), Some(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 123, + gas_price_major: 123, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: 0, + cw_transaction_fee_balance_minor: 0, }), ); @@ -1216,7 +1222,7 @@ mod tests { let cw_service_fee_balance_minor = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; let agent_for_analysis = BlockchainAgentMock::default() - .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) + .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(12356); @@ -1956,10 +1962,10 @@ mod tests { } struct TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: u64, + gas_price_major: u64, number_of_accounts: usize, estimated_transaction_fee_units_per_transaction: u64, - cw_transaction_fee_balance_major: u64, + cw_transaction_fee_balance_minor: u128, } fn make_input_for_initial_check_tests( @@ -1983,9 +1989,9 @@ mod tests { let qualified_payables = prepare_qualified_payables(payable_accounts); let blockchain_agent = make_agent( - transaction_fee_config.cw_transaction_fee_balance_major, + transaction_fee_config.cw_transaction_fee_balance_minor, transaction_fee_config.estimated_transaction_fee_units_per_transaction, - transaction_fee_config.agreed_transaction_fee_per_computed_unit_major, + transaction_fee_config.gas_price_major, service_fee_balances_config.cw_balance_minor, ); @@ -2015,10 +2021,10 @@ mod tests { accounts_count_from_sf_config: usize, ) -> TestConfigForTransactionFees { transaction_fee_config_opt.unwrap_or(TestConfigForTransactionFees { - agreed_transaction_fee_per_computed_unit_major: 120, + gas_price_major: 120, number_of_accounts: accounts_count_from_sf_config, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_major: u64::MAX, + cw_transaction_fee_balance_minor: u128::MAX, }) } @@ -2063,19 +2069,18 @@ mod tests { } fn make_agent( - cw_balance_transaction_fee_major: u64, + cw_transaction_fee_minor: u128, estimated_transaction_fee_units_per_transaction: u64, - agreed_transaction_fee_price: u64, + gas_price: u64, cw_service_fee_balance_minor: u128, ) -> Box { - let cw_transaction_fee_minor = gwei_to_wei(cw_balance_transaction_fee_major); let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( - estimated_transaction_fee_units_per_transaction * agreed_transaction_fee_price, + estimated_transaction_fee_units_per_transaction * gas_price, ); let blockchain_agent = BlockchainAgentMock::default() - .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) - .transaction_fee_balance_minor_result(cw_transaction_fee_minor) + .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) + .transaction_fee_balance_minor_result(cw_transaction_fee_minor.into()) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .estimated_transaction_fee_per_transaction_minor_result( estimated_transaction_fee_per_transaction_minor, @@ -2092,8 +2097,8 @@ mod tests { let panic_msg = err.downcast_ref::().unwrap(); assert_eq!( panic_msg, - "Broken code: Called the null implementation of \ - the original_cw_service_fee_balance_minor() method in PaymentAdjusterInner" + "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ + original_cw_service_fee_balance_minor()" ) } diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 636bfa43a..d512afa29 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -443,7 +443,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) - .agreed_transaction_fee_margin_result(PurePercentage::try_from(15).unwrap()) + .gas_price_margin_result(PurePercentage::try_from(15).unwrap()) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index efb89cdf8..3ba37f2f5 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -44,12 +44,12 @@ impl PreparatoryAnalyzer { let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); let per_transaction_requirement_minor = agent.estimated_transaction_fee_per_transaction_minor(); - let agreed_transaction_fee_margin = agent.agreed_transaction_fee_margin(); + let gas_price_margin = agent.gas_price_margin(); let transaction_fee_check_result = self .determine_transaction_count_limit_by_transaction_fee( cw_transaction_fee_balance_minor, - agreed_transaction_fee_margin, + gas_price_margin, per_transaction_requirement_minor, number_of_accounts, logger, @@ -165,13 +165,13 @@ impl PreparatoryAnalyzer { fn determine_transaction_count_limit_by_transaction_fee( &self, cw_transaction_fee_balance_minor: U256, - agreed_transaction_fee_margin: PurePercentage, + gas_price_margin: PurePercentage, per_transaction_requirement_minor: u128, number_of_qualified_accounts: usize, logger: &Logger, ) -> Result, TransactionFeeImmoderateInsufficiency> { let per_txn_requirement_minor_with_margin = - agreed_transaction_fee_margin.add_percent_to(per_transaction_requirement_minor); + gas_price_margin.add_percent_to(per_transaction_requirement_minor); let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, @@ -410,7 +410,7 @@ mod tests { DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; let blockchain_agent = BlockchainAgentMock::default() - .agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN) + .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(123456) .service_fee_balance_minor_result(cw_service_fee_balance); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index d9f23e876..7d8c85cfc 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -33,8 +33,8 @@ impl BlockchainAgent for BlockchainAgentNull { 0 } - fn agreed_transaction_fee_margin(&self) -> PurePercentage { - self.log_function_call("agreed_transaction_fee_margin()"); + fn gas_price_margin(&self) -> PurePercentage { + self.log_function_call("gas_price_margin()"); PurePercentage::try_from(0).expect("0 should cause no issue") } @@ -176,16 +176,16 @@ mod tests { } #[test] - fn null_agent_agreed_transaction_fee_margin() { + fn null_agent_gas_price_margin() { init_test_logging(); - let test_name = "null_agent_agreed_transaction_fee_margin"; + let test_name = "null_agent_gas_price_margin"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.agreed_transaction_fee_margin(); + let result = subject.gas_price_margin(); assert_eq!(result, PurePercentage::try_from(0).unwrap()); - assert_error_log(test_name, "agreed_transaction_fee_margin") + assert_error_log(test_name, "gas_price_margin") } #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 45185ef34..959fc29bb 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -14,7 +14,7 @@ use web3::types::U256; pub struct BlockchainAgentWeb3 { gas_price_gwei: u64, gas_limit_const_part: u64, - agreed_transaction_fee_margin: PurePercentage, + gas_price_margin: PurePercentage, maximum_added_gas_margin: u64, consuming_wallet: Wallet, consuming_wallet_balances: ConsumingWalletBalances, @@ -42,8 +42,8 @@ impl BlockchainAgent for BlockchainAgentWeb3 { self.gas_price_gwei } - fn agreed_transaction_fee_margin(&self) -> PurePercentage { - self.agreed_transaction_fee_margin + fn gas_price_margin(&self) -> PurePercentage { + self.gas_price_margin } fn consuming_wallet(&self) -> &Wallet { @@ -67,12 +67,12 @@ impl BlockchainAgentWeb3 { consuming_wallet_balances: ConsumingWalletBalances, pending_transaction_id: U256, ) -> Self { - let agreed_transaction_fee_margin = *TRANSACTION_FEE_MARGIN; + let gas_price_margin = *TRANSACTION_FEE_MARGIN; let maximum_added_gas_margin = WEB3_MAXIMAL_GAS_LIMIT_MARGIN; Self { gas_price_gwei, gas_limit_const_part, - agreed_transaction_fee_margin, + gas_price_margin, consuming_wallet, maximum_added_gas_margin, consuming_wallet_balances, diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index 0ab3bdaed..19b449886 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -26,7 +26,7 @@ pub trait BlockchainAgent: Send { fn transaction_fee_balance_minor(&self) -> U256; fn service_fee_balance_minor(&self) -> u128; fn agreed_fee_per_computation_unit(&self) -> u64; - fn agreed_transaction_fee_margin(&self) -> PurePercentage; + fn gas_price_margin(&self) -> PurePercentage; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index dc6cc49d4..49d125edc 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -16,7 +16,7 @@ pub struct BlockchainAgentMock { transaction_fee_balance_minor_results: RefCell>, service_fee_balance_minor_results: RefCell>, agreed_fee_per_computation_unit_results: RefCell>, - agreed_transaction_fee_margin: RefCell>, + gas_price_margin: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, arbitrary_id_stamp_opt: Option, @@ -47,8 +47,8 @@ impl BlockchainAgent for BlockchainAgentMock { .remove(0) } - fn agreed_transaction_fee_margin(&self) -> PurePercentage { - self.agreed_transaction_fee_margin.borrow_mut().remove(0) + fn gas_price_margin(&self) -> PurePercentage { + self.gas_price_margin.borrow_mut().remove(0) } fn consuming_wallet(&self) -> &Wallet { @@ -95,8 +95,8 @@ impl BlockchainAgentMock { self } - pub fn agreed_transaction_fee_margin_result(self, result: PurePercentage) -> Self { - self.agreed_transaction_fee_margin.borrow_mut().push(result); + pub fn gas_price_margin_result(self, result: PurePercentage) -> Self { + self.gas_price_margin.borrow_mut().push(result); self } 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 1f390fe95..5bc4d315d 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -1062,7 +1062,7 @@ mod tests { ); assert_eq!(result.service_fee_balance_minor(), masq_balance.as_u128()); assert_eq!( - result.agreed_transaction_fee_margin(), + result.gas_price_margin(), PurePercentage::try_from(15).unwrap() ); assert_eq!(result.agreed_fee_per_computation_unit(), 50); From bf272f5bd11e4eba06b9f435d9ccd27c6235a760 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 10 Nov 2024 01:22:13 +0100 Subject: [PATCH 216/250] GH-711-review-one: new fixes in the main mod --- node/src/accountant/mod.rs | 2 +- .../balance_calculator.rs | 6 +- .../disqualification_arbiter.rs | 14 +- node/src/accountant/payment_adjuster/mod.rs | 519 ++++++++++-------- .../payment_adjuster/non_unit_tests/mod.rs | 12 +- .../preparatory_analyser/mod.rs | 10 +- .../payment_adjuster/service_fee_adjuster.rs | 38 +- .../accountant/payment_adjuster/test_utils.rs | 101 +++- 8 files changed, 400 insertions(+), 302 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 049c2fff3..c5779f5e3 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1784,7 +1784,7 @@ mod tests { Adjustment::ByServiceFee, vec![make_meaningless_analyzed_account(123)], )))) - .adjust_payments_result(Err(PaymentAdjusterError::AllAccountsEliminated)); + .adjust_payments_result(Err(PaymentAdjusterError::RecursionDrainedAllAccounts)); test_payment_adjuster_error_during_different_stages(test_name, payment_adjuster); diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index db9f4cfc8..bd808301d 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -34,7 +34,7 @@ mod tests { use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; - use crate::accountant::payment_adjuster::test_utils::multiple_by_billion; + use crate::accountant::payment_adjuster::test_utils::multiply_by_billion; use crate::accountant::test_utils::make_meaningless_analyzed_account; use std::time::SystemTime; @@ -56,10 +56,10 @@ mod tests { .map(|(idx, n)| { let mut basic_analyzed_payable = make_meaningless_analyzed_account(idx as u64); basic_analyzed_payable.qualified_as.bare_account.balance_wei = - multiple_by_billion(n); + multiply_by_billion(n); basic_analyzed_payable .qualified_as - .payment_threshold_intercept_minor = multiple_by_billion(2) * (idx as u128 + 1); + .payment_threshold_intercept_minor = multiply_by_billion(2) * (idx as u128 + 1); basic_analyzed_payable }) .collect::>(); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index a29ea4a1e..d930c2faa 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -217,7 +217,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ - make_initialized_subject, make_non_guaranteed_unconfirmed_adjustment, + make_non_guaranteed_unconfirmed_adjustment, PaymentAdjusterTestBuilder, }; use crate::accountant::test_utils::make_qualified_payables; use crate::sub_lib::accountant::PaymentThresholds; @@ -464,13 +464,11 @@ mod tests { let qualified_payables = make_qualified_payables(accounts, &payment_thresholds, now); let analyzed_accounts = convert_collection(qualified_payables); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); - let payment_adjuster = make_initialized_subject( - Some(now), - Some(cw_service_fee_balance_minor), - None, - Some(largest_exceeding_balance), - None, - ); + let payment_adjuster = PaymentAdjusterTestBuilder::default() + .now(now) + .cw_service_fee_balance_minor(cw_service_fee_balance_minor) + .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) + .build(); let weights_and_accounts = payment_adjuster.calculate_weights(analyzed_accounts); let subject = DisqualificationArbiter::default(); let unconfirmed_adjustments = AdjustmentComputer::default() diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 9c3176911..b77542134 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -169,8 +169,8 @@ impl PaymentAdjusterReal { ) { let transaction_fee_limitation_opt = match required_adjustment { Adjustment::BeginByTransactionFee { - affordable_transaction_count, - } => Some(affordable_transaction_count), + transaction_count_limit, + } => Some(transaction_count_limit), Adjustment::ByServiceFee => None, }; @@ -196,7 +196,7 @@ impl PaymentAdjusterReal { let processed_accounts = self.resolve_initial_adjustment_dispatch(weighted_accounts)?; if no_affordable_accounts_found(&processed_accounts) { - return Err(PaymentAdjusterError::AllAccountsEliminated); + return Err(PaymentAdjusterError::RecursionDrainedAllAccounts); } match processed_accounts { @@ -244,10 +244,8 @@ impl PaymentAdjusterReal { let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); - let weighted_accounts_affordable_by_transaction_fee = eliminate_accounts_by_tx_fee_limit( - weighed_accounts, - transaction_count_limit, - ); + let weighted_accounts_affordable_by_transaction_fee = + eliminate_accounts_by_tx_fee_limit(weighed_accounts, transaction_count_limit); let cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); @@ -433,7 +431,7 @@ impl PaymentAdjusterReal { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Adjustment { ByServiceFee, - BeginByTransactionFee { affordable_transaction_count: u16 }, + BeginByTransactionFee { transaction_count_limit: u16 }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -464,7 +462,7 @@ pub enum PaymentAdjusterError { original_service_fee_required_total_minor: u128, cw_service_fee_balance_minor: u128, }, - AllAccountsEliminated, + RecursionDrainedAllAccounts, } #[derive(Debug, PartialEq, Eq)] @@ -484,7 +482,7 @@ impl PaymentAdjusterError { match self { PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => true, PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => true, - PaymentAdjusterError::AllAccountsEliminated => true, + PaymentAdjusterError::RecursionDrainedAllAccounts => true, // We haven't needed to worry in this matter yet, this is rather a future alarm that // will draw attention after somebody adds a possibility for an error not necessarily // implying that an insolvency was detected before. At the moment, each error occurs @@ -552,10 +550,10 @@ impl Display for PaymentAdjusterError { original_service_fee_required_total_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() ), - PaymentAdjusterError::AllAccountsEliminated => write!( + PaymentAdjusterError::RecursionDrainedAllAccounts => write!( f, - "The adjustment algorithm had to eliminate each payable from the recently urged \ - payment due to lack of resources." + "The payment adjuster wasn't able to compose any combination of payables that can \ + be paid immediately with provided finances." ), } } @@ -573,9 +571,8 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ - make_extreme_payables, make_initialized_subject, - make_meaningless_analyzed_account_by_wallet, multiple_by_billion, CriterionCalculatorMock, - DisqualificationGaugeMock, ServiceFeeAdjusterMock, + make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, + CriterionCalculatorMock, PaymentAdjusterTestBuilder, ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ @@ -590,7 +587,9 @@ mod tests { make_analyzed_payables, make_meaningless_analyzed_account, make_payable_account, make_qualified_payables, }; - use crate::accountant::{gwei_to_wei, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, AnalyzedPayableAccount}; + use crate::accountant::{ + AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, + }; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -608,8 +607,10 @@ mod tests { use web3::types::U256; #[test] - #[should_panic(expected = "The PaymentAdjuster Inner is uninitialised. It was detected while \ - executing unallocated_cw_service_fee_balance_minor()")] + #[should_panic( + expected = "The PaymentAdjuster Inner is uninitialised. It was detected while \ + executing unallocated_cw_service_fee_balance_minor()" + )] fn payment_adjuster_new_is_created_with_inner_null() { let subject = PaymentAdjusterReal::new(); @@ -654,11 +655,11 @@ mod tests { } #[test] - fn initialize_inner_processes_works() { + fn initialize_inner_works() { test_initialize_inner_works(Adjustment::ByServiceFee, None); test_initialize_inner_works( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 5, + transaction_count_limit: 5, }, Some(5), ); @@ -674,10 +675,10 @@ mod tests { let input_1 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Right(vec![ - gwei_to_wei(85_u64), - gwei_to_wei::(15) - 1, + multiply_by_billion(85), + multiply_by_billion(15) - 1, ]), - cw_balance_minor: gwei_to_wei(100_u64), + cw_balance_minor: multiply_by_billion(100), }), None, ); @@ -685,14 +686,14 @@ mod tests { let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Left(vec![85, 15]), - cw_balance_minor: gwei_to_wei(100_u64), + cw_balance_minor: multiply_by_billion(100), }), None, ); let transaction_fee_balance_exactly_required_minor: u128 = { let base_value = (100 * 6 * 53_000) as u128; let with_margin = TRANSACTION_FEE_MARGIN.add_percent_to(base_value); - gwei_to_wei(with_margin) + multiply_by_billion(with_margin) }; // Transaction fee balance > payments let input_3 = make_input_for_initial_check_tests( @@ -701,7 +702,8 @@ mod tests { gas_price_major: 100, number_of_accounts: 6, estimated_transaction_fee_units_per_transaction: 53_000, - cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor + 1, + cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor + + 1, }), ); // Transaction fee balance == payments @@ -743,7 +745,9 @@ mod tests { gas_price_major: 100, number_of_accounts, estimated_transaction_fee_units_per_transaction: 55_000, - cw_transaction_fee_balance_minor: TRANSACTION_FEE_MARGIN.add_percent_to(gwei_to_wei::(100 * 3 * 55_000)) - 1, + cw_transaction_fee_balance_minor: TRANSACTION_FEE_MARGIN + .add_percent_to(multiply_by_billion(100 * 3 * 55_000)) + - 1, }), ); @@ -754,7 +758,7 @@ mod tests { result, Ok(Either::Right(AdjustmentAnalysisReport::new( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 2 + transaction_count_limit: 2 }, analyzed_payables ))) @@ -782,10 +786,10 @@ mod tests { let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Right(vec![ - gwei_to_wei(85_u64), - gwei_to_wei::(15) + 1, + multiply_by_billion(85), + multiply_by_billion(15) + 1, ]), - cw_balance_minor: gwei_to_wei(100_u64), + cw_balance_minor: multiply_by_billion(100), }), None, ); @@ -818,14 +822,14 @@ mod tests { let subject = PaymentAdjusterReal::new(); let number_of_accounts = 3; let tx_fee_exactly_required_for_single_tx = { - let base_minor = gwei_to_wei::(55_000 * 100); + let base_minor = multiply_by_billion(55_000 * 100); TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) }; let cw_transaction_fee_balance_minor = tx_fee_exactly_required_for_single_tx - 1; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { account_balances: Either::Left(vec![123]), - cw_balance_minor: gwei_to_wei(444_u64), + cw_balance_minor: multiply_by_billion(444), }), Some(TestConfigForTransactionFees { gas_price_major: 100, @@ -838,7 +842,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = { - let base_minor = gwei_to_wei::(55_000 * 100); + let base_minor = multiply_by_billion(55_000 * 100); TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) }; assert_eq!( @@ -857,24 +861,33 @@ mod tests { } #[test] - fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error() - { + fn checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error() { let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error"; - let cw_service_fee_balance_minor = gwei_to_wei::(120_u64) / 2 - 1; - nmnnbnmbn - // TODO BIG TROUBLES, fix me + let garbage_cw_service_fee_balance = u128::MAX; let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { account_balances: Either::Left(vec![120, 300, 500]), - cw_balance_minor: cw_service_fee_balance_minor, + cw_balance_minor: garbage_cw_service_fee_balance, }); - let (qualified_payables, agent) = + let (qualified_payables, boxed_agent) = make_input_for_initial_check_tests(service_fee_balances_config_opt, None); - let analyzed_accounts: Vec = convert_collection(qualified_payables); - let minimal_disqualification_limit = analyzed_accounts.iter().map(|account|account.disqualification_limit_minor).min(); + let analyzed_accounts: Vec = + convert_collection(qualified_payables.clone()); + let minimal_disqualification_limit = analyzed_accounts + .iter() + .map(|account| account.disqualification_limit_minor) + .min() + .unwrap(); + // Condition for the error to be thrown + let actual_insufficient_cw_service_fee_balance = minimal_disqualification_limit - 1; + let agent_accessible = reconstruct_mock_agent(boxed_agent); + // Dropping the garbage value on the floor + let _ = agent_accessible.service_fee_balance_minor(); + let agent = agent_accessible + .service_fee_balance_minor_result(actual_insufficient_cw_service_fee_balance); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let result = subject.consider_adjustment(qualified_payables, &*agent); + let result = subject.consider_adjustment(qualified_payables, &agent); assert_eq!( result, @@ -884,7 +897,7 @@ mod tests { transaction_fee_opt: None, service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { total_service_fee_required_minor: 920_000_000_000, - cw_service_fee_balance_minor + cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance }) } ) @@ -911,7 +924,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = - TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::(123)); + TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * multiply_by_billion(123)); assert_eq!( result, Err( @@ -922,7 +935,7 @@ mod tests { cw_transaction_fee_balance_minor: U256::zero(), }), service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: multiple_by_billion(500), + total_service_fee_required_minor: multiply_by_billion(500), cw_service_fee_balance_minor: 0 }) } @@ -988,9 +1001,9 @@ mod tests { required amount of service fee: 1,234,567,891,011 wei, while the wallet contains \ 333,333 wei."), ( - PaymentAdjusterError::AllAccountsEliminated, - "The adjustment algorithm had to eliminate each payable from the recently urged \ - payment due to lack of resources.", + PaymentAdjusterError::RecursionDrainedAllAccounts, + "The payment adjuster wasn't able to compose any combination of payables that can \ + be paid immediately with provided finances.", ), ]; let inputs_count = inputs.len(); @@ -1017,7 +1030,7 @@ mod tests { #[test] fn we_can_say_if_error_occurred_after_insolvency_was_detected() { let inputs = vec![ - PaymentAdjusterError::AllAccountsEliminated, + PaymentAdjusterError::RecursionDrainedAllAccounts, PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 0, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { @@ -1062,41 +1075,32 @@ mod tests { } #[test] - fn tinier_but_larger_in_weight_account_is_prioritized_and_gains_up_to_its_disqualification_limit( + fn adjusted_balance_threats_to_outgrow_the_original_account_but_is_capped_by_disqualification_limit( ) { - let cw_service_fee_balance_minor = multiple_by_billion(4_200_000); - let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); + let cw_service_fee_balance_minor = multiply_by_billion(4_200_000); let mut account_1 = make_meaningless_analyzed_account_by_wallet("abc"); - let balance_1 = multiple_by_billion(3_000_000); - let disqualification_limit_1 = multiple_by_billion(2_300_000); + let balance_1 = multiply_by_billion(3_000_000); + let disqualification_limit_1 = multiply_by_billion(2_300_000); account_1.qualified_as.bare_account.balance_wei = balance_1; account_1.disqualification_limit_minor = disqualification_limit_1; + let weight_account_1 = multiply_by_billion(2_000_100); let mut account_2 = make_meaningless_analyzed_account_by_wallet("def"); let wallet_2 = account_2.qualified_as.bare_account.wallet.clone(); - let balance_2 = multiple_by_billion(2_500_000); - let disqualification_limit_2 = multiple_by_billion(1_800_000); + let balance_2 = multiply_by_billion(2_500_000); + let disqualification_limit_2 = multiply_by_billion(1_800_000); account_2.qualified_as.bare_account.balance_wei = balance_2; account_2.disqualification_limit_minor = disqualification_limit_2; + let weighed_account_2 = multiply_by_billion(3_999_900); let largest_exceeding_balance = (balance_1 - account_1.qualified_as.payment_threshold_intercept_minor) .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); - let mut subject = make_initialized_subject( - None, - Some(cw_service_fee_balance_minor), - None, - Some(largest_exceeding_balance), - None, - ); - let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(disqualification_limit_2) - .determine_limit_result(disqualification_limit_1) - .determine_limit_result(disqualification_limit_1) - .determine_limit_params(&determine_limit_params_arc); - subject.disqualification_arbiter = - DisqualificationArbiter::new(Box::new(disqualification_gauge)); + let mut subject = PaymentAdjusterTestBuilder::default() + .cw_service_fee_balance_minor(cw_service_fee_balance_minor) + .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) + .build(); let weighted_payables = vec![ - WeightedPayable::new(account_1, multiple_by_billion(2_000_100)), - WeightedPayable::new(account_2, multiple_by_billion(3_999_900)), + WeightedPayable::new(account_1, weight_account_1), + WeightedPayable::new(account_2, weighed_account_2), ]; let mut result = subject @@ -1105,44 +1109,41 @@ mod tests { .left() .unwrap(); - // Let's have an example to explain why this test is important. - prove_that_proposed_adjusted_balance_could_have_exceeded_the_original_value( + // This shows how the weights can turn tricky for which it's important to have a hard upper + // limit, chosen quite down, as the disqualification limit, for optimisation. In its + // extremity, the naked algorithm of the reallocation of funds could have granted a value + // above the original debt size, which is clearly unfair. + illustrate_that_we_need_to_prevent_exceeding_the_original_value( subject, cw_service_fee_balance_minor, weighted_payables.clone(), wallet_2, balance_2, ); - // So the assertion above showed the concern true. + let payable_account_1 = &weighted_payables[0] + .analyzed_account + .qualified_as + .bare_account; + let payable_account_2 = &weighted_payables[1] + .analyzed_account + .qualified_as + .bare_account; let first_returned_account = result.remove(0); - // Outweighed accounts always take the first places - assert_eq!( - &first_returned_account.original_account, - &weighted_payables[1] - .analyzed_account - .qualified_as - .bare_account - ); + assert_eq!(&first_returned_account.original_account, payable_account_2); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, disqualification_limit_2 ); let second_returned_account = result.remove(0); - assert_eq!( - &second_returned_account.original_account, - &weighted_payables[0] - .analyzed_account - .qualified_as - .bare_account - ); + assert_eq!(&second_returned_account.original_account, payable_account_1); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, - 2_300_000_000_000_000 + disqualification_limit_1 ); assert!(result.is_empty()); } - fn prove_that_proposed_adjusted_balance_could_have_exceeded_the_original_value( + fn illustrate_that_we_need_to_prevent_exceeding_the_original_value( mut subject: PaymentAdjusterReal, cw_service_fee_balance_minor: u128, weighted_accounts: Vec, @@ -1163,16 +1164,12 @@ mod tests { unconfirmed_adjustments[1].wallet(), &wallet_of_expected_outweighed ); - // The weight of this account grew progressively due to the additional criterion added - // in to the sum. Consequences would've been that redistribution of the adjusted balances - // would've attributed this account with a larger amount to pay than it would've - // contained before the test started. To prevent that, we used to secure a rule that - // an account could never demand more than 100% of itself. - // - // Later it was changed to other policy. so called "outweighed" account gains automatically - // a balance equal to its disqualification limit, also a prominent front position in - // the resulting set of the accounts to pay out. Additionally, due to its favorable position, - // it can be given a bit more from the remains still languishing in the consuming wallet. + // To prevent unjust reallocation we used to secure a rule an account could never demand + // more than 100% of its size. + + // Later it was changed to a different policy, so called "outweighed" account gains + // automatically a balance equal to its disqualification limit. Still, later on it's very + // likely to be given a bit more from the remains languishing in the consuming wallet. let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), @@ -1187,38 +1184,45 @@ mod tests { fn adjustment_started_but_all_accounts_were_eliminated_anyway() { let test_name = "adjustment_started_but_all_accounts_were_eliminated_anyway"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(3_000_000); + // This simplifies the overall picture, the debt age doesn't mean anything to our calculator, + // still, it influences the height of the intercept point read out from the payment thresholds + // which can induce an impact on the value of the disqualification limit which is derived + // from the intercept + let common_unimportant_age_for_accounts = + now.checked_sub(Duration::from_secs(200_000)).unwrap(); + let balance_1 = multiply_by_billion(3_000_000); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), + last_paid_timestamp: common_unimportant_age_for_accounts, pending_payable_opt: None, }; - let balance_2 = multiple_by_billion(2_000_000); + let balance_2 = multiply_by_billion(2_000_000); let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(50_000)).unwrap(), + last_paid_timestamp: common_unimportant_age_for_accounts, pending_payable_opt: None, }; - let balance_3 = multiple_by_billion(5_000_000); + let balance_3 = multiply_by_billion(5_000_000); let account_3 = PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(), + last_paid_timestamp: common_unimportant_age_for_accounts, pending_payable_opt: None, }; let payables = vec![account_1, account_2, account_3]; let qualified_payables = make_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(2_000_000_000)) + .calculate_result(multiply_by_billion(2_000_000_000)) .calculate_result(0) .calculate_result(0); - let mut subject = PaymentAdjusterReal::new(); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .logger(Logger::new(test_name)) + .build(); subject.calculators.push(Box::new(calculator_mock)); - subject.logger = Logger::new(test_name); - let agent_id_stamp = ArbitraryIdStamp::new(); let cw_service_fee_balance_minor = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; let agent_for_analysis = BlockchainAgentMock::default() @@ -1232,14 +1236,25 @@ mod tests { qualified_payables, &subject.logger, ); - // If the initial analysis at the entry into the PaymentAdjuster concludes there is no point - // going off because even the least demanding account could not be satisfied, and we would - // get an error here. - // However, it can only assess the lowest disqualification limit of an account in that set. - // Probably not as usual, but this particular account can be later outplayed by another one - // that is equipped with some extra significance while its disqualification limit does not - // fit inder the consuming wallet balance anymore. A late error, possibly two different, is - // born. + // The initial intelligent check that PA runs can feel out if the hypothetical adjustment + // would have some minimal chance to complete successfully. Still, this aspect of it is + // rather a weak spot, as the only guarantee it sets on works for an assurance that at + // least the smallest account, with its specific disqualification limit, can be fulfilled + // by the available funds. + // In this test it would be a yes there. There's even a surplus in case of the second + // account. + // Then the adjustment itself spins off. The accounts get their weights. The second one as + // to its lowest size should be granted a big one, wait until the other two are eliminated + // by the recursion and win for the scarce money as paid in the full scale. + // Normally, what was said would hold true. The big difference is caused by an extra, + // actually made up, parameter which comes in with the mock calculator stuck in to join + // the others. It changes the distribution of weights among those three accounts and makes + // the first account be the most important one. Because of that two other accounts are + // eliminated, the account three first, and then the account two. + // When we look back to the preceding entry check, the minimal condition was exercised on + // the account two, because at that time the weights hadn't been known yet. As the result, + // the recursion will continue to even eliminate the last account, the account one, for + // which there isn't enough money to get over its disqualification limit. let adjustment_analysis = match analysis_result { Ok(Either::Right(analysis)) => analysis, x => panic!( @@ -1247,12 +1262,10 @@ mod tests { x ), }; - let agent = { - let mock = BlockchainAgentMock::default() - .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(cw_service_fee_balance_minor); - Box::new(mock) - }; + let agent = Box::new( + BlockchainAgentMock::default() + .service_fee_balance_minor_result(cw_service_fee_balance_minor), + ); let adjustment_setup = PreparedAdjustment { agent, response_skeleton_opt: None, @@ -1268,7 +1281,7 @@ mod tests { ok.affordable_accounts ), }; - assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated) + assert_eq!(err, PaymentAdjusterError::RecursionDrainedAllAccounts) } #[test] @@ -1281,25 +1294,31 @@ mod tests { init_test_logging(); let test_name = "account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(80_000_000_000); + // This simplifies the overall picture, the debt age doesn't mean anything to our calculator, + // still, it influences the height of the intercept point read out from the payment thresholds + // which can induce an impact on the value of the disqualification limit which is derived + // from the intercept + let common_age_for_accounts_as_unimportant = + now.checked_sub(Duration::from_secs(200_000)).unwrap(); + let balance_1 = multiply_by_billion(80_000_000_000); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, - last_paid_timestamp: now.checked_sub(Duration::from_secs(24_000)).unwrap(), + last_paid_timestamp: common_age_for_accounts_as_unimportant, pending_payable_opt: None, }; - let balance_2 = multiple_by_billion(60_000_000_000); + let balance_2 = multiply_by_billion(60_000_000_000); let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, - last_paid_timestamp: now.checked_sub(Duration::from_secs(200_000)).unwrap(), + last_paid_timestamp: common_age_for_accounts_as_unimportant, pending_payable_opt: None, }; - let balance_3 = multiple_by_billion(40_000_000_000); + let balance_3 = multiply_by_billion(40_000_000_000); let account_3 = PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, - last_paid_timestamp: now.checked_sub(Duration::from_secs(160_000)).unwrap(), + last_paid_timestamp: common_age_for_accounts_as_unimportant, pending_payable_opt: None, }; let payables = vec![account_1, account_2.clone(), account_3.clone()]; @@ -1307,14 +1326,15 @@ mod tests { make_analyzed_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(0) - .calculate_result(multiple_by_billion(50_000_000_000)) - .calculate_result(multiple_by_billion(50_000_000_000)); - let mut subject = PaymentAdjusterReal::new(); + .calculate_result(multiply_by_billion(50_000_000_000)) + .calculate_result(multiply_by_billion(50_000_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .logger(Logger::new(test_name)) + .build(); subject.calculators.push(Box::new(calculator_mock)); - subject.logger = Logger::new(test_name); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum: u128 = balance_1 + balance_2 + balance_3; - let service_fee_balance_in_minor_units = accounts_sum - ((balance_1 * 90) / 100); + let service_fee_balance_in_minor_units = balance_2 + balance_3 + ((balance_1 * 10) / 100); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1339,16 +1359,17 @@ mod tests { } #[test] - fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() { + fn overloaded_by_mammoth_debts_to_see_if_we_can_pass_through_without_blowing_up() { init_test_logging(); let test_name = - "overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely"; + "overloaded_by_mammoth_debts_to_see_if_we_can_pass_through_without_blowing_up"; let now = SystemTime::now(); // Each of the 3 accounts refers to a debt sized as the entire MASQ token supply and being - // 10 years old which generates enormously large numbers in the criteria + // 10 years old which generates enormously large numbers in the algorithm, especially for + // the calculated criteria of over accounts let extreme_payables = { let debt_age_in_months = vec![120, 120, 120]; - make_extreme_payables( + make_mammoth_payables( Either::Left(( debt_age_in_months, *MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, @@ -1361,10 +1382,10 @@ mod tests { let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); // In turn, tiny cw balance - let cw_service_fee_balance = 1_000; + let cw_service_fee_balance_minor = 1_000; let agent = { let mock = BlockchainAgentMock::default() - .service_fee_balance_minor_result(cw_service_fee_balance); + .service_fee_balance_minor_result(cw_service_fee_balance_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1383,7 +1404,7 @@ mod tests { Ok(_) => panic!("we expected err but got ok"), Err(e) => e, }; - assert_eq!(err, PaymentAdjusterError::AllAccountsEliminated); + assert_eq!(err, PaymentAdjusterError::RecursionDrainedAllAccounts); let expected_log = |wallet: &str| { format!( "INFO: {test_name}: Ready payment to {wallet} was eliminated to spare MASQ for \ @@ -1401,6 +1422,8 @@ mod tests { .for_each(|address| { let _ = log_handler.exists_log_containing(&expected_log(address)); }); + + // Nothing blew up from the giant inputs, the test was a success } fn make_weighted_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { @@ -1484,42 +1507,43 @@ mod tests { SystemTime::now() } - // This function should take just such essential args as balances and those that play rather - // a secondary role, yet an important one in the verification processes for proposed adjusted - // balances. Refrain from employing more of the weights-affecting parameters as they would - // only burden us with their consideration in these tests. + // This function should take just such essential args like balances and also those that have + // a less significant, yet important, role within the verification process of the proposed + // adjusted balances. fn make_plucked_qualified_account( - wallet_addr_fragment: &str, + wallet_seed: &str, balance_minor: u128, threshold_intercept_major: u128, permanent_debt_allowed_major: u128, ) -> QualifiedPayableAccount { QualifiedPayableAccount::new( PayableAccount { - wallet: make_wallet(wallet_addr_fragment), + wallet: make_wallet(wallet_seed), balance_wei: balance_minor, last_paid_timestamp: meaningless_timestamp(), pending_payable_opt: None, }, - multiple_by_billion(threshold_intercept_major), - CreditorThresholds::new(multiple_by_billion(permanent_debt_allowed_major)), + multiply_by_billion(threshold_intercept_major), + CreditorThresholds::new(multiply_by_billion(permanent_debt_allowed_major)), ) } + //---------------------------------------------------------------------------------------------- + // Main-purpose okay tests manifesting the full pallet of different adjustment scenarios + #[test] - fn count_of_qualified_accounts_before_equals_the_one_of_payments_after() { - // In other words, adjustment by service fee with no account eliminated + fn accounts_count_does_not_change_during_adjustment() { init_test_logging(); let calculate_params_arc = Arc::new(Mutex::new(vec![])); - let test_name = "count_of_qualified_accounts_before_equals_the_one_of_payments_after"; + let test_name = "accounts_count_does_not_change_during_adjustment"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(5_444_444_444); + let balance_1 = 5_100_100_100_200_200_200; let qualified_account_1 = make_plucked_qualified_account("abc", balance_1, 2_000_000_000, 1_000_000_000); - let balance_2 = multiple_by_billion(6_000_000_000); + let balance_2 = 6_000_000_000_123_456_789; let qualified_account_2 = make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000); - let balance_3 = multiple_by_billion(6_666_666_666); + let balance_3 = 6_666_666_666_000_000_000; let qualified_account_3 = make_plucked_qualified_account("ghi", balance_3, 2_000_000_000, 1_111_111_111); let qualified_payables = vec![ @@ -1528,17 +1552,19 @@ mod tests { qualified_account_3.clone(), ]; let analyzed_payables = convert_collection(qualified_payables); - let mut subject = PaymentAdjusterReal::new(); let calculator_mock = CriterionCalculatorMock::default() .calculate_params(&calculate_params_arc) - .calculate_result(multiple_by_billion(4_500_000_000)) - .calculate_result(multiple_by_billion(4_200_000_000)) - .calculate_result(multiple_by_billion(3_800_000_000)); - subject.calculators = vec![Box::new(calculator_mock)]; - subject.logger = Logger::new(test_name); + .calculate_result(multiply_by_billion(4_600_000_000)) + .calculate_result(multiply_by_billion(4_200_000_000)) + .calculate_result(multiply_by_billion(3_800_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .criterion_calculator(calculator_mock) + .logger(Logger::new(test_name)) + .build(); let agent_id_stamp = ArbitraryIdStamp::new(); let accounts_sum_minor = balance_1 + balance_2 + balance_3; - let cw_service_fee_balance_minor = accounts_sum_minor - multiple_by_billion(2_000_000_000); + let cw_service_fee_balance_minor = accounts_sum_minor - multiply_by_billion(2_000_000_000); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(cw_service_fee_balance_minor); @@ -1553,8 +1579,8 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_adjusted_balance_1 = 4_833_333_333_000_000_000; - let expected_adjusted_balance_2 = 5_500_000_000_000_000_000; + let expected_adjusted_balance_1 = 4_488_988_989_200_200_200; + let expected_adjusted_balance_2 = 5_500_000_000_123_456_789; let expected_adjusted_balance_3 = 5_777_777_777_000_000_000; let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { @@ -1616,32 +1642,35 @@ mod tests { let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(111_000_000); + let balance_1 = multiply_by_billion(111_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 100_000_000, 20_000_000); - let balance_2 = multiple_by_billion(300_000_000); + let balance_2 = multiply_by_billion(300_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 120_000_000, 50_000_000); - let balance_3 = multiple_by_billion(222_222_222); + let balance_3 = multiply_by_billion(222_222_222); let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 40_000_000); let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(400_000_000)) + .calculate_result(multiply_by_billion(400_000_000)) // This account will be cut off because it has the lowest weight and only two accounts // can be kept according to the limitations detected in the transaction fee balance - .calculate_result(multiple_by_billion(120_000_000)) - .calculate_result(multiple_by_billion(250_000_000)); - let mut subject = PaymentAdjusterReal::new(); - subject.calculators = vec![Box::new(calculator_mock)]; - subject.logger = Logger::new(test_name); + .calculate_result(multiply_by_billion(120_000_000)) + .calculate_result(multiply_by_billion(250_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .criterion_calculator(calculator_mock) + .logger(Logger::new(test_name)) + .build(); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(u128::MAX); + let transaction_count_limit = 2; let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 2, + transaction_count_limit, }, analyzed_payables, ), @@ -1685,11 +1714,11 @@ mod tests { // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let balance_1 = multiple_by_billion(111_000_000); + let balance_1 = multiply_by_billion(111_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 50_000_000, 10_000_000); - let balance_2 = multiple_by_billion(333_000_000); + let balance_2 = multiply_by_billion(333_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 200_000_000, 50_000_000); - let balance_3 = multiple_by_billion(222_000_000); + let balance_3 = multiply_by_billion(222_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 35_000_000); let disqualification_arbiter = DisqualificationArbiter::default(); let disqualification_limit_1 = @@ -1699,13 +1728,15 @@ mod tests { let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(400_000_000)) - .calculate_result(multiple_by_billion(200_000_000)) - .calculate_result(multiple_by_billion(300_000_000)); - let mut subject = PaymentAdjusterReal::new(); - subject.calculators = vec![Box::new(calculator_mock)]; + .calculate_result(multiply_by_billion(400_000_000)) + .calculate_result(multiply_by_billion(200_000_000)) + .calculate_result(multiply_by_billion(300_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .criterion_calculator(calculator_mock) + .build(); let cw_service_fee_balance_minor = - disqualification_limit_1 + disqualification_limit_3 + multiple_by_billion(10_000_000); + disqualification_limit_1 + disqualification_limit_3 + multiply_by_billion(10_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1714,11 +1745,12 @@ mod tests { client_id: 123, context_id: 321, }); // Just hardening, not so important + let transaction_count_limit = 2; let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 2, + transaction_count_limit, }, analyzed_payables, ), @@ -1751,23 +1783,25 @@ mod tests { let test_name = "only_service_fee_balance_limits_the_payments_count"; let now = SystemTime::now(); // Account to be adjusted to keep as much as it is left in the cw balance - let balance_1 = multiple_by_billion(333_000_000); + let balance_1 = multiply_by_billion(333_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 200_000_000, 50_000_000); // Account to be outweighed and fully preserved - let balance_2 = multiple_by_billion(111_000_000); + let balance_2 = multiply_by_billion(111_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000, 10_000_000); // Account to be disqualified - let balance_3 = multiple_by_billion(600_000_000); + let balance_3 = multiply_by_billion(600_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(900_000_000)) - .calculate_result(multiple_by_billion(1_100_000_000)) - .calculate_result(multiple_by_billion(600_000_000)); - let mut subject = PaymentAdjusterReal::new(); - subject.calculators = vec![Box::new(calculator_mock)]; - subject.logger = Logger::new(test_name); + .calculate_result(multiply_by_billion(900_000_000)) + .calculate_result(multiply_by_billion(1_100_000_000)) + .calculate_result(multiply_by_billion(600_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .criterion_calculator(calculator_mock) + .logger(Logger::new(test_name)) + .build(); let service_fee_balance_in_minor_units = balance_1 + balance_2 - 55; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() @@ -1816,39 +1850,42 @@ mod tests { init_test_logging(); let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(100_000_000_000); + let balance_1 = multiply_by_billion(100_000_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 60_000_000_000, 10_000_000_000); // The second is thrown away first in a response to the shortage of transaction fee, // as its weight is the least significant - let balance_2 = multiple_by_billion(500_000_000_000); + let balance_2 = multiply_by_billion(500_000_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 100_000_000_000, 30_000_000_000); // Thrown away as the second one due to a shortage in the service fee, // listed among accounts to disqualify and picked eventually for its // lowest weight - let balance_3 = multiple_by_billion(250_000_000_000); + let balance_3 = multiply_by_billion(250_000_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 90_000_000_000, 20_000_000_000); let qualified_payables = vec![account_1.clone(), account_2, account_3]; let analyzed_payables = convert_collection(qualified_payables); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiple_by_billion(900_000_000_000)) - .calculate_result(multiple_by_billion(500_000_000_000)) - .calculate_result(multiple_by_billion(750_000_000_000)); - let mut subject = PaymentAdjusterReal::new(); - subject.calculators = vec![Box::new(calculator_mock)]; - subject.logger = Logger::new(test_name); - let service_fee_balance_in_minor = balance_1 - multiple_by_billion(10_000_000_000); + .calculate_result(multiply_by_billion(900_000_000_000)) + .calculate_result(multiply_by_billion(500_000_000_000)) + .calculate_result(multiply_by_billion(750_000_000_000)); + let mut subject = PaymentAdjusterTestBuilder::default() + .start_with_inner_null() + .criterion_calculator(calculator_mock) + .logger(Logger::new(test_name)) + .build(); + let service_fee_balance_in_minor = balance_1 - multiply_by_billion(10_000_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) .service_fee_balance_minor_result(service_fee_balance_in_minor); + let transaction_count_limit = 2; let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 2, + transaction_count_limit, }, analyzed_payables, ), @@ -1884,20 +1921,21 @@ mod tests { test_inner_was_reset_to_null(subject) } + //TODO move this test above the happy path tests #[test] fn late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient( ) { init_test_logging(); let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); - let balance_1 = multiple_by_billion(500_000_000_000); + let balance_1 = multiply_by_billion(500_000_000_000); let account_1 = make_plucked_qualified_account("abc", balance_1, 300_000_000_000, 100_000_000_000); // This account is eliminated in the transaction fee cut - let balance_2 = multiple_by_billion(111_000_000_000); + let balance_2 = multiply_by_billion(111_000_000_000); let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000_000, 10_000_000_000); - let balance_3 = multiple_by_billion(300_000_000_000); + let balance_3 = multiply_by_billion(300_000_000_000); let account_3 = make_plucked_qualified_account("ghi", balance_3, 150_000_000_000, 50_000_000_000); let mut subject = PaymentAdjusterReal::new(); @@ -1911,11 +1949,12 @@ mod tests { let analyzed_payables = convert_collection(qualified_payables); let agent = BlockchainAgentMock::default() .service_fee_balance_minor_result(cw_service_fee_balance_minor); + let transaction_count_limit = 2; let adjustment_setup = PreparedAdjustment { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::BeginByTransactionFee { - affordable_transaction_count: 2, + transaction_count_limit, }, analyzed_payables, ), @@ -2010,7 +2049,7 @@ mod tests { match account_balances { Either::Left(in_major) => in_major .into_iter() - .map(|major| gwei_to_wei(major)) + .map(|major| multiply_by_billion(major as u128)) .collect(), Either::Right(in_minor) => in_minor, } @@ -2074,8 +2113,8 @@ mod tests { gas_price: u64, cw_service_fee_balance_minor: u128, ) -> Box { - let estimated_transaction_fee_per_transaction_minor = gwei_to_wei( - estimated_transaction_fee_units_per_transaction * gas_price, + let estimated_transaction_fee_per_transaction_minor = multiply_by_billion( + (estimated_transaction_fee_units_per_transaction * gas_price) as u128, ); let blockchain_agent = BlockchainAgentMock::default() @@ -2089,6 +2128,16 @@ mod tests { Box::new(blockchain_agent) } + fn reconstruct_mock_agent(boxed: Box) -> BlockchainAgentMock { + BlockchainAgentMock::default() + .gas_price_margin_result(boxed.gas_price_margin()) + .transaction_fee_balance_minor_result(boxed.transaction_fee_balance_minor()) + .service_fee_balance_minor_result(boxed.service_fee_balance_minor()) + .estimated_transaction_fee_per_transaction_minor_result( + boxed.estimated_transaction_fee_per_transaction_minor(), + ) + } + fn test_inner_was_reset_to_null(subject: PaymentAdjusterReal) { let err = catch_unwind(AssertUnwindSafe(|| { subject.inner.original_cw_service_fee_balance_minor() @@ -2111,14 +2160,14 @@ mod tests { let qualified_payable = QualifiedPayableAccount { bare_account: PayableAccount { wallet: make_wallet("abc"), - balance_wei: gwei_to_wei::(444_666_888), + balance_wei: multiply_by_billion(444_666_888), last_paid_timestamp: now.checked_sub(Duration::from_secs(123_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept_minor: gwei_to_wei::(20_000), - creditor_thresholds: CreditorThresholds::new(gwei_to_wei::(10_000)), + payment_threshold_intercept_minor: multiply_by_billion(20_000), + creditor_thresholds: CreditorThresholds::new(multiply_by_billion(10_000)), }; - let cw_service_fee_balance_minor = gwei_to_wei::(3_000); + let cw_service_fee_balance_minor = multiply_by_billion(3_000); let exceeding_balance = qualified_payable.bare_account.balance_wei - qualified_payable.payment_threshold_intercept_minor; let context = PaymentAdjusterInnerReal::new( @@ -2187,7 +2236,7 @@ mod tests { ) { let calculators_count = PaymentAdjusterReal::default().calculators.len(); let now = SystemTime::now(); - let cw_service_fee_balance_minor = gwei_to_wei::(1_000_000); + let cw_service_fee_balance_minor = multiply_by_billion(1_000_000); let (template_accounts, template_computed_weight) = prepare_nominal_data_before_loading_actual_test_input( now, @@ -2231,12 +2280,12 @@ mod tests { let make_qualified_payable = |wallet| QualifiedPayableAccount { bare_account: PayableAccount { wallet, - balance_wei: gwei_to_wei::(20_000_000), + balance_wei: multiply_by_billion(20_000_000), last_paid_timestamp: now.checked_sub(Duration::from_secs(10_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept_minor: gwei_to_wei::(12_000_000), - creditor_thresholds: CreditorThresholds::new(gwei_to_wei::(1_000_000)), + payment_threshold_intercept_minor: multiply_by_billion(12_000_000), + creditor_thresholds: CreditorThresholds::new(multiply_by_billion(1_000_000)), }; [ @@ -2279,13 +2328,13 @@ mod tests { let analyzed_payables = convert_collection(qualified_payables); let max_debt_above_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); - let mut subject = make_initialized_subject( - Some(now), - Some(cw_service_fee_balance_minor), - None, - Some(max_debt_above_threshold_in_qualified_payables), - None, - ); + let mut subject = PaymentAdjusterTestBuilder::default() + .now(now) + .cw_service_fee_balance_minor(cw_service_fee_balance_minor) + .max_debt_above_threshold_in_qualified_payables( + max_debt_above_threshold_in_qualified_payables, + ) + .build(); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); let service_fee_adjuster_mock = ServiceFeeAdjusterMock::default() // We use this container to intercept those values we are after @@ -2316,7 +2365,7 @@ mod tests { // It allows to halt the code executions without a dive in the recursion assert_eq!( actual_result, - Err(PaymentAdjusterError::AllAccountsEliminated) + Err(PaymentAdjusterError::RecursionDrainedAllAccounts) ); let mut perform_adjustment_by_service_fee_params = perform_adjustment_by_service_fee_params_arc.lock().unwrap(); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index d512afa29..c4435f7f7 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -449,10 +449,10 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { let also_by_transaction_fee = generate_boolean(gn); if also_by_transaction_fee && accounts_count > 2 { - let affordable_transaction_count = + let transaction_count_limit = u16::try_from(generate_non_zero_usize(gn, accounts_count)).unwrap(); Adjustment::BeginByTransactionFee { - affordable_transaction_count, + transaction_count_limit, } } else { Adjustment::ByServiceFee @@ -649,7 +649,7 @@ fn write_brief_test_summary_into_file( {}\n\n\ Unsuccessful\n\ Caught by the entry check:............. {}\n\ - With 'AllAccountsEliminated':.......... {}\n\ + With 'RecursionDrainedAllAccounts':.......... {}\n\ With late insufficient balance errors:. {}\n\n\ Legend\n\ Partially adjusted accounts mark:...... {}", @@ -714,7 +714,7 @@ fn do_final_processing_of_single_scenario( PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => { test_overall_output.late_immoderately_insufficient_service_fee_balance += 1 } - PaymentAdjusterError::AllAccountsEliminated => { + PaymentAdjusterError::RecursionDrainedAllAccounts => { test_overall_output.all_accounts_eliminated += 1 } } @@ -907,8 +907,8 @@ fn resolve_affordable_transaction_count(adjustment: &Adjustment) -> String { match adjustment { Adjustment::ByServiceFee => "UNLIMITED".to_string(), Adjustment::BeginByTransactionFee { - affordable_transaction_count, - } => affordable_transaction_count.to_string(), + transaction_count_limit, + } => transaction_count_limit.to_string(), } } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 3ba37f2f5..1dc672aef 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -91,8 +91,8 @@ impl PreparatoryAnalyzer { let adjustment = match transaction_fee_limitation_opt { None => Adjustment::ByServiceFee, - Some(affordable_transaction_count) => Adjustment::BeginByTransactionFee { - affordable_transaction_count, + Some(transaction_count_limit) => Adjustment::BeginByTransactionFee { + transaction_count_limit, }, }; @@ -373,7 +373,7 @@ mod tests { PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, }; use crate::accountant::payment_adjuster::test_utils::{ - make_weighed_account, multiple_by_billion, DisqualificationGaugeMock, + make_weighed_account, multiply_by_billion, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, @@ -580,14 +580,14 @@ mod tests { fn accounts_analyzing_works_even_for_weighted_payable() { init_test_logging(); let test_name = "accounts_analyzing_works_even_for_weighted_payable"; - let balance_1 = multiple_by_billion(2_000_000); + let balance_1 = multiply_by_billion(2_000_000); let mut weighted_account_1 = make_weighed_account(123); weighted_account_1 .analyzed_account .qualified_as .bare_account .balance_wei = balance_1; - let balance_2 = multiple_by_billion(3_456_000); + let balance_2 = multiply_by_billion(3_456_000); let mut weighted_account_2 = make_weighed_account(456); weighted_account_2 .analyzed_account diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index b0ab1996c..b1b9b6c92 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -222,7 +222,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, multiple_by_billion, + make_non_guaranteed_unconfirmed_adjustment, multiply_by_billion, }; #[test] @@ -234,12 +234,12 @@ mod tests { .analyzed_account .qualified_as .bare_account - .balance_wei = multiple_by_billion(2_000_000_000); + .balance_wei = multiply_by_billion(2_000_000_000); account_1 .weighted_account .analyzed_account - .disqualification_limit_minor = multiple_by_billion(1_800_000_000); - account_1.proposed_adjusted_balance_minor = multiple_by_billion(3_000_000_000); + .disqualification_limit_minor = multiply_by_billion(1_800_000_000); + account_1.proposed_adjusted_balance_minor = multiply_by_billion(3_000_000_000); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); let weight_2 = account_2.weighted_account.weight; account_2 @@ -247,24 +247,24 @@ mod tests { .analyzed_account .qualified_as .bare_account - .balance_wei = multiple_by_billion(5_000_000_000); + .balance_wei = multiply_by_billion(5_000_000_000); account_2 .weighted_account .analyzed_account - .disqualification_limit_minor = multiple_by_billion(4_200_000_000) - 1; - account_2.proposed_adjusted_balance_minor = multiple_by_billion(4_200_000_000); + .disqualification_limit_minor = multiply_by_billion(4_200_000_000) - 1; + account_2.proposed_adjusted_balance_minor = multiply_by_billion(4_200_000_000); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 .weighted_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiple_by_billion(3_000_000_000); + .balance_wei = multiply_by_billion(3_000_000_000); account_3 .weighted_account .analyzed_account - .disqualification_limit_minor = multiple_by_billion(2_000_000_000) + 1; - account_3.proposed_adjusted_balance_minor = multiple_by_billion(2_000_000_000); + .disqualification_limit_minor = multiply_by_billion(2_000_000_000) + 1; + account_3.proposed_adjusted_balance_minor = multiply_by_billion(2_000_000_000); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); let weight_4 = account_4.weighted_account.weight; account_4 @@ -272,24 +272,24 @@ mod tests { .analyzed_account .qualified_as .bare_account - .balance_wei = multiple_by_billion(1_500_000_000); + .balance_wei = multiply_by_billion(1_500_000_000); account_4 .weighted_account .analyzed_account - .disqualification_limit_minor = multiple_by_billion(500_000_000); - account_4.proposed_adjusted_balance_minor = multiple_by_billion(500_000_000); + .disqualification_limit_minor = multiply_by_billion(500_000_000); + account_4.proposed_adjusted_balance_minor = multiply_by_billion(500_000_000); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 .weighted_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiple_by_billion(2_000_000_000); + .balance_wei = multiply_by_billion(2_000_000_000); account_5 .weighted_account .analyzed_account - .disqualification_limit_minor = multiple_by_billion(1_000_000_000) + 1; - account_5.proposed_adjusted_balance_minor = multiple_by_billion(1_000_000_000); + .disqualification_limit_minor = multiply_by_billion(1_000_000_000) + 1; + account_5.proposed_adjusted_balance_minor = multiply_by_billion(1_000_000_000); let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -310,7 +310,7 @@ mod tests { .qualified_as .bare_account, weight_1, - multiple_by_billion(1_800_000_000), + multiply_by_billion(1_800_000_000), ), AdjustedAccountBeforeFinalization::new( account_2 @@ -319,7 +319,7 @@ mod tests { .qualified_as .bare_account, weight_2, - multiple_by_billion(4_200_000_000) - 1, + multiply_by_billion(4_200_000_000) - 1, ), AdjustedAccountBeforeFinalization::new( account_4 @@ -328,7 +328,7 @@ mod tests { .qualified_as .bare_account, weight_4, - multiple_by_billion(500_000_000), + multiply_by_billion(500_000_000), ), ]; assert_eq!(thriving_competitors, expected_adjusted_outweighed_accounts) diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index dc35f548e..c3e6967c0 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -16,7 +16,7 @@ use crate::accountant::payment_adjuster::PaymentAdjusterReal; use crate::accountant::test_utils::{ make_meaningless_analyzed_account, make_meaningless_qualified_payable, }; -use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::accountant::{gwei_to_wei, AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::sub_lib::accountant::PaymentThresholds; use crate::test_utils::make_wallet; use itertools::Either; @@ -29,37 +29,87 @@ use std::time::{Duration, SystemTime}; lazy_static! { pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = - MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18); + multiply_by_billion(multiply_by_billion(MASQ_TOTAL_SUPPLY as u128)); pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; } -pub fn make_initialized_subject( +#[derive(Default)] +pub struct PaymentAdjusterTestBuilder { + start_with_inner_null: bool, now_opt: Option, cw_service_fee_balance_minor_opt: Option, criterion_calculator_mock_opt: Option, - max_debt_above_threshold_in_qualified_payables: Option, + max_debt_above_threshold_in_qualified_payables_opt: Option, + transaction_limit_count_opt: Option, logger_opt: Option, -) -> PaymentAdjusterReal { - let cw_service_fee_balance_minor = cw_service_fee_balance_minor_opt.unwrap_or(0); - let logger = logger_opt.unwrap_or(Logger::new("test")); - let mut subject = PaymentAdjusterReal::default(); - subject.logger = logger; - subject.inner = Box::new(PaymentAdjusterInnerReal::new( - now_opt.unwrap_or(SystemTime::now()), - None, - cw_service_fee_balance_minor, - max_debt_above_threshold_in_qualified_payables.unwrap_or(0), - )); - if let Some(calculator) = criterion_calculator_mock_opt { - subject.calculators = vec![Box::new(calculator)] - } - subject -} - -pub fn make_extreme_payables( +} + +impl PaymentAdjusterTestBuilder { + pub fn build(self) -> PaymentAdjusterReal { + let mut payment_adjuster = PaymentAdjusterReal::default(); + let logger = self.logger_opt.unwrap_or(Logger::new("test")); + payment_adjuster.logger = logger; + if !self.start_with_inner_null { + let inner = Box::new(PaymentAdjusterInnerReal::new( + self.now_opt.unwrap_or(SystemTime::now()), + self.transaction_limit_count_opt, + self.cw_service_fee_balance_minor_opt.unwrap_or(0), + self.max_debt_above_threshold_in_qualified_payables_opt + .unwrap_or(0), + )); + payment_adjuster.inner = inner; + } + if let Some(calculator) = self.criterion_calculator_mock_opt { + payment_adjuster.calculators = vec![Box::new(calculator)] + } + payment_adjuster + } + + pub fn start_with_inner_null(mut self) -> Self { + self.start_with_inner_null = true; + self + } + + pub fn criterion_calculator(mut self, calculator_mock: CriterionCalculatorMock) -> Self { + self.criterion_calculator_mock_opt = Some(calculator_mock); + self + } + + pub fn cw_service_fee_balance_minor(mut self, cw_service_fee_balance_minor: u128) -> Self { + self.cw_service_fee_balance_minor_opt = Some(cw_service_fee_balance_minor); + self + } + + pub fn max_debt_above_threshold_in_qualified_payables( + mut self, + max_exceeding_part_of_debt: u128, + ) -> Self { + self.max_debt_above_threshold_in_qualified_payables_opt = Some(max_exceeding_part_of_debt); + self + } + + pub fn now(mut self, now: SystemTime) -> Self { + self.now_opt = Some(now); + self + } + + pub fn logger(mut self, logger: Logger) -> Self { + self.logger_opt = Some(logger); + self + } + + #[allow(dead_code)] + pub fn transaction_limit_count(mut self, tx_limit: u16) -> Self { + self.transaction_limit_count_opt = Some(tx_limit); + self + } +} + +pub fn make_mammoth_payables( months_of_debt_and_balance_minor: Either<(Vec, u128), Vec<(usize, u128)>>, now: SystemTime, ) -> Vec { + // What is a mammoth like? Prehistoric, giant, and impossible to meet. Exactly as these payables. let accounts_seeds: Vec<(usize, u128)> = match months_of_debt_and_balance_minor { Either::Left((vec_of_months, constant_balance)) => vec_of_months .into_iter() @@ -216,9 +266,10 @@ impl ServiceFeeAdjusterMock { } } -pub fn multiple_by_billion(num: u128) -> u128 { - num * 10_u128.pow(9) +pub fn multiply_by_billion(num: u128) -> u128 { + gwei_to_wei(num) } + pub fn make_meaningless_analyzed_account_by_wallet( wallet_address_segment: &str, ) -> AnalyzedPayableAccount { @@ -233,7 +284,7 @@ pub fn make_weighed_account(n: u64) -> WeightedPayable { WeightedPayable::new(make_meaningless_analyzed_account(n), 123456789) } -// Should stay test only! +// Should stay test only!! impl From for AnalyzedPayableAccount { fn from(qualified_account: QualifiedPayableAccount) -> Self { let disqualification_limit = From dc69d866c9e3261198dbf2938b9ba68cbd20d363 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 10 Nov 2024 17:06:01 +0100 Subject: [PATCH 217/250] GH-711-review-one: interim commit --- .../payment_adjuster/disqualification_arbiter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index d930c2faa..abf302233 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -164,8 +164,8 @@ impl DisqualificationGauge for DisqualificationGaugeReal { } impl DisqualificationGaugeReal { - const FIRST_QUAL_COND_COEF: u128 = 2; - const SECOND_QUAL_COND_COEF: u128 = 2; + const FIRST_QUALIFICATION_CONDITION_COEFFICIENT: u128 = 2; + const SECOND_QUALIFICATION_CONDITION_COEFFICIENT: u128 = 2; const MULTIPLIER_FOR_THICKER_MARGIN: u128 = 2; fn qualifies_for_thicker_margin( @@ -178,10 +178,10 @@ impl DisqualificationGaugeReal { let minimal_acceptable_payment = exceeding_threshold + permanent_debt_allowed_minor; let condition_of_debt_fast_growth = - minimal_acceptable_payment >= Self::FIRST_QUAL_COND_COEF * considered_forgiven; + minimal_acceptable_payment >= Self::FIRST_QUALIFICATION_CONDITION_COEFFICIENT * considered_forgiven; let condition_of_position_on_rather_the_left_half_of_the_slope = - considered_forgiven >= Self::SECOND_QUAL_COND_COEF * permanent_debt_allowed_minor; + considered_forgiven >= Self::SECOND_QUALIFICATION_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; condition_of_debt_fast_growth && condition_of_position_on_rather_the_left_half_of_the_slope } @@ -228,8 +228,8 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(DisqualificationGaugeReal::FIRST_QUAL_COND_COEF, 2); - assert_eq!(DisqualificationGaugeReal::SECOND_QUAL_COND_COEF, 2); + assert_eq!(DisqualificationGaugeReal::FIRST_QUALIFICATION_CONDITION_COEFFICIENT, 2); + assert_eq!(DisqualificationGaugeReal::SECOND_QUALIFICATION_CONDITION_COEFFICIENT, 2); assert_eq!(DisqualificationGaugeReal::MULTIPLIER_FOR_THICKER_MARGIN, 2) } From 00e0cbe621d7003e16e2663611c69a371844c75b Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 14 Nov 2024 01:35:17 +0100 Subject: [PATCH 218/250] GH-711-review-one: interim commit --- node/src/accountant/payment_adjuster/mod.rs | 46 ++++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b77542134..cbcf2c57b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -48,12 +48,13 @@ use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::time::SystemTime; +use actix::Addr; use thousands::Separable; use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; - +use crate::test_utils::recorder::Recorder; // PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions // of an acute insolvency. You can easily expand the range of evaluated parameters to determine // an optimized allocation of scarce assets by writing your own CriterionCalculator. The calculator @@ -1528,6 +1529,47 @@ mod tests { ) } + struct PayableAccountSeed { + wallet_seed: &'static str, + balance_minor: u128, + threshold_intercept_major: u128, + permanent_debt_allowed_major: u128, + } + + struct DemonstrativeDisqualificationLimits { + account_1: u128, + account_2: u128, + account_3: u128 + } + + impl DemonstrativeDisqualificationLimits { + fn new(accounts: &[AnalyzedPayableAccount;3])-> Self { + todo!() + } + } + + fn make_analyzed_accounts_and_show_their_actual_disqualification_limits( + accounts_seeds: [PayableAccountSeed;3] + ) -> ([AnalyzedPayableAccount;3], DemonstrativeDisqualificationLimits) { + + let qualified_payables: Vec<_> = accounts_seeds.map(|account_seed| + QualifiedPayableAccount::new( + PayableAccount { + wallet: make_wallet(account_seed.wallet_seed), + balance_wei: account_seed.balance_minor, + last_paid_timestamp: meaningless_timestamp(), + pending_payable_opt: None, + }, + multiply_by_billion(account_seed.threshold_intercept_major), + CreditorThresholds::new(multiply_by_billion(account_seed.permanent_debt_allowed_major)), + ) + ).collect(); + let analyzed_accounts: Vec = convert_collection(qualified_payables); + let analyzed_accounts: [AnalyzedPayableAccount;3] = analyzed_accounts.try_into().unwrap(); + let disqualification_limits = DemonstrativeDisqualificationLimits::new(&analyzed_accounts); + (analyzed_accounts, disqualification_limits) + } + //---------------------------------------------------------------------------------------------- // Main-purpose okay tests manifesting the full pallet of different adjustment scenarios @@ -2469,4 +2511,4 @@ mod tests { }, ); } -} +} \ No newline at end of file From 7cec5790e67c03f2cbbe5a0a032f835c8f317982 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 5 Dec 2024 15:34:12 +0100 Subject: [PATCH 219/250] GH-711-review-one: interim commit --- .../disqualification_arbiter.rs | 20 +- node/src/accountant/payment_adjuster/mod.rs | 220 ++++++++++++------ 2 files changed, 157 insertions(+), 83 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index abf302233..ff17ec3e9 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -111,7 +111,7 @@ impl DisqualificationArbiter { pub struct DisqualificationSuspectedAccount<'account> { pub wallet: &'account Wallet, pub weight: u128, - // The rest is for diagnostics and logging + // The rest serves diagnostics and logging pub proposed_adjusted_balance_minor: u128, pub disqualification_limit_minor: u128, pub initial_account_balance_minor: u128, @@ -177,11 +177,11 @@ impl DisqualificationGaugeReal { let considered_forgiven = threshold_intercept_minor - permanent_debt_allowed_minor; let minimal_acceptable_payment = exceeding_threshold + permanent_debt_allowed_minor; - let condition_of_debt_fast_growth = - minimal_acceptable_payment >= Self::FIRST_QUALIFICATION_CONDITION_COEFFICIENT * considered_forgiven; + let condition_of_debt_fast_growth = minimal_acceptable_payment + >= Self::FIRST_QUALIFICATION_CONDITION_COEFFICIENT * considered_forgiven; - let condition_of_position_on_rather_the_left_half_of_the_slope = - considered_forgiven >= Self::SECOND_QUALIFICATION_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; + let condition_of_position_on_rather_the_left_half_of_the_slope = considered_forgiven + >= Self::SECOND_QUALIFICATION_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; condition_of_debt_fast_growth && condition_of_position_on_rather_the_left_half_of_the_slope } @@ -228,8 +228,14 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(DisqualificationGaugeReal::FIRST_QUALIFICATION_CONDITION_COEFFICIENT, 2); - assert_eq!(DisqualificationGaugeReal::SECOND_QUALIFICATION_CONDITION_COEFFICIENT, 2); + assert_eq!( + DisqualificationGaugeReal::FIRST_QUALIFICATION_CONDITION_COEFFICIENT, + 2 + ); + assert_eq!( + DisqualificationGaugeReal::SECOND_QUALIFICATION_CONDITION_COEFFICIENT, + 2 + ); assert_eq!(DisqualificationGaugeReal::MULTIPLIER_FOR_THICKER_MARGIN, 2) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index cbcf2c57b..ac6e4add5 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -54,7 +54,6 @@ use variant_count::VariantCount; use web3::types::U256; use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; -use crate::test_utils::recorder::Recorder; // PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions // of an acute insolvency. You can easily expand the range of evaluated parameters to determine // an optimized allocation of scarce assets by writing your own CriterionCalculator. The calculator @@ -525,7 +524,7 @@ impl Display for PaymentAdjusterError { (Some(transaction_fee_check_summary), Some(service_fee_check_summary)) => write!( f, - "Neither transaction fee or service fee balance is enough to pay a single payment. \ + "Neither transaction fee nor service fee balance is enough to pay a single payment. \ Number of payments considered: {}. Transaction fee per payment: {} wei, while in \ wallet: {} wei. Total service fee required: {} wei, while in wallet: {} wei", number_of_accounts, @@ -595,7 +594,7 @@ mod tests { use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; - use itertools::Either; + use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::utils::convert_collection; @@ -985,7 +984,7 @@ mod tests { cw_service_fee_balance_minor: 100_000_000 }) }, - "Neither transaction fee or service fee balance is enough to pay a single payment. \ + "Neither transaction fee nor service fee balance is enough to pay a single payment. \ Number of payments considered: 5. Transaction fee per payment: 5,000,000,000 wei, \ while in wallet: 3,000,000,000 wei. Total service fee required: 7,000,000,000 wei, \ while in wallet: 100,000,000 wei", @@ -1529,49 +1528,86 @@ mod tests { ) } - struct PayableAccountSeed { - wallet_seed: &'static str, + #[derive(Debug, PartialEq, Clone)] + struct SketchedPayableAccount { + wallet_addr_seed: &'static str, balance_minor: u128, threshold_intercept_major: u128, permanent_debt_allowed_major: u128, } - struct DemonstrativeDisqualificationLimits { + #[derive(Debug, PartialEq)] + struct QuantifiedDisqualificationLimits { account_1: u128, account_2: u128, - account_3: u128 + account_3: u128, + } + + impl QuantifiedDisqualificationLimits { + fn validate_against_expected( + self, + expected_limit_account_1: u128, + expected_limit_account_2: u128, + expected_limit_account_3: u128, + ) { + let actual = [self.account_1, self.account_2, self.account_3]; + let expected = [ + expected_limit_account_1, + expected_limit_account_2, + expected_limit_account_3, + ]; + assert_eq!( + actual, expected, + "Test manifests disqualification limits as {:?} to help \ + with visualising the conditions but such limits are ot true, because the accounts in \ + the input actually evaluates to these limits {:?}", + expected, actual + ); + } } - impl DemonstrativeDisqualificationLimits { - fn new(accounts: &[AnalyzedPayableAccount;3])-> Self { - todo!() + impl From<&[AnalyzedPayableAccount; 3]> for QuantifiedDisqualificationLimits { + fn from(accounts: &[AnalyzedPayableAccount; 3]) -> Self { + Self { + account_1: accounts[0].disqualification_limit_minor, + account_2: accounts[1].disqualification_limit_minor, + account_3: accounts[2].disqualification_limit_minor, + } } } fn make_analyzed_accounts_and_show_their_actual_disqualification_limits( - accounts_seeds: [PayableAccountSeed;3] - ) -> ([AnalyzedPayableAccount;3], DemonstrativeDisqualificationLimits) { - - let qualified_payables: Vec<_> = accounts_seeds.map(|account_seed| - QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet(account_seed.wallet_seed), - balance_wei: account_seed.balance_minor, - last_paid_timestamp: meaningless_timestamp(), - pending_payable_opt: None, - }, - multiply_by_billion(account_seed.threshold_intercept_major), - CreditorThresholds::new(multiply_by_billion(account_seed.permanent_debt_allowed_major)), - ) - ).collect(); + accounts_seeds: [SketchedPayableAccount; 3], + ) -> ( + [AnalyzedPayableAccount; 3], + QuantifiedDisqualificationLimits, + ) { + let qualified_payables: Vec<_> = accounts_seeds + .into_iter() + .map(|account_seed| { + QualifiedPayableAccount::new( + PayableAccount { + wallet: make_wallet(account_seed.wallet_addr_seed), + balance_wei: account_seed.balance_minor, + last_paid_timestamp: meaningless_timestamp(), + pending_payable_opt: None, + }, + multiply_by_billion(account_seed.threshold_intercept_major), + CreditorThresholds::new(multiply_by_billion( + account_seed.permanent_debt_allowed_major, + )), + ) + }) + .collect(); let analyzed_accounts: Vec = convert_collection(qualified_payables); - let analyzed_accounts: [AnalyzedPayableAccount;3] = analyzed_accounts.try_into().unwrap(); - let disqualification_limits = DemonstrativeDisqualificationLimits::new(&analyzed_accounts); + let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); + let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); (analyzed_accounts, disqualification_limits) } //---------------------------------------------------------------------------------------------- - // Main-purpose okay tests manifesting the full pallet of different adjustment scenarios + // The following overall tests demonstrate showcases for PA through different situations that + // can come about during an adjustment #[test] fn accounts_count_does_not_change_during_adjustment() { @@ -1579,21 +1615,31 @@ mod tests { let calculate_params_arc = Arc::new(Mutex::new(vec![])); let test_name = "accounts_count_does_not_change_during_adjustment"; let now = SystemTime::now(); - let balance_1 = 5_100_100_100_200_200_200; - let qualified_account_1 = - make_plucked_qualified_account("abc", balance_1, 2_000_000_000, 1_000_000_000); - let balance_2 = 6_000_000_000_123_456_789; - let qualified_account_2 = - make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000); - let balance_3 = 6_666_666_666_000_000_000; - let qualified_account_3 = - make_plucked_qualified_account("ghi", balance_3, 2_000_000_000, 1_111_111_111); - let qualified_payables = vec![ - qualified_account_1.clone(), - qualified_account_2.clone(), - qualified_account_3.clone(), + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: 5_100_100_100_200_200_200, + threshold_intercept_major: 2_000_000_000, + permanent_debt_allowed_major: 1_000_000_000, + }; + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: 6_000_000_000_123_456_789, + threshold_intercept_major: 2_500_000_000, + permanent_debt_allowed_major: 2_000_000_000, + }; + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: 6_666_666_666_666_666_666, + threshold_intercept_major: 2_000_000_000, + permanent_debt_allowed_major: 1_111_111_111, + }; + let account_seeds = [ + sketched_account_1.clone(), + sketched_account_2.clone(), + sketched_account_3.clone(), ]; - let analyzed_payables = convert_collection(qualified_payables); + let (analyzed_payables, actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(account_seeds); let calculator_mock = CriterionCalculatorMock::default() .calculate_params(&calculate_params_arc) .calculate_result(multiply_by_billion(4_600_000_000)) @@ -1605,7 +1651,9 @@ mod tests { .logger(Logger::new(test_name)) .build(); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum_minor = balance_1 + balance_2 + balance_3; + let accounts_sum_minor = sketched_account_1.balance_minor + + sketched_account_2.balance_minor + + sketched_account_3.balance_minor; let cw_service_fee_balance_minor = accounts_sum_minor - multiply_by_billion(2_000_000_000); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1614,28 +1662,33 @@ mod tests { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, - analyzed_payables, + analyzed_payables.clone().into(), ), response_skeleton_opt: None, }; let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + actual_disqualification_limits.validate_against_expected( + 4_100_100_100_200_200_200, + 5_500_000_000_123_456_789, + 5_777_777_777_666_666_666, + ); let expected_adjusted_balance_1 = 4_488_988_989_200_200_200; let expected_adjusted_balance_2 = 5_500_000_000_123_456_789; - let expected_adjusted_balance_3 = 5_777_777_777_000_000_000; + let expected_adjusted_balance_3 = 5_777_777_777_666_666_666; let expected_criteria_computation_output = { let account_1_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_1, - ..qualified_account_1.bare_account.clone() + ..analyzed_payables[0].qualified_as.bare_account.clone() }; let account_2_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_2, - ..qualified_account_2.bare_account.clone() + ..analyzed_payables[1].qualified_as.bare_account.clone() }; let account_3_adjusted = PayableAccount { balance_wei: expected_adjusted_balance_3, - ..qualified_account_3.bare_account.clone() + ..analyzed_payables[2].qualified_as.bare_account.clone() }; vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] }; @@ -1646,14 +1699,11 @@ mod tests { assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); let calculate_params = calculate_params_arc.lock().unwrap(); - assert_eq!( - *calculate_params, - vec![ - qualified_account_1, - qualified_account_2, - qualified_account_3 - ] - ); + let expected_calculate_params = analyzed_payables + .into_iter() + .map(|account| account.qualified_as) + .collect_vec(); + assert_eq!(*calculate_params, expected_calculate_params); let log_msg = format!( "DEBUG: {test_name}: \n\ |Payable Account Balance Wei @@ -1667,11 +1717,11 @@ mod tests { | {} |0x0000000000000000000000000000000000616263 {} | {}", - balance_3.separate_with_commas(), + sketched_account_3.balance_minor.separate_with_commas(), expected_adjusted_balance_3.separate_with_commas(), - balance_2.separate_with_commas(), + sketched_account_2.balance_minor.separate_with_commas(), expected_adjusted_balance_2.separate_with_commas(), - balance_1.separate_with_commas(), + sketched_account_1.balance_minor.separate_with_commas(), expected_adjusted_balance_1.separate_with_commas() ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); @@ -1684,18 +1734,32 @@ mod tests { let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; let now = SystemTime::now(); - let balance_1 = multiply_by_billion(111_000_000); - let account_1 = make_plucked_qualified_account("abc", balance_1, 100_000_000, 20_000_000); - let balance_2 = multiply_by_billion(300_000_000); - let account_2 = make_plucked_qualified_account("def", balance_2, 120_000_000, 50_000_000); - let balance_3 = multiply_by_billion(222_222_222); - let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 40_000_000); - let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; - let analyzed_payables = convert_collection(qualified_payables); + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: multiply_by_billion(111_000_000), + threshold_intercept_major: 100_000_000, + permanent_debt_allowed_major: 20_000_000, + }; + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: multiply_by_billion(300_000_000), + threshold_intercept_major: 120_000_000, + permanent_debt_allowed_major: 50_000_000, + }; + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: multiply_by_billion(222_222_222), + threshold_intercept_major: 100_000_000, + permanent_debt_allowed_major: 40_000_000, + }; + let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; + let (analyzed_payables, _actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiply_by_billion(400_000_000)) - // This account will be cut off because it has the lowest weight and only two accounts - // can be kept according to the limitations detected in the transaction fee balance + // This account will have to fall off because of its lowest weight and that only two + // accounts can be kept according to the limitations detected in the transaction fee + // balance .calculate_result(multiply_by_billion(120_000_000)) .calculate_result(multiply_by_billion(250_000_000)); let mut subject = PaymentAdjusterTestBuilder::default() @@ -1714,7 +1778,7 @@ mod tests { Adjustment::BeginByTransactionFee { transaction_count_limit, }, - analyzed_payables, + analyzed_payables.clone().into(), ), response_skeleton_opt: None, }; @@ -1722,10 +1786,14 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); // The account 1 takes the first place for its weight being the biggest - assert_eq!( - result.affordable_accounts, - vec![account_1.bare_account, account_3.bare_account] - ); + let expected_affordable_accounts = { + let mut analyzed_payables = analyzed_payables.to_vec(); + let account_1_unchanged = analyzed_payables.remove(0).qualified_as.bare_account; + let _ = analyzed_payables.remove(0); + let account_3_unchanged = analyzed_payables.remove(0).qualified_as.bare_account; + vec![account_1_unchanged, account_3_unchanged] + }; + assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); let log_msg = format!( @@ -2511,4 +2579,4 @@ mod tests { }, ); } -} \ No newline at end of file +} From e99725bae84cf53089828809ffc31b83274f09a8 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 5 Dec 2024 23:14:23 +0100 Subject: [PATCH 220/250] GH-711-review-one: tests with more illustrative disqualification limits (to enable easier understanding) --- node/src/accountant/payment_adjuster/mod.rs | 460 +++++++++++--------- 1 file changed, 261 insertions(+), 199 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index ac6e4add5..095e5b7f7 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -48,7 +48,6 @@ use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::time::SystemTime; -use actix::Addr; use thousands::Separable; use variant_count::VariantCount; use web3::types::U256; @@ -562,7 +561,6 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ @@ -1503,108 +1501,6 @@ mod tests { ) } - fn meaningless_timestamp() -> SystemTime { - SystemTime::now() - } - - // This function should take just such essential args like balances and also those that have - // a less significant, yet important, role within the verification process of the proposed - // adjusted balances. - fn make_plucked_qualified_account( - wallet_seed: &str, - balance_minor: u128, - threshold_intercept_major: u128, - permanent_debt_allowed_major: u128, - ) -> QualifiedPayableAccount { - QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet(wallet_seed), - balance_wei: balance_minor, - last_paid_timestamp: meaningless_timestamp(), - pending_payable_opt: None, - }, - multiply_by_billion(threshold_intercept_major), - CreditorThresholds::new(multiply_by_billion(permanent_debt_allowed_major)), - ) - } - - #[derive(Debug, PartialEq, Clone)] - struct SketchedPayableAccount { - wallet_addr_seed: &'static str, - balance_minor: u128, - threshold_intercept_major: u128, - permanent_debt_allowed_major: u128, - } - - #[derive(Debug, PartialEq)] - struct QuantifiedDisqualificationLimits { - account_1: u128, - account_2: u128, - account_3: u128, - } - - impl QuantifiedDisqualificationLimits { - fn validate_against_expected( - self, - expected_limit_account_1: u128, - expected_limit_account_2: u128, - expected_limit_account_3: u128, - ) { - let actual = [self.account_1, self.account_2, self.account_3]; - let expected = [ - expected_limit_account_1, - expected_limit_account_2, - expected_limit_account_3, - ]; - assert_eq!( - actual, expected, - "Test manifests disqualification limits as {:?} to help \ - with visualising the conditions but such limits are ot true, because the accounts in \ - the input actually evaluates to these limits {:?}", - expected, actual - ); - } - } - - impl From<&[AnalyzedPayableAccount; 3]> for QuantifiedDisqualificationLimits { - fn from(accounts: &[AnalyzedPayableAccount; 3]) -> Self { - Self { - account_1: accounts[0].disqualification_limit_minor, - account_2: accounts[1].disqualification_limit_minor, - account_3: accounts[2].disqualification_limit_minor, - } - } - } - - fn make_analyzed_accounts_and_show_their_actual_disqualification_limits( - accounts_seeds: [SketchedPayableAccount; 3], - ) -> ( - [AnalyzedPayableAccount; 3], - QuantifiedDisqualificationLimits, - ) { - let qualified_payables: Vec<_> = accounts_seeds - .into_iter() - .map(|account_seed| { - QualifiedPayableAccount::new( - PayableAccount { - wallet: make_wallet(account_seed.wallet_addr_seed), - balance_wei: account_seed.balance_minor, - last_paid_timestamp: meaningless_timestamp(), - pending_payable_opt: None, - }, - multiply_by_billion(account_seed.threshold_intercept_major), - CreditorThresholds::new(multiply_by_billion( - account_seed.permanent_debt_allowed_major, - )), - ) - }) - .collect(); - let analyzed_accounts: Vec = convert_collection(qualified_payables); - let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); - let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); - (analyzed_accounts, disqualification_limits) - } - //---------------------------------------------------------------------------------------------- // The following overall tests demonstrate showcases for PA through different situations that // can come about during an adjustment @@ -1615,21 +1511,24 @@ mod tests { let calculate_params_arc = Arc::new(Mutex::new(vec![])); let test_name = "accounts_count_does_not_change_during_adjustment"; let now = SystemTime::now(); + let balance_account_1 = 5_100_100_100_200_200_200; let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", - balance_minor: 5_100_100_100_200_200_200, + balance_minor: balance_account_1, threshold_intercept_major: 2_000_000_000, permanent_debt_allowed_major: 1_000_000_000, }; + let balance_account_2 = 6_000_000_000_123_456_789; let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", - balance_minor: 6_000_000_000_123_456_789, + balance_minor: balance_account_2, threshold_intercept_major: 2_500_000_000, permanent_debt_allowed_major: 2_000_000_000, }; + let balance_account_3 = 6_666_666_666_666_666_666; let sketched_account_3 = SketchedPayableAccount { wallet_addr_seed: "ghi", - balance_minor: 6_666_666_666_666_666_666, + balance_minor: balance_account_3, threshold_intercept_major: 2_000_000_000, permanent_debt_allowed_major: 1_111_111_111, }; @@ -1651,9 +1550,7 @@ mod tests { .logger(Logger::new(test_name)) .build(); let agent_id_stamp = ArbitraryIdStamp::new(); - let accounts_sum_minor = sketched_account_1.balance_minor - + sketched_account_2.balance_minor - + sketched_account_3.balance_minor; + let accounts_sum_minor = balance_account_1 + balance_account_2 + balance_account_3; let cw_service_fee_balance_minor = accounts_sum_minor - multiply_by_billion(2_000_000_000); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1678,18 +1575,12 @@ mod tests { let expected_adjusted_balance_2 = 5_500_000_000_123_456_789; let expected_adjusted_balance_3 = 5_777_777_777_666_666_666; let expected_criteria_computation_output = { - let account_1_adjusted = PayableAccount { - balance_wei: expected_adjusted_balance_1, - ..analyzed_payables[0].qualified_as.bare_account.clone() - }; - let account_2_adjusted = PayableAccount { - balance_wei: expected_adjusted_balance_2, - ..analyzed_payables[1].qualified_as.bare_account.clone() - }; - let account_3_adjusted = PayableAccount { - balance_wei: expected_adjusted_balance_3, - ..analyzed_payables[2].qualified_as.bare_account.clone() - }; + let account_1_adjusted = + account_with_new_balance(&analyzed_payables[0], expected_adjusted_balance_1); + let account_2_adjusted = + account_with_new_balance(&analyzed_payables[1], expected_adjusted_balance_2); + let account_3_adjusted = + account_with_new_balance(&analyzed_payables[2], expected_adjusted_balance_3); vec![account_1_adjusted, account_2_adjusted, account_3_adjusted] }; assert_eq!( @@ -1717,11 +1608,11 @@ mod tests { | {} |0x0000000000000000000000000000000000616263 {} | {}", - sketched_account_3.balance_minor.separate_with_commas(), + balance_account_3.separate_with_commas(), expected_adjusted_balance_3.separate_with_commas(), - sketched_account_2.balance_minor.separate_with_commas(), + balance_account_2.separate_with_commas(), expected_adjusted_balance_2.separate_with_commas(), - sketched_account_1.balance_minor.separate_with_commas(), + balance_account_1.separate_with_commas(), expected_adjusted_balance_1.separate_with_commas() ); TestLogHandler::new().exists_log_containing(&log_msg.replace("|", "")); @@ -1817,26 +1708,37 @@ mod tests { } #[test] - fn both_balances_insufficient_but_adjustment_by_service_fee_will_not_affect_the_payments_count() + fn both_balances_insufficient_but_adjustment_by_service_fee_will_not_affect_the_payment_count() { // The course of events: // 1) adjustment by transaction fee (always means accounts elimination), // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let balance_1 = multiply_by_billion(111_000_000); - let account_1 = make_plucked_qualified_account("abc", balance_1, 50_000_000, 10_000_000); - let balance_2 = multiply_by_billion(333_000_000); - let account_2 = make_plucked_qualified_account("def", balance_2, 200_000_000, 50_000_000); - let balance_3 = multiply_by_billion(222_000_000); - let account_3 = make_plucked_qualified_account("ghi", balance_3, 100_000_000, 35_000_000); - let disqualification_arbiter = DisqualificationArbiter::default(); - let disqualification_limit_1 = - disqualification_arbiter.calculate_disqualification_edge(&account_1); - let disqualification_limit_3 = - disqualification_arbiter.calculate_disqualification_edge(&account_3); - let qualified_payables = vec![account_1.clone(), account_2, account_3.clone()]; - let analyzed_payables = convert_collection(qualified_payables); + let balance_account_1 = multiply_by_billion(111_000_000); + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: balance_account_1, + threshold_intercept_major: 50_000_000, + permanent_debt_allowed_major: 10_000_000, + }; + let balance_account_2 = multiply_by_billion(333_000_000); + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: balance_account_2, + threshold_intercept_major: 200_000_000, + permanent_debt_allowed_major: 50_000_000, + }; + let balance_account_3 = multiply_by_billion(222_000_000); + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: balance_account_3, + threshold_intercept_major: 100_000_000, + permanent_debt_allowed_major: 35_000_000, + }; + let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; + let (analyzed_payables, actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiply_by_billion(400_000_000)) .calculate_result(multiply_by_billion(200_000_000)) @@ -1845,8 +1747,9 @@ mod tests { .start_with_inner_null() .criterion_calculator(calculator_mock) .build(); - let cw_service_fee_balance_minor = - disqualification_limit_1 + disqualification_limit_3 + multiply_by_billion(10_000_000); + let cw_service_fee_balance_minor = actual_disqualification_limits.account_1 + + actual_disqualification_limits.account_3 + + multiply_by_billion(10_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1862,23 +1765,26 @@ mod tests { Adjustment::BeginByTransactionFee { transaction_count_limit, }, - analyzed_payables, + analyzed_payables.clone().into(), ), response_skeleton_opt, }; let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + actual_disqualification_limits.validate_against_expected( + 71_000_000_000_000_000, + 183_000_000_000_000_000, + 157_000_000_000_000_000, + ); // Account 2, the least important one, was eliminated for a lack of transaction fee in the cw + let expected_adjusted_balance_1 = 81_000_000_000_000_000; + let expected_adjusted_balance_3 = 157_000_000_000_000_000; let expected_accounts = { - let account_1_adjusted = PayableAccount { - balance_wei: 81_000_000_000_000_000, - ..account_1.bare_account - }; - let account_3_adjusted = PayableAccount { - balance_wei: 157_000_000_000_000_000, - ..account_3.bare_account - }; + let account_1_adjusted = + account_with_new_balance(&analyzed_payables[0], expected_adjusted_balance_1); + let account_3_adjusted = + account_with_new_balance(&analyzed_payables[2], expected_adjusted_balance_3); vec![account_1_adjusted, account_3_adjusted] }; assert_eq!(result.affordable_accounts, expected_accounts); @@ -1893,16 +1799,32 @@ mod tests { let test_name = "only_service_fee_balance_limits_the_payments_count"; let now = SystemTime::now(); // Account to be adjusted to keep as much as it is left in the cw balance - let balance_1 = multiply_by_billion(333_000_000); - let account_1 = make_plucked_qualified_account("abc", balance_1, 200_000_000, 50_000_000); + let balance_account_1 = multiply_by_billion(333_000_000); + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: balance_account_1, + threshold_intercept_major: 200_000_000, + permanent_debt_allowed_major: 50_000_000, + }; // Account to be outweighed and fully preserved - let balance_2 = multiply_by_billion(111_000_000); - let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000, 10_000_000); + let balance_account_2 = multiply_by_billion(111_000_000); + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: balance_account_2, + threshold_intercept_major: 50_000_000, + permanent_debt_allowed_major: 10_000_000, + }; // Account to be disqualified - let balance_3 = multiply_by_billion(600_000_000); - let account_3 = make_plucked_qualified_account("ghi", balance_3, 400_000_000, 100_000_000); - let qualified_payables = vec![account_1.clone(), account_2.clone(), account_3]; - let analyzed_payables = convert_collection(qualified_payables); + let balance_account_3 = multiply_by_billion(600_000_000); + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: balance_account_3, + threshold_intercept_major: 400_000_000, + permanent_debt_allowed_major: 100_000_000, + }; + let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; + let (analyzed_payables, actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiply_by_billion(900_000_000)) .calculate_result(multiply_by_billion(1_100_000_000)) @@ -1912,7 +1834,9 @@ mod tests { .criterion_calculator(calculator_mock) .logger(Logger::new(test_name)) .build(); - let service_fee_balance_in_minor_units = balance_1 + balance_2 - 55; + let service_fee_balance_in_minor_units = actual_disqualification_limits.account_1 + + actual_disqualification_limits.account_2 + + 123_456_789; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1925,17 +1849,28 @@ mod tests { agent: Box::new(agent), adjustment_analysis: AdjustmentAnalysisReport::new( Adjustment::ByServiceFee, - analyzed_payables, + analyzed_payables.clone().into(), ), response_skeleton_opt, }; let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + actual_disqualification_limits.validate_against_expected( + 183_000_000_000_000_000, + 71_000_000_000_000_000, + 300_000_000_000_000_000, + ); let expected_accounts = { - let mut account_1_adjusted = account_1; - account_1_adjusted.bare_account.balance_wei -= 55; - vec![account_2.bare_account, account_1_adjusted.bare_account] + let adjusted_account_2 = account_with_new_balance( + &analyzed_payables[1], + actual_disqualification_limits.account_2 + 123_456_789, + ); + let adjusted_account_1 = account_with_new_balance( + &analyzed_payables[0], + actual_disqualification_limits.account_1, + ); + vec![adjusted_account_2, adjusted_account_1] }; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, response_skeleton_opt); @@ -1960,22 +1895,35 @@ mod tests { init_test_logging(); let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; let now = SystemTime::now(); - let balance_1 = multiply_by_billion(100_000_000_000); - let account_1 = - make_plucked_qualified_account("abc", balance_1, 60_000_000_000, 10_000_000_000); + let balance_account_1 = multiply_by_billion(100_000_000_000); + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: balance_account_1, + threshold_intercept_major: 60_000_000_000, + permanent_debt_allowed_major: 10_000_000_000, + }; // The second is thrown away first in a response to the shortage of transaction fee, // as its weight is the least significant - let balance_2 = multiply_by_billion(500_000_000_000); - let account_2 = - make_plucked_qualified_account("def", balance_2, 100_000_000_000, 30_000_000_000); + let balance_account_2 = multiply_by_billion(500_000_000_000); + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: balance_account_2, + threshold_intercept_major: 100_000_000_000, + permanent_debt_allowed_major: 30_000_000_000, + }; // Thrown away as the second one due to a shortage in the service fee, // listed among accounts to disqualify and picked eventually for its // lowest weight - let balance_3 = multiply_by_billion(250_000_000_000); - let account_3 = - make_plucked_qualified_account("ghi", balance_3, 90_000_000_000, 20_000_000_000); - let qualified_payables = vec![account_1.clone(), account_2, account_3]; - let analyzed_payables = convert_collection(qualified_payables); + let balance_account_3 = multiply_by_billion(250_000_000_000); + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: balance_account_3, + threshold_intercept_major: 90_000_000_000, + permanent_debt_allowed_major: 20_000_000_000, + }; + let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; + let (analyzed_payables, actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() .calculate_result(multiply_by_billion(900_000_000_000)) .calculate_result(multiply_by_billion(500_000_000_000)) @@ -1985,7 +1933,7 @@ mod tests { .criterion_calculator(calculator_mock) .logger(Logger::new(test_name)) .build(); - let service_fee_balance_in_minor = balance_1 - multiply_by_billion(10_000_000_000); + let service_fee_balance_in_minor = balance_account_1 - multiply_by_billion(10_000_000_000); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1997,18 +1945,22 @@ mod tests { Adjustment::BeginByTransactionFee { transaction_count_limit, }, - analyzed_payables, + analyzed_payables.clone().into(), ), response_skeleton_opt: None, }; let result = subject.adjust_payments(adjustment_setup, now).unwrap(); - let expected_accounts = { - let mut account = account_1; - account.bare_account.balance_wei = service_fee_balance_in_minor; - vec![account.bare_account] - }; + actual_disqualification_limits.validate_against_expected( + 50_000_000_000_000_000_000, + 460_000_000_000_000_000_000, + 200_000_000_000_000_000_000, + ); + let expected_accounts = vec![account_with_new_balance( + &analyzed_payables[0], + service_fee_balance_in_minor, + )]; assert_eq!(result.affordable_accounts, expected_accounts); assert_eq!(result.response_skeleton_opt, None); assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); @@ -2031,32 +1983,135 @@ mod tests { test_inner_was_reset_to_null(subject) } - //TODO move this test above the happy path tests + #[derive(Debug, PartialEq, Clone)] + struct SketchedPayableAccount { + wallet_addr_seed: &'static str, + balance_minor: u128, + threshold_intercept_major: u128, + permanent_debt_allowed_major: u128, + } + + #[derive(Debug, PartialEq)] + struct QuantifiedDisqualificationLimits { + account_1: u128, + account_2: u128, + account_3: u128, + } + + impl QuantifiedDisqualificationLimits { + fn validate_against_expected( + &self, + expected_limit_account_1: u128, + expected_limit_account_2: u128, + expected_limit_account_3: u128, + ) { + let actual = [self.account_1, self.account_2, self.account_3]; + let expected = [ + expected_limit_account_1, + expected_limit_account_2, + expected_limit_account_3, + ]; + assert_eq!( + actual, expected, + "Test manifests disqualification limits as {:?} to help with visualising \ + the conditions but such limits are ot true, because the accounts in the input \ + actually evaluates to these limits {:?}", + expected, actual + ); + } + } + + impl From<&[AnalyzedPayableAccount; 3]> for QuantifiedDisqualificationLimits { + fn from(accounts: &[AnalyzedPayableAccount; 3]) -> Self { + Self { + account_1: accounts[0].disqualification_limit_minor, + account_2: accounts[1].disqualification_limit_minor, + account_3: accounts[2].disqualification_limit_minor, + } + } + } + + fn make_analyzed_accounts_and_show_their_actual_disqualification_limits( + accounts_seeds: [SketchedPayableAccount; 3], + ) -> ( + [AnalyzedPayableAccount; 3], + QuantifiedDisqualificationLimits, + ) { + let qualified_payables: Vec<_> = accounts_seeds + .into_iter() + .map(|account_seed| { + QualifiedPayableAccount::new( + PayableAccount { + wallet: make_wallet(account_seed.wallet_addr_seed), + balance_wei: account_seed.balance_minor, + last_paid_timestamp: meaningless_timestamp(), + pending_payable_opt: None, + }, + multiply_by_billion(account_seed.threshold_intercept_major), + CreditorThresholds::new(multiply_by_billion( + account_seed.permanent_debt_allowed_major, + )), + ) + }) + .collect(); + let analyzed_accounts: Vec = convert_collection(qualified_payables); + let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); + let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); + (analyzed_accounts, disqualification_limits) + } + + fn meaningless_timestamp() -> SystemTime { + SystemTime::now() + } + + fn account_with_new_balance( + analyzed_payable: &AnalyzedPayableAccount, + adjusted_balance: u128, + ) -> PayableAccount { + PayableAccount { + balance_wei: adjusted_balance, + ..analyzed_payable.qualified_as.bare_account.clone() + } + } + + //---------------------------------------------------------------------------------------------- + // End of happy path section + #[test] fn late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient( ) { init_test_logging(); let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; let now = SystemTime::now(); - let balance_1 = multiply_by_billion(500_000_000_000); - let account_1 = - make_plucked_qualified_account("abc", balance_1, 300_000_000_000, 100_000_000_000); + let balance_account_1 = multiply_by_billion(500_000_000_000); + let sketched_account_1 = SketchedPayableAccount { + wallet_addr_seed: "abc", + balance_minor: balance_account_1, + threshold_intercept_major: 300_000_000_000, + permanent_debt_allowed_major: 100_000_000_000, + }; // This account is eliminated in the transaction fee cut - let balance_2 = multiply_by_billion(111_000_000_000); - let account_2 = - make_plucked_qualified_account("def", balance_2, 50_000_000_000, 10_000_000_000); - let balance_3 = multiply_by_billion(300_000_000_000); - let account_3 = - make_plucked_qualified_account("ghi", balance_3, 150_000_000_000, 50_000_000_000); + let balance_account_2 = multiply_by_billion(111_000_000_000); + let sketched_account_2 = SketchedPayableAccount { + wallet_addr_seed: "def", + balance_minor: balance_account_2, + threshold_intercept_major: 50_000_000_000, + permanent_debt_allowed_major: 10_000_000_000, + }; + let balance_account_3 = multiply_by_billion(300_000_000_000); + let sketched_account_3 = SketchedPayableAccount { + wallet_addr_seed: "ghi", + balance_minor: balance_account_3, + threshold_intercept_major: 150_000_000_000, + permanent_debt_allowed_major: 50_000_000_000, + }; + let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; + let (analyzed_payables, actual_disqualification_limits) = + make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - let disqualification_arbiter = DisqualificationArbiter::default(); - let disqualification_limit_2 = - disqualification_arbiter.calculate_disqualification_edge(&account_2); // This is exactly the amount which will provoke an error - let cw_service_fee_balance_minor = disqualification_limit_2 - 1; - let qualified_payables = vec![account_1, account_2, account_3]; - let analyzed_payables = convert_collection(qualified_payables); + let cw_service_fee_balance_minor = actual_disqualification_limits.account_2 - 1; let agent = BlockchainAgentMock::default() .service_fee_balance_minor_result(cw_service_fee_balance_minor); let transaction_count_limit = 2; @@ -2066,13 +2121,18 @@ mod tests { Adjustment::BeginByTransactionFee { transaction_count_limit, }, - analyzed_payables, + analyzed_payables.into(), ), response_skeleton_opt: None, }; let result = subject.adjust_payments(adjustment_setup, now); + actual_disqualification_limits.validate_against_expected( + multiply_by_billion(300_000_000_000), + multiply_by_billion(71_000_000_000), + multiply_by_billion(250_000_000_000), + ); let err = match result { Ok(_) => panic!("expected an error but got Ok()"), Err(e) => e, @@ -2082,7 +2142,9 @@ mod tests { PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { original_number_of_accounts: 3, number_of_accounts: 2, - original_service_fee_required_total_minor: balance_1 + balance_2 + balance_3, + original_service_fee_required_total_minor: balance_account_1 + + balance_account_2 + + balance_account_3, cw_service_fee_balance_minor } ); From bfdcf977778b0718218fd3630e3160eae0914324 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 6 Dec 2024 11:52:24 +0100 Subject: [PATCH 221/250] GH-711-review-one: more tests repaired --- node/src/accountant/payment_adjuster/mod.rs | 113 +++++++++++------- .../accountant/payment_adjuster/test_utils.rs | 11 +- 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 095e5b7f7..c96f69df8 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -566,7 +566,9 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, WeightedPayable, }; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; + use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ + find_largest_exceeding_balance, sum_as, + }; use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, @@ -1283,19 +1285,20 @@ mod tests { } #[test] - fn account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them() - { - // We test that a condition to short-circuit through is integrated in for a situation where - // a performed disqualification frees means that will become available for other accounts, - // and it happens that the remaining accounts require together less than what is left to - // give out. + fn account_disqualification_makes_the_rest_flooded_with_enough_money_suddenly() { + // We test a condition to short-circuit that is built in for the case of an account + // disqualification has just been processed which has freed means, until then tied with this + // account that is gone now, and which will become an extra portion newly available for + // the other accounts from which they can gain, however, at the same time the remaining + // accounts require together less than how much can be given out. init_test_logging(); - let test_name = "account_disqualification_makes_the_rest_outweighed_as_cw_balance_becomes_excessive_for_them"; + let test_name = + "account_disqualification_makes_the_rest_flooded_with_enough_money_suddenly"; let now = SystemTime::now(); - // This simplifies the overall picture, the debt age doesn't mean anything to our calculator, - // still, it influences the height of the intercept point read out from the payment thresholds - // which can induce an impact on the value of the disqualification limit which is derived - // from the intercept + // This common value simplifies the settings for visualisation, the debt age doesn't mean + // anything, especially with all calculators mocked out, it only influences the height of + // the intercept with the payment thresholds which can in turn take role in evaluating + // the disqualification limit in each account let common_age_for_accounts_as_unimportant = now.checked_sub(Duration::from_secs(200_000)).unwrap(); let balance_1 = multiply_by_billion(80_000_000_000); @@ -1323,20 +1326,28 @@ mod tests { let analyzed_accounts = make_analyzed_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(0) - .calculate_result(multiply_by_billion(50_000_000_000)) + // If we consider that the consuming wallet holds less than the sum of + // the disqualification limits of all these 3 accounts (as also formally checked by one + // of the attached assertions below), this must mean that disqualification has to be + // ruled in the first round, where the first account is eventually eliminated for its + // lowest weight. + .calculate_result(multiply_by_billion(10_000_000_000)) + .calculate_result(multiply_by_billion(30_000_000_000)) .calculate_result(multiply_by_billion(50_000_000_000)); + let sum_of_disqualification_limits = sum_as(&analyzed_accounts, |account| { + account.disqualification_limit_minor + }); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() + .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); - subject.calculators.push(Box::new(calculator_mock)); let agent_id_stamp = ArbitraryIdStamp::new(); - let service_fee_balance_in_minor_units = balance_2 + balance_3 + ((balance_1 * 10) / 100); + let service_fee_balance_minor = balance_2 + balance_3 + ((balance_1 * 10) / 100); let agent = { let mock = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) - .service_fee_balance_minor_result(service_fee_balance_in_minor_units); + .service_fee_balance_minor_result(service_fee_balance_minor); Box::new(mock) }; let adjustment_setup = PreparedAdjustment { @@ -1353,7 +1364,11 @@ mod tests { let expected_affordable_accounts = { vec![account_3, account_2] }; assert_eq!(result.affordable_accounts, expected_affordable_accounts); assert_eq!(result.response_skeleton_opt, None); - assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp) + assert_eq!(result.agent.arbitrary_id_stamp(), agent_id_stamp); + // This isn't any kind of universal requirement, but this condition is enough to be + // certain that at least one account must be offered a smaller amount than what says its + // disqualification limit, and therefore a disqualification needs to take place. + assert!(sum_of_disqualification_limits > service_fee_balance_minor); } #[test] @@ -1518,6 +1533,7 @@ mod tests { threshold_intercept_major: 2_000_000_000, permanent_debt_allowed_major: 1_000_000_000, }; + let balance_account_2 = 6_000_000_000_123_456_789; let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", @@ -1532,6 +1548,9 @@ mod tests { threshold_intercept_major: 2_000_000_000, permanent_debt_allowed_major: 1_111_111_111, }; + let total_weight_account_1 = multiply_by_billion(400_000_000); + let total_weight_account_2 = multiply_by_billion(200_000_000); + let total_weight_account_3 = multiply_by_billion(300_000_000); let account_seeds = [ sketched_account_1.clone(), sketched_account_2.clone(), @@ -1541,12 +1560,12 @@ mod tests { make_analyzed_accounts_and_show_their_actual_disqualification_limits(account_seeds); let calculator_mock = CriterionCalculatorMock::default() .calculate_params(&calculate_params_arc) - .calculate_result(multiply_by_billion(4_600_000_000)) - .calculate_result(multiply_by_billion(4_200_000_000)) - .calculate_result(multiply_by_billion(3_800_000_000)); + .calculate_result(total_weight_account_1) + .calculate_result(total_weight_account_2) + .calculate_result(total_weight_account_3); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() - .criterion_calculator(calculator_mock) + .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1643,19 +1662,22 @@ mod tests { threshold_intercept_major: 100_000_000, permanent_debt_allowed_major: 40_000_000, }; + let total_weight_account_1 = multiply_by_billion(400_000_000); + // This account will have to fall off because of its lowest weight and that only two + // accounts can be kept according to the limitations detected in the transaction fee + // balance + let total_weight_account_2 = multiply_by_billion(200_000_000); + let total_weight_account_3 = multiply_by_billion(300_000_000); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, _actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiply_by_billion(400_000_000)) - // This account will have to fall off because of its lowest weight and that only two - // accounts can be kept according to the limitations detected in the transaction fee - // balance - .calculate_result(multiply_by_billion(120_000_000)) - .calculate_result(multiply_by_billion(250_000_000)); + .calculate_result(total_weight_account_1) + .calculate_result(total_weight_account_2) + .calculate_result(total_weight_account_3); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() - .criterion_calculator(calculator_mock) + .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); let agent_id_stamp = ArbitraryIdStamp::new(); @@ -1736,16 +1758,19 @@ mod tests { threshold_intercept_major: 100_000_000, permanent_debt_allowed_major: 35_000_000, }; + let total_weight_account_1 = multiply_by_billion(400_000_000); + let total_weight_account_2 = multiply_by_billion(200_000_000); + let total_weight_account_3 = multiply_by_billion(300_000_000); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiply_by_billion(400_000_000)) - .calculate_result(multiply_by_billion(200_000_000)) - .calculate_result(multiply_by_billion(300_000_000)); + .calculate_result(total_weight_account_1) + .calculate_result(total_weight_account_2) + .calculate_result(total_weight_account_3); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() - .criterion_calculator(calculator_mock) + .replace_calculators_with_mock(calculator_mock) .build(); let cw_service_fee_balance_minor = actual_disqualification_limits.account_1 + actual_disqualification_limits.account_3 @@ -1822,16 +1847,19 @@ mod tests { threshold_intercept_major: 400_000_000, permanent_debt_allowed_major: 100_000_000, }; + let total_weight_account_1 = multiply_by_billion(900_000_000); + let total_weight_account_2 = multiply_by_billion(1_100_000_000); + let total_weight_account_3 = multiply_by_billion(600_000_000); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiply_by_billion(900_000_000)) - .calculate_result(multiply_by_billion(1_100_000_000)) - .calculate_result(multiply_by_billion(600_000_000)); + .calculate_result(total_weight_account_1) + .calculate_result(total_weight_account_2) + .calculate_result(total_weight_account_3); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() - .criterion_calculator(calculator_mock) + .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); let service_fee_balance_in_minor_units = actual_disqualification_limits.account_1 @@ -1921,16 +1949,19 @@ mod tests { threshold_intercept_major: 90_000_000_000, permanent_debt_allowed_major: 20_000_000_000, }; + let total_weight_account_1 = multiply_by_billion(900_000_000_000); + let total_weight_account_2 = multiply_by_billion(500_000_000_000); + let total_weight_account_3 = multiply_by_billion(750_000_000_000); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiply_by_billion(900_000_000_000)) - .calculate_result(multiply_by_billion(500_000_000_000)) - .calculate_result(multiply_by_billion(750_000_000_000)); + .calculate_result(total_weight_account_1) + .calculate_result(total_weight_account_2) + .calculate_result(total_weight_account_3); let mut subject = PaymentAdjusterTestBuilder::default() .start_with_inner_null() - .criterion_calculator(calculator_mock) + .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); let service_fee_balance_in_minor = balance_account_1 - multiply_by_billion(10_000_000_000); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index c3e6967c0..6fe45887a 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -38,7 +38,7 @@ pub struct PaymentAdjusterTestBuilder { start_with_inner_null: bool, now_opt: Option, cw_service_fee_balance_minor_opt: Option, - criterion_calculator_mock_opt: Option, + mock_replacing_calculators_opt: Option, max_debt_above_threshold_in_qualified_payables_opt: Option, transaction_limit_count_opt: Option, logger_opt: Option, @@ -59,7 +59,7 @@ impl PaymentAdjusterTestBuilder { )); payment_adjuster.inner = inner; } - if let Some(calculator) = self.criterion_calculator_mock_opt { + if let Some(calculator) = self.mock_replacing_calculators_opt { payment_adjuster.calculators = vec![Box::new(calculator)] } payment_adjuster @@ -70,8 +70,11 @@ impl PaymentAdjusterTestBuilder { self } - pub fn criterion_calculator(mut self, calculator_mock: CriterionCalculatorMock) -> Self { - self.criterion_calculator_mock_opt = Some(calculator_mock); + pub fn replace_calculators_with_mock( + mut self, + calculator_mock: CriterionCalculatorMock, + ) -> Self { + self.mock_replacing_calculators_opt = Some(calculator_mock); self } From 490fe96b1c4aab475eeff1eb2c24d356b0743529 Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 7 Dec 2024 13:58:02 +0100 Subject: [PATCH 222/250] GH-711-review-one: mod.rs taken care of fully --- node/src/accountant/payment_adjuster/mod.rs | 247 +++++++++++--------- 1 file changed, 131 insertions(+), 116 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index c96f69df8..d321ed25c 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -674,10 +674,10 @@ mod tests { // Service fee balance > payments let input_1 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Right(vec![ + payable_account_balances_minor: vec![ multiply_by_billion(85), multiply_by_billion(15) - 1, - ]), + ], cw_balance_minor: multiply_by_billion(100), }), None, @@ -685,7 +685,7 @@ mod tests { // Service fee balance == payments let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![85, 15]), + payable_account_balances_minor: vec![85, 15], cw_balance_minor: multiply_by_billion(100), }), None, @@ -701,7 +701,7 @@ mod tests { Some(TestConfigForTransactionFees { gas_price_major: 100, number_of_accounts: 6, - estimated_transaction_fee_units_per_transaction: 53_000, + tx_computation_units: 53_000, cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor + 1, }), @@ -712,7 +712,7 @@ mod tests { Some(TestConfigForTransactionFees { gas_price_major: 100, number_of_accounts: 6, - estimated_transaction_fee_units_per_transaction: 53_000, + tx_computation_units: 53_000, cw_transaction_fee_balance_minor: transaction_fee_balance_exactly_required_minor, }), ); @@ -744,7 +744,7 @@ mod tests { Some(TestConfigForTransactionFees { gas_price_major: 100, number_of_accounts, - estimated_transaction_fee_units_per_transaction: 55_000, + tx_computation_units: 55_000, cw_transaction_fee_balance_minor: TRANSACTION_FEE_MARGIN .add_percent_to(multiply_by_billion(100 * 3 * 55_000)) - 1, @@ -785,10 +785,10 @@ mod tests { subject.logger = logger; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Right(vec![ + payable_account_balances_minor: vec![ multiply_by_billion(85), multiply_by_billion(15) + 1, - ]), + ], cw_balance_minor: multiply_by_billion(100), }), None, @@ -828,13 +828,13 @@ mod tests { let cw_transaction_fee_balance_minor = tx_fee_exactly_required_for_single_tx - 1; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![123]), + payable_account_balances_minor: vec![multiply_by_billion(123)], cw_balance_minor: multiply_by_billion(444), }), Some(TestConfigForTransactionFees { gas_price_major: 100, number_of_accounts, - estimated_transaction_fee_units_per_transaction: 55_000, + tx_computation_units: 55_000, cw_transaction_fee_balance_minor, }), ); @@ -865,7 +865,11 @@ mod tests { let test_name = "checking_three_accounts_happy_for_transaction_fee_but_service_fee_balance_throws_error"; let garbage_cw_service_fee_balance = u128::MAX; let service_fee_balances_config_opt = Some(TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![120, 300, 500]), + payable_account_balances_minor: vec![ + multiply_by_billion(120), + multiply_by_billion(300), + multiply_by_billion(500), + ], cw_balance_minor: garbage_cw_service_fee_balance, }); let (qualified_payables, boxed_agent) = @@ -910,13 +914,16 @@ mod tests { let number_of_accounts = 2; let (qualified_payables, agent) = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![200, 300]), + payable_account_balances_minor: vec![ + multiply_by_billion(200), + multiply_by_billion(300), + ], cw_balance_minor: 0, }), Some(TestConfigForTransactionFees { gas_price_major: 123, number_of_accounts, - estimated_transaction_fee_units_per_transaction: 55_000, + tx_computation_units: 55_000, cw_transaction_fee_balance_minor: 0, }), ); @@ -1549,8 +1556,8 @@ mod tests { permanent_debt_allowed_major: 1_111_111_111, }; let total_weight_account_1 = multiply_by_billion(400_000_000); - let total_weight_account_2 = multiply_by_billion(200_000_000); - let total_weight_account_3 = multiply_by_billion(300_000_000); + let total_weight_account_2 = multiply_by_billion(300_000_000); + let total_weight_account_3 = multiply_by_billion(200_000_000); let account_seeds = [ sketched_account_1.clone(), sketched_account_2.clone(), @@ -2109,10 +2116,10 @@ mod tests { // End of happy path section #[test] - fn late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient( - ) { + fn late_error_after_tx_fee_adjusted_but_rechecked_service_fee_found_fatally_insufficient() { init_test_logging(); - let test_name = "late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient"; + let test_name = + "late_error_after_tx_fee_adjusted_but_rechecked_service_fee_found_fatally_insufficient"; let now = SystemTime::now(); let balance_account_1 = multiply_by_billion(500_000_000_000); let sketched_account_1 = SketchedPayableAccount { @@ -2141,7 +2148,7 @@ mod tests { make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); let mut subject = PaymentAdjusterReal::new(); subject.logger = Logger::new(test_name); - // This is exactly the amount which will provoke an error + // This is exactly the amount which provokes an error let cw_service_fee_balance_minor = actual_disqualification_limits.account_2 - 1; let agent = BlockchainAgentMock::default() .service_fee_balance_minor_result(cw_service_fee_balance_minor); @@ -2198,41 +2205,48 @@ mod tests { } struct TestConfigForServiceFeeBalances { - // Either gwei or wei - account_balances: Either, Vec>, + payable_account_balances_minor: Vec, cw_balance_minor: u128, } + impl Default for TestConfigForServiceFeeBalances { + fn default() -> Self { + TestConfigForServiceFeeBalances { + payable_account_balances_minor: vec![1, 2], + cw_balance_minor: u64::MAX as u128, + } + } + } + struct TestConfigForTransactionFees { gas_price_major: u64, number_of_accounts: usize, - estimated_transaction_fee_units_per_transaction: u64, + tx_computation_units: u64, cw_transaction_fee_balance_minor: u128, } fn make_input_for_initial_check_tests( - service_fee_balances_config_opt: Option, - transaction_fee_config_opt: Option, + service_fee_config_opt: Option, + tx_fee_config_opt: Option, ) -> (Vec, Box) { - let service_fee_balances_config = - get_service_fee_balances_config(service_fee_balances_config_opt); - let balances_of_accounts_minor = - get_service_fee_balances(service_fee_balances_config.account_balances); + let service_fee_balances_config = service_fee_config_opt.unwrap_or_default(); + let balances_of_accounts_minor = service_fee_balances_config.payable_account_balances_minor; let accounts_count_from_sf_config = balances_of_accounts_minor.len(); - let transaction_fee_config = - get_transaction_fee_config(transaction_fee_config_opt, accounts_count_from_sf_config); - - let payable_accounts = prepare_payable_accounts( - transaction_fee_config.number_of_accounts, - accounts_count_from_sf_config, - balances_of_accounts_minor, - ); + let transaction_fee_config = tx_fee_config_opt + .unwrap_or_else(|| default_transaction_fee_config(accounts_count_from_sf_config)); + let payable_accounts = if transaction_fee_config.number_of_accounts + != accounts_count_from_sf_config + { + prepare_payable_accounts_from(Either::Left(transaction_fee_config.number_of_accounts)) + } else { + prepare_payable_accounts_from(Either::Right(balances_of_accounts_minor)) + }; let qualified_payables = prepare_qualified_payables(payable_accounts); - let blockchain_agent = make_agent( + let blockchain_agent = prepare_agent( transaction_fee_config.cw_transaction_fee_balance_minor, - transaction_fee_config.estimated_transaction_fee_units_per_transaction, + transaction_fee_config.tx_computation_units, transaction_fee_config.gas_price_major, service_fee_balances_config.cw_balance_minor, ); @@ -2240,47 +2254,25 @@ mod tests { (qualified_payables, blockchain_agent) } - fn get_service_fee_balances_config( - service_fee_balances_config_opt: Option, - ) -> TestConfigForServiceFeeBalances { - service_fee_balances_config_opt.unwrap_or_else(|| TestConfigForServiceFeeBalances { - account_balances: Either::Left(vec![1, 1]), - cw_balance_minor: u64::MAX as u128, - }) - } - fn get_service_fee_balances(account_balances: Either, Vec>) -> Vec { - match account_balances { - Either::Left(in_major) => in_major - .into_iter() - .map(|major| multiply_by_billion(major as u128)) - .collect(), - Either::Right(in_minor) => in_minor, - } - } - - fn get_transaction_fee_config( - transaction_fee_config_opt: Option, + fn default_transaction_fee_config( accounts_count_from_sf_config: usize, ) -> TestConfigForTransactionFees { - transaction_fee_config_opt.unwrap_or(TestConfigForTransactionFees { + TestConfigForTransactionFees { gas_price_major: 120, number_of_accounts: accounts_count_from_sf_config, - estimated_transaction_fee_units_per_transaction: 55_000, + tx_computation_units: 55_000, cw_transaction_fee_balance_minor: u128::MAX, - }) + } } - fn prepare_payable_accounts( - accounts_count_from_tf_config: usize, - accounts_count_from_sf_config: usize, - balances_of_accounts_minor: Vec, + fn prepare_payable_accounts_from( + balances_or_desired_accounts_count: Either>, ) -> Vec { - if accounts_count_from_tf_config != accounts_count_from_sf_config { - (0..accounts_count_from_tf_config) + match balances_or_desired_accounts_count { + Either::Left(desired_accounts_count) => (0..desired_accounts_count) .map(|idx| make_payable_account(idx as u64)) - .collect() - } else { - balances_of_accounts_minor + .collect(), + Either::Right(balances_of_accounts_minor) => balances_of_accounts_minor .into_iter() .enumerate() .map(|(idx, balance)| { @@ -2288,7 +2280,7 @@ mod tests { account.balance_wei = balance; account }) - .collect() + .collect(), } } @@ -2310,15 +2302,14 @@ mod tests { .collect() } - fn make_agent( + fn prepare_agent( cw_transaction_fee_minor: u128, - estimated_transaction_fee_units_per_transaction: u64, + tx_computation_units: u64, gas_price: u64, cw_service_fee_balance_minor: u128, ) -> Box { - let estimated_transaction_fee_per_transaction_minor = multiply_by_billion( - (estimated_transaction_fee_units_per_transaction * gas_price) as u128, - ); + let estimated_transaction_fee_per_transaction_minor = + multiply_by_billion((tx_computation_units * gas_price) as u128); let blockchain_agent = BlockchainAgentMock::default() .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) @@ -2354,7 +2345,8 @@ mod tests { ) } - // The following tests together prove the use of correct calculators in the production code + // The following tests put together evidences pointing to the use of correct calculators in + // the production code #[test] fn each_of_defaulted_calculators_returns_different_value() { @@ -2384,43 +2376,62 @@ mod tests { .into_iter() .map(|calculator| calculator.calculate(&qualified_payable, &context)) .fold(0, |previous_result, current_result| { - let min = (current_result * 97) / 100; - let max = (current_result * 97) / 100; + let slightly_less_than_current = (current_result * 97) / 100; + let slightly_more_than_current = (current_result * 103) / 100; assert_ne!(current_result, 0); - assert!(min <= previous_result || previous_result <= max); + assert!( + previous_result <= slightly_less_than_current + || slightly_more_than_current <= previous_result + ); current_result }); } + struct CalculatorTestScenario { + payable: QualifiedPayableAccount, + expected_weight: u128, + } + type InputMatrixConfigurator = fn( (QualifiedPayableAccount, QualifiedPayableAccount, SystemTime), - ) -> Vec<[(QualifiedPayableAccount, u128); 2]>; + ) -> Vec<[CalculatorTestScenario; 2]>; + + // This is the value that is computed if the account stays unmodified. Same for both nominal + // accounts. + const NOMINAL_ACCOUNT_WEIGHT: u128 = 8000000000000000; #[test] fn defaulted_calculators_react_on_correct_params() { - // When adding a test case for a new calculator, you need to make an array of inputs. Don't - // create brand-new accounts but clone the provided nominal accounts and modify them - // accordingly. Modify only those parameters that affect your calculator. + // When adding a test case for a new calculator, you need to make a two-dimensional array + // of inputs. Don't create brand-new accounts but clone the provided nominal accounts and + // modify them accordingly. Modify only those parameters that affect your calculator. // It's recommended to orientate the modifications rather positively (additions), because // there is a smaller chance you would run into some limit let input_matrix: InputMatrixConfigurator = |(nominal_account_1, nominal_account_2, _now)| { vec![ - // First test case: BalanceCalculator + // This puts only the first calculator on test, the BalanceCalculator... { let mut account_1 = nominal_account_1; account_1.bare_account.balance_wei += 123_456_789; let mut account_2 = nominal_account_2; account_2.bare_account.balance_wei += 999_999_999; - [(account_1, 8000001876543209), (account_2, 8000000999999999)] + [ + CalculatorTestScenario { + payable: account_1, + expected_weight: 8000001876543209, + }, + CalculatorTestScenario { + payable: account_2, + expected_weight: 8000000999999999, + }, + ] }, + // ...your newly added calculator should come here, and so on... ] }; - // This is the value that is computed if the account stays unmodified. Same for both nominal - // accounts. - let current_nominal_weight = 8000000000000000; - test_calculators_reactivity(input_matrix, current_nominal_weight) + test_calculators_reactivity(input_matrix) } #[derive(Clone, Copy)] @@ -2433,10 +2444,7 @@ mod tests { weight: u128, } - fn test_calculators_reactivity( - input_matrix_configurator: InputMatrixConfigurator, - nominal_weight: u128, - ) { + fn test_calculators_reactivity(input_matrix_configurator: InputMatrixConfigurator) { let calculators_count = PaymentAdjusterReal::default().calculators.len(); let now = SystemTime::now(); let cw_service_fee_balance_minor = multiply_by_billion(1_000_000); @@ -2445,7 +2453,10 @@ mod tests { now, cw_service_fee_balance_minor, ); - assert_eq!(template_computed_weight.common_weight, nominal_weight); + assert_eq!( + template_computed_weight.common_weight, + NOMINAL_ACCOUNT_WEIGHT + ); let mut template_accounts = template_accounts.to_vec(); let mut pop_account = || template_accounts.remove(0); let nominal_account_1 = pop_account(); @@ -2454,13 +2465,15 @@ mod tests { assert_eq!( input_matrix.len(), calculators_count, - "If you've recently added in a new calculator, you should add in its new test case to \ - this test. See the input matrix, it is the place where you should use the two accounts \ - you can clone. Make sure you modify only those parameters processed by your new calculator " + "Testing production code, the number of defaulted calculators should match the number \ + of test scenarios included in this test. If there are any missing, and you've recently \ + added in a new calculator, you should construct a new test case to it. See the input \ + matrix, it is the place where you should use the two accounts you can clone. Be careful \ + to modify only those parameters that are processed within your new calculator " ); test_accounts_from_input_matrix( - input_matrix, now, + input_matrix, cw_service_fee_balance_minor, template_computed_weight, ) @@ -2583,20 +2596,22 @@ mod tests { } fn test_accounts_from_input_matrix( - input_matrix: Vec<[(QualifiedPayableAccount, u128); 2]>, now: SystemTime, + input_matrix: Vec<[CalculatorTestScenario; 2]>, cw_service_fee_balance_minor: u128, template_computed_weight: TemplateComputedWeight, ) { - fn prepare_args_expected_weights_for_comparison( - (qualified_payable, expected_computed_weight): (QualifiedPayableAccount, u128), + fn prepare_inputs_with_expected_weights( + particular_calculator_scenario: CalculatorTestScenario, ) -> (QualifiedPayableAccount, ExpectedWeightWithWallet) { - let wallet = qualified_payable.bare_account.wallet.clone(); - let expected_weight = ExpectedWeightWithWallet { - wallet, - weight: expected_computed_weight, - }; - (qualified_payable, expected_weight) + let wallet = particular_calculator_scenario + .payable + .bare_account + .wallet + .clone(); + let weight = particular_calculator_scenario.expected_weight; + let expected_weight = ExpectedWeightWithWallet { wallet, weight }; + (particular_calculator_scenario.payable, expected_weight) } input_matrix @@ -2604,23 +2619,23 @@ mod tests { .map(|test_case| { test_case .into_iter() - .map(prepare_args_expected_weights_for_comparison) + .map(prepare_inputs_with_expected_weights) .collect::>() }) - .for_each(|qualified_payments_and_expected_computed_weights| { + .for_each(|qualified_payables_and_their_expected_weights| { let (qualified_payments, expected_computed_weights): (Vec<_>, Vec<_>) = - qualified_payments_and_expected_computed_weights + qualified_payables_and_their_expected_weights .into_iter() .unzip(); - let weighted_accounts = exercise_production_code_to_get_weighted_accounts( + let actual_weighted_accounts = exercise_production_code_to_get_weighted_accounts( qualified_payments, now, cw_service_fee_balance_minor, ); assert_results( - weighted_accounts, + actual_weighted_accounts, expected_computed_weights, template_computed_weight, ) @@ -2664,9 +2679,9 @@ mod tests { ); assert_ne!( actual_account.weight, previous_account_actual_weight, - "You were expected to prepare two accounts with at least slightly \ - different parameters. Therefore, the evenness of their weights is \ - highly improbable and suspicious." + "You were expected to prepare two accounts with at least slightly different \ + parameters. Therefore, the evenness of their weights is highly improbable and \ + suspicious." ); actual_account.weight }, From 5f038ddcec728e33dd57c2eb68e4bac89e5176bc Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 7 Dec 2024 19:27:52 +0100 Subject: [PATCH 223/250] GH-711-review-one: interim commit --- .../disqualification_arbiter.rs | 4 +- node/src/accountant/payment_adjuster/mod.rs | 1 - .../accounts_abstraction.rs | 58 ++----- .../payment_adjuster/service_fee_adjuster.rs | 158 ++++++++---------- 4 files changed, 85 insertions(+), 136 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index ff17ec3e9..85f05e91f 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -215,7 +215,6 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; - use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_non_guaranteed_unconfirmed_adjustment, PaymentAdjusterTestBuilder, }; @@ -477,8 +476,7 @@ mod tests { .build(); let weights_and_accounts = payment_adjuster.calculate_weights(analyzed_accounts); let subject = DisqualificationArbiter::default(); - let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); + let unconfirmed_adjustments = compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); let result = subject .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d321ed25c..0057e4021 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -569,7 +569,6 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, sum_as, }; - use crate::accountant::payment_adjuster::service_fee_adjuster::AdjustmentComputer; use crate::accountant::payment_adjuster::test_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, CriterionCalculatorMock, PaymentAdjusterTestBuilder, ServiceFeeAdjusterMock, diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs index 9c6ef091e..fb433a756 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs @@ -1,40 +1,34 @@ -use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; +// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeightedPayable; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; -pub trait DisqualificationAnalysableAccount: BalanceProvidingAccount -where - Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount, -{ - fn prepare_analyzable_account( - self, - disqualification_arbiter: &DisqualificationArbiter, - ) -> Product; -} - pub trait BalanceProvidingAccount { fn initial_balance_minor(&self) -> u128; } -pub trait DisqualificationLimitProvidingAccount { - fn disqualification_limit(&self) -> u128; +impl BalanceProvidingAccount for WeightedPayable { + fn initial_balance_minor(&self) -> u128 { + self.analyzed_account.initial_balance_minor() + } } -impl DisqualificationAnalysableAccount for WeightedPayable { - fn prepare_analyzable_account( - self, - _disqualification_arbiter: &DisqualificationArbiter, - ) -> WeightedPayable { - self +impl BalanceProvidingAccount for AnalyzedPayableAccount { + fn initial_balance_minor(&self) -> u128 { + self.qualified_as.initial_balance_minor() } } -impl BalanceProvidingAccount for WeightedPayable { +impl BalanceProvidingAccount for QualifiedPayableAccount { fn initial_balance_minor(&self) -> u128 { - self.analyzed_account.initial_balance_minor() + self.bare_account.balance_wei } } +pub trait DisqualificationLimitProvidingAccount { + fn disqualification_limit(&self) -> u128; +} + impl DisqualificationLimitProvidingAccount for WeightedPayable { fn disqualification_limit(&self) -> u128 { self.analyzed_account.disqualification_limit() @@ -46,25 +40,3 @@ impl DisqualificationLimitProvidingAccount for AnalyzedPayableAccount { self.disqualification_limit_minor } } - -impl BalanceProvidingAccount for AnalyzedPayableAccount { - fn initial_balance_minor(&self) -> u128 { - self.qualified_as.initial_balance_minor() - } -} - -impl DisqualificationAnalysableAccount for QualifiedPayableAccount { - fn prepare_analyzable_account( - self, - disqualification_arbiter: &DisqualificationArbiter, - ) -> AnalyzedPayableAccount { - let dsq_limit = disqualification_arbiter.calculate_disqualification_edge(&self); - AnalyzedPayableAccount::new(self, dsq_limit) - } -} - -impl BalanceProvidingAccount for QualifiedPayableAccount { - fn initial_balance_minor(&self) -> u128 { - self.bare_account.balance_wei - } -} diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index b1b9b6c92..dcc323b82 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -24,9 +24,8 @@ pub trait ServiceFeeAdjuster { ) -> AdjustmentIterationResult; } -pub struct ServiceFeeAdjusterReal { - adjustment_computer: AdjustmentComputer, -} +#[derive(Default)] +pub struct ServiceFeeAdjusterReal {} impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { fn perform_adjustment_by_service_fee( @@ -36,11 +35,10 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { - let unconfirmed_adjustments = self - .adjustment_computer - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + let unconfirmed_adjustments = + compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - let checked_accounts = Self::handle_winning_accounts(unconfirmed_adjustments); + let checked_accounts = Self::try_confirm_some_accounts(unconfirmed_adjustments); match checked_accounts { Either::Left(no_thriving_competitors) => Self::disqualify_single_account( @@ -53,51 +51,39 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { } } -impl Default for ServiceFeeAdjusterReal { - fn default() -> Self { - Self::new() - } -} - impl ServiceFeeAdjusterReal { - fn new() -> Self { - Self { - adjustment_computer: Default::default(), - } - } - - // The thin term "outweighed account" coma from a phenomenon with an account whose weight - // increases significantly based on a different parameter than the debt size. Untreated, we - // would wind up granting the account (much) more money than what it got recorded by + // The thin term "outweighed account" comes from a phenomenon related to an account whose weight + // increases significantly based on a different parameter than the debt size. Untreated, it + // could easily wind up with granting the account (much) more money than it was recorded by // the Accountant. // - // Each outweighed account, as well as any one with the proposed balance computed as a value - // between the disqualification limit of this account and the entire balance (originally - // requested), will gain instantly the same portion that equals the disqualification limit - // of this account. Anything below that is, in turn, considered unsatisfying, hence a reason - // for that account to go away simply disqualified. + // Each outweighed account, and even further, also any account with the proposed adjusted + // balance higher than its disqualification limit, will gain instantly equally to its + // disqualification limit. Anything below that is, in turn, considered unsatisfying, hence + // the reason to be disqualified. // - // The idea is that we want to spare as much as possible in the held means that could be - // continuously distributed among the rest of accounts until it is possible to adjust an account - // and unmeet the condition for a disqualification. + // The idea is that we try to spare as much as possible from the means that could be, if done + // wisely, better redistributed among the rest of accounts, as much as the wider group of them + // can be satisfied, even though just partially. // - // On the other hand, if it begins being clear that the remaining money can keep no other - // account up in the selection there is yet another operation to come where the already - // selected accounts are reviewed again in the order of their significance and some of - // the unused money is poured into them, which goes on until zero. - fn handle_winning_accounts( + // However, if it begins to be clear that the remaining money doesn't allow to keep any + // additional account in the selection, there is the next step to come, where the already + // selected accounts are reviewed again in the order of their significance resolved from + // remembering their weights from the earlier processing, and the unused money is poured into, + // until all resources are used. + fn try_confirm_some_accounts( unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let (thriving_competitors, losing_competitors) = + let (accounts_above_disq_limit, accounts_below_disq_limit) = Self::filter_and_process_winners(unconfirmed_adjustments); - if thriving_competitors.is_empty() { - Either::Left(losing_competitors) + if accounts_above_disq_limit.is_empty() { + Either::Left(accounts_below_disq_limit) } else { let remaining_undecided_accounts: Vec = - convert_collection(losing_competitors); + convert_collection(accounts_below_disq_limit); let pre_processed_decided_accounts: Vec = - convert_collection(thriving_competitors); + convert_collection(accounts_above_disq_limit); Either::Right(AdjustmentIterationResult { decided_accounts: pre_processed_decided_accounts, remaining_undecided_accounts, @@ -159,62 +145,56 @@ impl ServiceFeeAdjusterReal { } } -#[derive(Default)] -pub struct AdjustmentComputer {} - -impl AdjustmentComputer { - pub fn compute_unconfirmed_adjustments( - &self, - weighted_accounts: Vec, - unallocated_cw_service_fee_balance_minor: u128, - ) -> Vec { - let weights_total = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.weight - }); - let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; +fn compute_unconfirmed_adjustments( + weighted_accounts: Vec, + unallocated_cw_service_fee_balance_minor: u128, +) -> Vec { + let weights_total = sum_as(&weighted_accounts, |weighted_account| { + weighted_account.weight + }); + let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; - let multiplication_coefficient = - compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance); + let multiplication_coefficient = + compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance); - let proportional_cw_balance_fragment = Self::compute_proportional_cw_fragment( - cw_service_fee_balance, - weights_total, - multiplication_coefficient, - ); + let proportional_cw_balance_fragment = compute_proportional_cw_fragment( + cw_service_fee_balance, + weights_total, + multiplication_coefficient, + ); - let compute_proposed_adjusted_balance = - |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; + let compute_proposed_adjusted_balance = + |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; - weighted_accounts - .into_iter() - .map(|weighted_account| { - let proposed_adjusted_balance = - compute_proposed_adjusted_balance(weighted_account.weight); + weighted_accounts + .into_iter() + .map(|weighted_account| { + let proposed_adjusted_balance = + compute_proposed_adjusted_balance(weighted_account.weight); - proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); + proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); - UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) - }) - .collect() - } + UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) + }) + .collect() +} - fn compute_proportional_cw_fragment( - cw_service_fee_balance_minor: u128, - weights_total: u128, - multiplication_coefficient: u128, - ) -> u128 { - cw_service_fee_balance_minor - // Considered safe due to the process of getting this coefficient - .checked_mul(multiplication_coefficient) - .unwrap_or_else(|| { - panic!( - "mul overflow from {} * {}", - cw_service_fee_balance_minor, multiplication_coefficient - ) - }) - .checked_div(weights_total) - .expect("div overflow") - } +fn compute_proportional_cw_fragment( + cw_service_fee_balance_minor: u128, + weights_total: u128, + multiplication_coefficient: u128, +) -> u128 { + cw_service_fee_balance_minor + // Considered safe due to the process of getting this coefficient + .checked_mul(multiplication_coefficient) + .unwrap_or_else(|| { + panic!( + "mul overflow from {} * {}", + cw_service_fee_balance_minor, multiplication_coefficient + ) + }) + .checked_div(weights_total) + .expect("div overflow") } #[cfg(test)] From 2c8df5e393dc14021fa3361323d274819a70495b Mon Sep 17 00:00:00 2001 From: Bert Date: Sat, 7 Dec 2024 20:48:27 +0100 Subject: [PATCH 224/250] GH-711-review-one: more improvements --- .../disqualification_arbiter.rs | 130 ++++++++---------- node/src/accountant/payment_adjuster/mod.rs | 41 +----- .../payment_adjuster/service_fee_adjuster.rs | 80 ++++++++--- .../accountant/payment_adjuster/test_utils.rs | 2 +- 4 files changed, 121 insertions(+), 132 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 85f05e91f..2ec9e1fe9 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -207,23 +207,17 @@ impl DisqualificationGaugeReal { #[cfg(test)] mod tests { - use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::db_access_objects::utils::from_time_t; use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, DisqualificationGaugeReal, DisqualificationSuspectedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; - use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, PaymentAdjusterTestBuilder, + make_non_guaranteed_unconfirmed_adjustment, make_weighed_account, }; - use crate::accountant::test_utils::make_qualified_payables; - use crate::sub_lib::accountant::PaymentThresholds; - use crate::test_utils::make_wallet; + use itertools::Itertools; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; - use std::time::SystemTime; #[test] fn constants_are_correct() { @@ -421,73 +415,67 @@ mod tests { #[test] fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { - let test_name = - "only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration"; - let now = SystemTime::now(); - let cw_service_fee_balance_minor = 200_000_000_000; - let mut payment_thresholds = PaymentThresholds::default(); - payment_thresholds.permanent_debt_allowed_gwei = 10; - payment_thresholds.maturity_threshold_sec = 1_000; - payment_thresholds.threshold_interval_sec = 10_000; - let logger = Logger::new(test_name); - let wallet_1 = make_wallet("abc"); - // Meaning we're operating in the horizontal area defined by "permanent debt allowed" - let common_timestamp = from_time_t( - (payment_thresholds.maturity_threshold_sec - + payment_thresholds.threshold_interval_sec - + 1) as i64, - ); - let account_1 = PayableAccount { - wallet: wallet_1.clone(), - balance_wei: 120_000_000_000 + 1, - last_paid_timestamp: common_timestamp, - pending_payable_opt: None, - }; - let wallet_2 = make_wallet("def"); - let account_2 = PayableAccount { - wallet: wallet_2.clone(), - balance_wei: 120_000_000_000, - last_paid_timestamp: common_timestamp, - pending_payable_opt: None, - }; - let wallet_3 = make_wallet("ghi"); - // This account has the largest exceeding balance and therefore has the smallest weight - let account_3 = PayableAccount { - wallet: wallet_3.clone(), - balance_wei: 120_000_000_000 + 2, - last_paid_timestamp: common_timestamp, - pending_payable_opt: None, - }; - let wallet_4 = make_wallet("jkl"); - let account_4 = PayableAccount { - wallet: wallet_4.clone(), - balance_wei: 120_000_000_000 - 1, - last_paid_timestamp: common_timestamp, - pending_payable_opt: None, - }; - let accounts = vec![account_1, account_2, account_3, account_4]; - let qualified_payables = make_qualified_payables(accounts, &payment_thresholds, now); - let analyzed_accounts = convert_collection(qualified_payables); - let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); - let payment_adjuster = PaymentAdjusterTestBuilder::default() - .now(now) - .cw_service_fee_balance_minor(cw_service_fee_balance_minor) - .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) - .build(); - let weights_and_accounts = payment_adjuster.calculate_weights(analyzed_accounts); + let mut account_1 = make_weighed_account(123); + account_1.analyzed_account.disqualification_limit_minor = 1_000_000; + account_1.weight = 1000; + let mut account_2 = make_weighed_account(456); + account_2.analyzed_account.disqualification_limit_minor = 1_000_000; + account_2.weight = 1002; + let mut account_3 = make_weighed_account(789); + account_3.analyzed_account.disqualification_limit_minor = 1_000_000; + account_3.weight = 999; + let wallet_3 = account_3 + .analyzed_account + .qualified_as + .bare_account + .wallet + .clone(); + let mut account_4 = make_weighed_account(012); + account_4.analyzed_account.disqualification_limit_minor = 1_000_000; + account_4.weight = 1001; + // Notice that each proposed adjustment is below 1_000_000 which makes it clear all these + // accounts are nominated for disqualification, only one can be picked though + let seeds = vec![ + (account_1, 900_000), + (account_2, 920_000), + (account_3, 910_000), + (account_4, 930_000), + ]; + let unconfirmed_adjustments = seeds + .into_iter() + .map( + |(weighted_account, proposed_adjusted_balance_minor)| UnconfirmedAdjustment { + weighted_account, + proposed_adjusted_balance_minor, + }, + ) + .collect_vec(); let subject = DisqualificationArbiter::default(); - let unconfirmed_adjustments = compute_unconfirmed_adjustments(weights_and_accounts, cw_service_fee_balance_minor); - let result = subject - .find_an_account_to_disqualify_in_this_iteration(&unconfirmed_adjustments, &logger); + let result = subject.find_an_account_to_disqualify_in_this_iteration( + &unconfirmed_adjustments, + &Logger::new("test"), + ); - unconfirmed_adjustments.iter().for_each(|payable| { - // In the horizontal area, the disqualification limit equals to the entire debt size. - // This check says that every account qualified for disqualification but only one - // will eventually be chosen - assert!(payable.proposed_adjusted_balance_minor < payable.initial_balance_minor()) - }); assert_eq!(result, wallet_3); + // Hardening of the test with more formal checks + let all_wallets = unconfirmed_adjustments + .iter() + .map(|unconfirmed_adjustment| { + &unconfirmed_adjustment + .weighted_account + .analyzed_account + .qualified_as + .bare_account + .wallet + }) + .collect_vec(); + assert_eq!(all_wallets.len(), 4); + let wallets_as_wallet_3 = all_wallets + .iter() + .filter(|wallet| wallet == &&&wallet_3) + .collect_vec(); + assert_eq!(wallets_as_wallet_3.len(), 1); } fn make_unconfirmed_adjustments(weights: Vec) -> Vec { diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0057e4021..102bb3e4e 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -569,6 +569,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, sum_as, }; + use crate::accountant::payment_adjuster::service_fee_adjuster::test_helpers::illustrate_why_we_need_to_prevent_exceeding_the_original_value; use crate::accountant::payment_adjuster::test_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, CriterionCalculatorMock, PaymentAdjusterTestBuilder, ServiceFeeAdjusterMock, @@ -1119,8 +1120,7 @@ mod tests { // limit, chosen quite down, as the disqualification limit, for optimisation. In its // extremity, the naked algorithm of the reallocation of funds could have granted a value // above the original debt size, which is clearly unfair. - illustrate_that_we_need_to_prevent_exceeding_the_original_value( - subject, + illustrate_why_we_need_to_prevent_exceeding_the_original_value( cw_service_fee_balance_minor, weighted_payables.clone(), wallet_2, @@ -1149,43 +1149,6 @@ mod tests { assert!(result.is_empty()); } - fn illustrate_that_we_need_to_prevent_exceeding_the_original_value( - mut subject: PaymentAdjusterReal, - cw_service_fee_balance_minor: u128, - weighted_accounts: Vec, - wallet_of_expected_outweighed: Wallet, - original_balance_of_outweighed_account: u128, - ) { - let garbage_max_debt_above_threshold_in_qualified_payables = 123456789; - subject.inner = Box::new(PaymentAdjusterInnerReal::new( - SystemTime::now(), - None, - cw_service_fee_balance_minor, - garbage_max_debt_above_threshold_in_qualified_payables, - )); - let unconfirmed_adjustments = AdjustmentComputer::default() - .compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); - // The results are sorted from the biggest weights down - assert_eq!( - unconfirmed_adjustments[1].wallet(), - &wallet_of_expected_outweighed - ); - // To prevent unjust reallocation we used to secure a rule an account could never demand - // more than 100% of its size. - - // Later it was changed to a different policy, so called "outweighed" account gains - // automatically a balance equal to its disqualification limit. Still, later on it's very - // likely to be given a bit more from the remains languishing in the consuming wallet. - let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; - assert!( - proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), - "we expected the proposed balance at least 1.1 times bigger than the original balance \ - which is {} but it was {}", - original_balance_of_outweighed_account.separate_with_commas(), - proposed_adjusted_balance.separate_with_commas() - ); - } - #[test] fn adjustment_started_but_all_accounts_were_eliminated_anyway() { let test_name = "adjustment_started_but_all_accounts_were_eliminated_anyway"; diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index dcc323b82..66879305c 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -75,7 +75,7 @@ impl ServiceFeeAdjusterReal { unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { let (accounts_above_disq_limit, accounts_below_disq_limit) = - Self::filter_and_process_winners(unconfirmed_adjustments); + Self::filter_and_process_confirmable_accounts(unconfirmed_adjustments); if accounts_above_disq_limit.is_empty() { Either::Left(accounts_below_disq_limit) @@ -112,36 +112,37 @@ impl ServiceFeeAdjusterReal { } } - fn filter_and_process_winners( + fn filter_and_process_confirmable_accounts( unconfirmed_adjustments: Vec, ) -> ( Vec, Vec, ) { let init: (Vec, Vec) = (vec![], vec![]); - let (thriving_competitors, losing_competitors) = unconfirmed_adjustments.into_iter().fold( - init, - |(mut thriving_competitors, mut losing_competitors), current| { - let disqualification_limit = current.disqualification_limit_minor(); - if current.proposed_adjusted_balance_minor >= disqualification_limit { - thriving_competitor_found_diagnostics(¤t, disqualification_limit); - let mut adjusted = current; - adjusted.proposed_adjusted_balance_minor = disqualification_limit; - thriving_competitors.push(adjusted) - } else { - losing_competitors.push(current) - } - (thriving_competitors, losing_competitors) - }, - ); + let fold_guts = |(mut above_disq_limit, mut below_disq_limit): (Vec<_>, Vec<_>), + current: UnconfirmedAdjustment| { + let disqualification_limit = current.disqualification_limit_minor(); + if current.proposed_adjusted_balance_minor >= disqualification_limit { + thriving_competitor_found_diagnostics(¤t, disqualification_limit); + let mut adjusted = current; + adjusted.proposed_adjusted_balance_minor = disqualification_limit; + above_disq_limit.push(adjusted) + } else { + below_disq_limit.push(current) + } + (above_disq_limit, below_disq_limit) + }; - let decided_accounts = if thriving_competitors.is_empty() { + let (accounts_above_disq_limit, accounts_below_disq_limit) = + unconfirmed_adjustments.into_iter().fold(init, fold_guts); + + let decided_accounts = if accounts_above_disq_limit.is_empty() { vec![] } else { - convert_collection(thriving_competitors) + convert_collection(accounts_above_disq_limit) }; - (decided_accounts, losing_competitors) + (decided_accounts, accounts_below_disq_limit) } } @@ -279,7 +280,7 @@ mod tests { ]; let (thriving_competitors, losing_competitors) = - ServiceFeeAdjusterReal::filter_and_process_winners(unconfirmed_accounts); + ServiceFeeAdjusterReal::filter_and_process_confirmable_accounts(unconfirmed_accounts); assert_eq!(losing_competitors, vec![account_3, account_5]); let expected_adjusted_outweighed_accounts = vec![ @@ -314,3 +315,40 @@ mod tests { assert_eq!(thriving_competitors, expected_adjusted_outweighed_accounts) } } + +#[cfg(test)] +pub mod test_helpers { + use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeightedPayable; + use crate::accountant::payment_adjuster::service_fee_adjuster::compute_unconfirmed_adjustments; + use crate::sub_lib::wallet::Wallet; + use thousands::Separable; + + pub fn illustrate_why_we_need_to_prevent_exceeding_the_original_value( + cw_service_fee_balance_minor: u128, + weighted_accounts: Vec, + wallet_of_expected_outweighed: Wallet, + original_balance_of_outweighed_account: u128, + ) { + let unconfirmed_adjustments = + compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + // The results are sorted from the biggest weights down + assert_eq!( + unconfirmed_adjustments[1].wallet(), + &wallet_of_expected_outweighed + ); + // To prevent unjust reallocation we used to secure a rule an account could never demand + // more than 100% of its size. + + // Later it was changed to a different policy, so called "outweighed" account gains + // automatically a balance equal to its disqualification limit. Still, later on it's very + // likely to be given a bit more from the remains languishing in the consuming wallet. + let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; + assert!( + proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), + "we expected the proposed balance at least 1.1 times bigger than the original balance \ + which is {} but it was {}", + original_balance_of_outweighed_account.separate_with_commas(), + proposed_adjusted_balance.separate_with_commas() + ); + } +} diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 6fe45887a..bce1b0085 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -284,7 +284,7 @@ pub fn make_meaningless_analyzed_account_by_wallet( } pub fn make_weighed_account(n: u64) -> WeightedPayable { - WeightedPayable::new(make_meaningless_analyzed_account(n), 123456789) + WeightedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) } // Should stay test only!! From 4c04716a7a4c4f2c8c0b2292b85e82960b592e0f Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 11 Dec 2024 15:34:11 +0100 Subject: [PATCH 225/250] GH-711-review-one: using much multipliers for more ergonomiv number writes...and other stuff --- node/src/accountant/mod.rs | 10 +- .../disqualification_arbiter.rs | 12 +- .../logging_and_diagnostics/diagnostics.rs | 8 +- .../account_stages_conversions.rs | 61 ++-- .../miscellaneous/data_structures.rs | 18 +- .../miscellaneous/helper_functions.rs | 10 +- node/src/accountant/payment_adjuster/mod.rs | 300 +++++++++--------- .../accounts_abstraction.rs | 6 +- .../preparatory_analyser/mod.rs | 51 +-- .../payment_adjuster/service_fee_adjuster.rs | 125 ++++---- .../accountant/payment_adjuster/test_utils.rs | 57 +++- .../payable_scanner/agent_null.rs | 12 +- .../payable_scanner/agent_web3.rs | 4 +- .../payable_scanner/blockchain_agent.rs | 2 +- .../payable_scanner/test_utils.rs | 14 +- .../blockchain_interface_web3/mod.rs | 6 +- 16 files changed, 360 insertions(+), 336 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c5779f5e3..96d450047 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1792,15 +1792,15 @@ mod tests { log_handler.exists_log_containing(&format!( "WARN: {test_name}: Payment adjustment has not produced any executable payments. Add \ more funds into your consuming wallet in order to become able to repay already expired \ - liabilities as the creditors would respond by delinquency bans otherwise. Details: \ - The adjustment algorithm had to eliminate each payable from the recently urged payment \ - due to lack of resources" + liabilities as the creditors would respond by delinquency bans otherwise. Details: The \ + payments adjusting process failed to find any combination of payables that can be paid \ + immediately with the finances provided" )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - The cause appears to be in competence of the user" + "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for \ + payments. The cause appears to be in competence of the user" )); } diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 2ec9e1fe9..7e78e815e 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -123,7 +123,7 @@ impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> fn from(unconfirmed_account: &'unconfirmed_accounts UnconfirmedAdjustment) -> Self { DisqualificationSuspectedAccount { wallet: unconfirmed_account.wallet(), - weight: unconfirmed_account.weighted_account.weight, + weight: unconfirmed_account.weighed_account.weight, proposed_adjusted_balance_minor: unconfirmed_account.proposed_adjusted_balance_minor, disqualification_limit_minor: unconfirmed_account.disqualification_limit_minor(), initial_account_balance_minor: unconfirmed_account.initial_balance_minor(), @@ -378,7 +378,7 @@ mod tests { let mut account = make_non_guaranteed_unconfirmed_adjustment(444); account.proposed_adjusted_balance_minor = 1_000_000_000; account - .weighted_account + .weighed_account .analyzed_account .disqualification_limit_minor = 1_000_000_000; let accounts = vec![account]; @@ -444,8 +444,8 @@ mod tests { let unconfirmed_adjustments = seeds .into_iter() .map( - |(weighted_account, proposed_adjusted_balance_minor)| UnconfirmedAdjustment { - weighted_account, + |(weighed_account, proposed_adjusted_balance_minor)| UnconfirmedAdjustment { + weighed_account, proposed_adjusted_balance_minor, }, ) @@ -463,7 +463,7 @@ mod tests { .iter() .map(|unconfirmed_adjustment| { &unconfirmed_adjustment - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account @@ -484,7 +484,7 @@ mod tests { .enumerate() .map(|(idx, weight)| { let mut account = make_non_guaranteed_unconfirmed_adjustment(idx as u64); - account.weighted_account.weight = weight; + account.weighed_account.weight = weight; account }) .collect() diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 38184ad8b..fc0445308 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -95,7 +95,7 @@ pub mod ordinary_diagnostic_functions { use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeighedPayable, }; use crate::sub_lib::wallet::Wallet; use thousands::Separable; @@ -130,11 +130,11 @@ pub mod ordinary_diagnostic_functions { } pub fn minimal_acceptable_balance_assigned_diagnostics( - weighted_account: &WeightedPayable, + weighed_account: &WeighedPayable, disqualification_limit: u128, ) { diagnostics!( - weighted_account.wallet(), + weighed_account.wallet(), "MINIMAL ACCEPTABLE BALANCE ASSIGNED", "Used disqualification limit for given account {}", disqualification_limit.separate_with_commas() @@ -167,7 +167,7 @@ pub mod ordinary_diagnostic_functions { } pub fn proposed_adjusted_balance_diagnostics( - account: &WeightedPayable, + account: &WeighedPayable, proposed_adjusted_balance: u128, ) { diagnostics!( diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index eb86e4289..2007975d1 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -3,7 +3,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::minimal_acceptable_balance_assigned_diagnostics; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeighedPayable, }; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; use crate::accountant::QualifiedPayableAccount; @@ -18,9 +18,9 @@ impl From for PayableAccount { // Transaction fee adjustment just done, but no need to go off with the other fee, so we only // extract the original payable accounts of those retained after the adjustment. PA is done and can // begin to return. -impl From for PayableAccount { - fn from(weighted_account: WeightedPayable) -> Self { - weighted_account.analyzed_account.qualified_as.bare_account +impl From for PayableAccount { + fn from(weighed_account: WeighedPayable) -> Self { + weighed_account.analyzed_account.qualified_as.bare_account } } @@ -34,10 +34,10 @@ impl From for PayableAccount { } // Makes "remaining unresolved accounts" ready for another recursion that always begins with -// structures in the type of WeightedPayable -impl From for WeightedPayable { +// structures in the type of WeighedPayable +impl From for WeighedPayable { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { - unconfirmed_adjustment.weighted_account + unconfirmed_adjustment.weighed_account } } @@ -46,9 +46,9 @@ impl From for AdjustedAccountBeforeFinalization { fn from(unconfirmed_adjustment: UnconfirmedAdjustment) -> Self { let proposed_adjusted_balance_minor = unconfirmed_adjustment.proposed_adjusted_balance_minor; - let weight = unconfirmed_adjustment.weighted_account.weight; + let weight = unconfirmed_adjustment.weighed_account.weight; let original_account = unconfirmed_adjustment - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account; @@ -64,15 +64,12 @@ impl From for AdjustedAccountBeforeFinalization { // When we detect that the upcoming iterations will begin with a surplus in the remaining // unallocated CW service fee, therefore the remaining accounts' balances are automatically granted // an amount that equals to their disqualification limit (and can be later provided with even more) -impl From for AdjustedAccountBeforeFinalization { - fn from(weighted_account: WeightedPayable) -> Self { - let limited_adjusted_balance = weighted_account.disqualification_limit(); - minimal_acceptable_balance_assigned_diagnostics( - &weighted_account, - limited_adjusted_balance, - ); - let weight = weighted_account.weight; - let original_account = weighted_account.analyzed_account.qualified_as.bare_account; +impl From for AdjustedAccountBeforeFinalization { + fn from(weighed_account: WeighedPayable) -> Self { + let limited_adjusted_balance = weighed_account.disqualification_limit(); + minimal_acceptable_balance_assigned_diagnostics(&weighed_account, limited_adjusted_balance); + let weight = weighed_account.weight; + let original_account = weighed_account.analyzed_account.qualified_as.bare_account; AdjustedAccountBeforeFinalization::new(original_account, weight, limited_adjusted_balance) } } @@ -81,7 +78,7 @@ impl From for AdjustedAccountBeforeFinalization { mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeightedPayable, + AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeighedPayable, }; use crate::accountant::test_utils::{make_meaningless_qualified_payable, make_payable_account}; use crate::accountant::AnalyzedPayableAccount; @@ -102,7 +99,7 @@ mod tests { assert_eq!(result, original_payable_account) } - fn prepare_weighted_account(payable_account: PayableAccount) -> WeightedPayable { + fn prepare_weighed_account(payable_account: PayableAccount) -> WeighedPayable { let garbage_disqualification_limit = 333_333_333; let garbage_weight = 777_777_777; let mut analyzed_account = AnalyzedPayableAccount::new( @@ -110,29 +107,29 @@ mod tests { garbage_disqualification_limit, ); analyzed_account.qualified_as.bare_account = payable_account; - WeightedPayable::new(analyzed_account, garbage_weight) + WeighedPayable::new(analyzed_account, garbage_weight) } #[test] - fn conversation_between_weighted_payable_and_standard_payable_account() { + fn conversation_between_weighed_payable_and_standard_payable_account() { let original_payable_account = make_payable_account(345); - let weighted_account = prepare_weighted_account(original_payable_account.clone()); + let weighed_account = prepare_weighed_account(original_payable_account.clone()); - let result = PayableAccount::from(weighted_account); + let result = PayableAccount::from(weighed_account); assert_eq!(result, original_payable_account) } #[test] - fn conversion_between_weighted_payable_and_non_finalized_account() { + fn conversion_between_weighed_payable_and_non_finalized_account() { let original_payable_account = make_payable_account(123); - let mut weighted_account = prepare_weighted_account(original_payable_account.clone()); - weighted_account + let mut weighed_account = prepare_weighed_account(original_payable_account.clone()); + weighed_account .analyzed_account .disqualification_limit_minor = 200_000_000; - weighted_account.weight = 78910; + weighed_account.weight = 78910; - let result = AdjustedAccountBeforeFinalization::from(weighted_account); + let result = AdjustedAccountBeforeFinalization::from(weighed_account); let expected_result = AdjustedAccountBeforeFinalization::new(original_payable_account, 78910, 200_000_000); @@ -143,12 +140,12 @@ mod tests { fn conversion_between_unconfirmed_adjustment_and_non_finalized_account() { let mut original_payable_account = make_payable_account(123); original_payable_account.balance_wei = 200_000_000; - let mut weighted_account = prepare_weighted_account(original_payable_account.clone()); + let mut weighed_account = prepare_weighed_account(original_payable_account.clone()); let weight = 321654; - weighted_account.weight = weight; + weighed_account.weight = weight; let proposed_adjusted_balance_minor = 111_222_333; let unconfirmed_adjustment = - UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance_minor); + UnconfirmedAdjustment::new(weighed_account, proposed_adjusted_balance_minor); let result = AdjustedAccountBeforeFinalization::from(unconfirmed_adjustment); diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 7d9cdf147..23f3d90af 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -6,12 +6,12 @@ use crate::sub_lib::wallet::Wallet; use web3::types::U256; #[derive(Clone, Debug, PartialEq, Eq)] -pub struct WeightedPayable { +pub struct WeighedPayable { pub analyzed_account: AnalyzedPayableAccount, pub weight: u128, } -impl WeightedPayable { +impl WeighedPayable { pub fn new(analyzed_account: AnalyzedPayableAccount, weight: u128) -> Self { Self { analyzed_account, @@ -31,7 +31,7 @@ impl WeightedPayable { #[derive(Debug, PartialEq, Eq)] pub struct AdjustmentIterationResult { pub decided_accounts: Vec, - pub remaining_undecided_accounts: Vec, + pub remaining_undecided_accounts: Vec, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -57,28 +57,28 @@ impl AdjustedAccountBeforeFinalization { #[derive(Debug, PartialEq, Eq, Clone)] pub struct UnconfirmedAdjustment { - pub weighted_account: WeightedPayable, + pub weighed_account: WeighedPayable, pub proposed_adjusted_balance_minor: u128, } impl UnconfirmedAdjustment { - pub fn new(weighted_account: WeightedPayable, proposed_adjusted_balance_minor: u128) -> Self { + pub fn new(weighed_account: WeighedPayable, proposed_adjusted_balance_minor: u128) -> Self { Self { - weighted_account, + weighed_account, proposed_adjusted_balance_minor, } } pub fn wallet(&self) -> &Wallet { - self.weighted_account.wallet() + self.weighed_account.wallet() } pub fn initial_balance_minor(&self) -> u128 { - self.weighted_account.initial_balance_minor() + self.weighed_account.initial_balance_minor() } pub fn disqualification_limit_minor(&self) -> u128 { - self.weighted_account + self.weighed_account .analyzed_account .disqualification_limit_minor } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 911d721a6..e5632f12a 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -6,7 +6,7 @@ use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeighedPayable}; use crate::accountant::{AnalyzedPayableAccount}; use itertools::{Either, Itertools}; @@ -28,10 +28,10 @@ where } pub fn eliminate_accounts_by_tx_fee_limit( - weighted_accounts: Vec, + weighed_accounts: Vec, affordable_transaction_count: u16, -) -> Vec { - let sorted_accounts = sort_in_descending_order_by_weights(weighted_accounts); +) -> Vec { + let sorted_accounts = sort_in_descending_order_by_weights(weighed_accounts); diagnostics!( "ACCOUNTS CUTBACK FOR TRANSACTION FEE", @@ -49,7 +49,7 @@ pub fn eliminate_accounts_by_tx_fee_limit( .collect() } -fn sort_in_descending_order_by_weights(unsorted: Vec) -> Vec { +fn sort_in_descending_order_by_weights(unsorted: Vec) -> Vec { unsorted .into_iter() .sorted_by(|account_a, account_b| Ord::cmp(&account_b.weight, &account_a.weight)) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 102bb3e4e..a28e2fa33 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -27,7 +27,7 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeightedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeighedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ eliminate_accounts_by_tx_fee_limit, exhaust_cw_balance_entirely, find_largest_exceeding_balance, @@ -191,8 +191,8 @@ impl PaymentAdjusterReal { &mut self, analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { - let weighted_accounts = self.calculate_weights(analyzed_accounts); - let processed_accounts = self.resolve_initial_adjustment_dispatch(weighted_accounts)?; + let weighed_accounts = self.calculate_weights(analyzed_accounts); + let processed_accounts = self.resolve_initial_adjustment_dispatch(weighed_accounts)?; if no_affordable_accounts_found(&processed_accounts) { return Err(PaymentAdjusterError::RecursionDrainedAllAccounts); @@ -214,23 +214,23 @@ impl PaymentAdjusterReal { fn resolve_initial_adjustment_dispatch( &mut self, - weighted_payables: Vec, + weighed_payables: Vec, ) -> Result< Either, Vec>, PaymentAdjusterError, > { if let Some(limit) = self.inner.transaction_fee_count_limit_opt() { - return self.begin_with_adjustment_by_transaction_fee(weighted_payables, limit); + return self.begin_with_adjustment_by_transaction_fee(weighed_payables, limit); } - Ok(Either::Left(self.propose_possible_adjustment_recursively( - weighted_payables, - ))) + Ok(Either::Left( + self.propose_possible_adjustment_recursively(weighed_payables), + )) } fn begin_with_adjustment_by_transaction_fee( &mut self, - weighed_accounts: Vec, + weighed_accounts: Vec, transaction_count_limit: u16, ) -> Result< Either, Vec>, @@ -243,26 +243,26 @@ impl PaymentAdjusterReal { let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); - let weighted_accounts_affordable_by_transaction_fee = + let weighed_accounts_affordable_by_transaction_fee = eliminate_accounts_by_tx_fee_limit(weighed_accounts, transaction_count_limit); let cw_service_fee_balance_minor = self.inner.original_cw_service_fee_balance_minor(); if self.analyzer.recheck_if_service_fee_adjustment_is_needed( - &weighted_accounts_affordable_by_transaction_fee, + &weighed_accounts_affordable_by_transaction_fee, cw_service_fee_balance_minor, error_factory, &self.logger, )? { let final_set_before_exhausting_cw_balance = self .propose_possible_adjustment_recursively( - weighted_accounts_affordable_by_transaction_fee, + weighed_accounts_affordable_by_transaction_fee, ); Ok(Either::Left(final_set_before_exhausting_cw_balance)) } else { let accounts_not_needing_adjustment = - convert_collection(weighted_accounts_affordable_by_transaction_fee); + convert_collection(weighed_accounts_affordable_by_transaction_fee); Ok(Either::Right(accounts_not_needing_adjustment)) } @@ -270,7 +270,7 @@ impl PaymentAdjusterReal { fn propose_possible_adjustment_recursively( &mut self, - weighed_accounts: Vec, + weighed_accounts: Vec, ) -> Vec { diagnostics!( "\nUNRESOLVED ACCOUNTS IN CURRENT ITERATION:", @@ -323,12 +323,12 @@ impl PaymentAdjusterReal { fn is_cw_balance_enough_to_remaining_accounts( &self, - remaining_undecided_accounts: &[WeightedPayable], + remaining_undecided_accounts: &[WeighedPayable], ) -> bool { let unallocated_cw_service_fee_balance = self.inner.unallocated_cw_service_fee_balance_minor(); - let minimum_sum_required: u128 = sum_as(remaining_undecided_accounts, |weighted_account| { - weighted_account.disqualification_limit() + let minimum_sum_required: u128 = sum_as(remaining_undecided_accounts, |weighed_account| { + weighed_account.disqualification_limit() }); minimum_sum_required <= unallocated_cw_service_fee_balance } @@ -341,7 +341,7 @@ impl PaymentAdjusterReal { previously_decided_accounts } - fn calculate_weights(&self, accounts: Vec) -> Vec { + fn calculate_weights(&self, accounts: Vec) -> Vec { self.apply_criteria(self.calculators.as_slice(), accounts) } @@ -349,7 +349,7 @@ impl PaymentAdjusterReal { &self, criteria_calculators: &[Box], qualified_accounts: Vec, - ) -> Vec { + ) -> Vec { qualified_accounts .into_iter() .map(|payable| { @@ -372,7 +372,7 @@ impl PaymentAdjusterReal { summed_up }); - WeightedPayable::new(payable, weight) + WeighedPayable::new(payable, weight) }) .collect() } @@ -551,8 +551,8 @@ impl Display for PaymentAdjusterError { ), PaymentAdjusterError::RecursionDrainedAllAccounts => write!( f, - "The payment adjuster wasn't able to compose any combination of payables that can \ - be paid immediately with provided finances." + "The payments adjusting process failed to find any combination of payables that \ + can be paid immediately with the finances provided." ), } } @@ -564,7 +564,7 @@ mod tests { use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, WeightedPayable, + AdjustmentIterationResult, WeighedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, sum_as, @@ -572,7 +572,8 @@ mod tests { use crate::accountant::payment_adjuster::service_fee_adjuster::test_helpers::illustrate_why_we_need_to_prevent_exceeding_the_original_value; use crate::accountant::payment_adjuster::test_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, - CriterionCalculatorMock, PaymentAdjusterTestBuilder, ServiceFeeAdjusterMock, + multiply_by_billion_concise, multiply_by_quintillion, multiply_by_quintillion_concise, + CriterionCalculatorMock, PaymentAdjusterBuilder, ServiceFeeAdjusterMock, MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ @@ -900,7 +901,7 @@ mod tests { number_of_accounts: 3, transaction_fee_opt: None, service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: 920_000_000_000, + total_service_fee_required_minor: multiply_by_billion(920), cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance }) } @@ -957,7 +958,7 @@ mod tests { PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts: 4, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ - per_transaction_requirement_minor: 70_000_000_000_000, + per_transaction_requirement_minor: multiply_by_billion(70_000), cw_transaction_fee_balance_minor: U256::from(90_000), }), service_fee_opt: None @@ -1009,8 +1010,8 @@ mod tests { 333,333 wei."), ( PaymentAdjusterError::RecursionDrainedAllAccounts, - "The payment adjuster wasn't able to compose any combination of payables that can \ - be paid immediately with provided finances.", + "The payments adjusting process failed to find any combination of payables that \ + can be paid immediately with the finances provided.", ), ]; let inputs_count = inputs.len(); @@ -1101,17 +1102,17 @@ mod tests { let largest_exceeding_balance = (balance_1 - account_1.qualified_as.payment_threshold_intercept_minor) .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .cw_service_fee_balance_minor(cw_service_fee_balance_minor) .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) .build(); - let weighted_payables = vec![ - WeightedPayable::new(account_1, weight_account_1), - WeightedPayable::new(account_2, weighed_account_2), + let weighed_payables = vec![ + WeighedPayable::new(account_1, weight_account_1), + WeighedPayable::new(account_2, weighed_account_2), ]; let mut result = subject - .resolve_initial_adjustment_dispatch(weighted_payables.clone()) + .resolve_initial_adjustment_dispatch(weighed_payables.clone()) .unwrap() .left() .unwrap(); @@ -1122,15 +1123,15 @@ mod tests { // above the original debt size, which is clearly unfair. illustrate_why_we_need_to_prevent_exceeding_the_original_value( cw_service_fee_balance_minor, - weighted_payables.clone(), + weighed_payables.clone(), wallet_2, balance_2, ); - let payable_account_1 = &weighted_payables[0] + let payable_account_1 = &weighed_payables[0] .analyzed_account .qualified_as .bare_account; - let payable_account_2 = &weighted_payables[1] + let payable_account_2 = &weighed_payables[1] .analyzed_account .qualified_as .bare_account; @@ -1159,21 +1160,21 @@ mod tests { // from the intercept let common_unimportant_age_for_accounts = now.checked_sub(Duration::from_secs(200_000)).unwrap(); - let balance_1 = multiply_by_billion(3_000_000); + let balance_1 = multiply_by_quintillion_concise(0.003); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, last_paid_timestamp: common_unimportant_age_for_accounts, pending_payable_opt: None, }; - let balance_2 = multiply_by_billion(2_000_000); + let balance_2 = multiply_by_quintillion_concise(0.002); let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, last_paid_timestamp: common_unimportant_age_for_accounts, pending_payable_opt: None, }; - let balance_3 = multiply_by_billion(5_000_000); + let balance_3 = multiply_by_quintillion_concise(0.005); let account_3 = PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, @@ -1184,10 +1185,10 @@ mod tests { let qualified_payables = make_qualified_payables(payables, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now); let calculator_mock = CriterionCalculatorMock::default() - .calculate_result(multiply_by_billion(2_000_000_000)) + .calculate_result(multiply_by_quintillion(2)) .calculate_result(0) .calculate_result(0); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .logger(Logger::new(test_name)) .build(); @@ -1270,21 +1271,21 @@ mod tests { // the disqualification limit in each account let common_age_for_accounts_as_unimportant = now.checked_sub(Duration::from_secs(200_000)).unwrap(); - let balance_1 = multiply_by_billion(80_000_000_000); + let balance_1 = multiply_by_quintillion(80); let account_1 = PayableAccount { wallet: make_wallet("abc"), balance_wei: balance_1, last_paid_timestamp: common_age_for_accounts_as_unimportant, pending_payable_opt: None, }; - let balance_2 = multiply_by_billion(60_000_000_000); + let balance_2 = multiply_by_quintillion(60); let account_2 = PayableAccount { wallet: make_wallet("def"), balance_wei: balance_2, last_paid_timestamp: common_age_for_accounts_as_unimportant, pending_payable_opt: None, }; - let balance_3 = multiply_by_billion(40_000_000_000); + let balance_3 = multiply_by_quintillion(40); let account_3 = PayableAccount { wallet: make_wallet("ghi"), balance_wei: balance_3, @@ -1300,13 +1301,13 @@ mod tests { // of the attached assertions below), this must mean that disqualification has to be // ruled in the first round, where the first account is eventually eliminated for its // lowest weight. - .calculate_result(multiply_by_billion(10_000_000_000)) - .calculate_result(multiply_by_billion(30_000_000_000)) - .calculate_result(multiply_by_billion(50_000_000_000)); + .calculate_result(multiply_by_quintillion(10)) + .calculate_result(multiply_by_quintillion(30)) + .calculate_result(multiply_by_quintillion(50)); let sum_of_disqualification_limits = sum_as(&analyzed_accounts, |account| { account.disqualification_limit_minor }); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1408,9 +1409,9 @@ mod tests { // Nothing blew up from the giant inputs, the test was a success } - fn make_weighted_payable(n: u64, initial_balance_minor: u128) -> WeightedPayable { + fn make_weighed_payable(n: u64, initial_balance_minor: u128) -> WeighedPayable { let mut payable = - WeightedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); + WeighedPayable::new(make_meaningless_analyzed_account(111), n as u128 * 1234); payable .analyzed_account .qualified_as @@ -1432,23 +1433,23 @@ mod tests { SystemTime::now(), ); let mut payable_1 = - make_weighted_payable(111, 2 * initial_disqualification_limit_for_each_account); + make_weighed_payable(111, 2 * initial_disqualification_limit_for_each_account); payable_1.analyzed_account.disqualification_limit_minor = initial_disqualification_limit_for_each_account; let mut payable_2 = - make_weighted_payable(222, 3 * initial_disqualification_limit_for_each_account); + make_weighed_payable(222, 3 * initial_disqualification_limit_for_each_account); payable_2.analyzed_account.disqualification_limit_minor = initial_disqualification_limit_for_each_account; - let weighted_payables = vec![payable_1, payable_2]; + let weighed_payables = vec![payable_1, payable_2]; - let result = subject.is_cw_balance_enough_to_remaining_accounts(&weighted_payables); + let result = subject.is_cw_balance_enough_to_remaining_accounts(&weighed_payables); assert_eq!(result, expected_result) } #[test] fn untaken_balance_is_equal_to_sum_of_disqualification_limits_in_remaining_accounts() { - let disqualification_limit_for_each_account = 5_000_000_000; + let disqualification_limit_for_each_account = multiply_by_billion(5); let untaken_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account; @@ -1461,7 +1462,7 @@ mod tests { #[test] fn untaken_balance_is_more_than_sum_of_disqualification_limits_in_remaining_accounts() { - let disqualification_limit_for_each_account = 5_000_000_000; + let disqualification_limit_for_each_account = multiply_by_billion(5); let untaken_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account + 1; @@ -1474,7 +1475,7 @@ mod tests { #[test] fn untaken_balance_is_less_than_sum_of_disqualification_limits_in_remaining_accounts() { - let disqualification_limit_for_each_account = 5_000_000_000; + let disqualification_limit_for_each_account = multiply_by_billion(5); let untaken_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account - 1; @@ -1517,9 +1518,9 @@ mod tests { threshold_intercept_major: 2_000_000_000, permanent_debt_allowed_major: 1_111_111_111, }; - let total_weight_account_1 = multiply_by_billion(400_000_000); - let total_weight_account_2 = multiply_by_billion(300_000_000); - let total_weight_account_3 = multiply_by_billion(200_000_000); + let total_weight_account_1 = multiply_by_quintillion_concise(0.4); + let total_weight_account_2 = multiply_by_quintillion_concise(0.3); + let total_weight_account_3 = multiply_by_quintillion_concise(0.2); let account_seeds = [ sketched_account_1.clone(), sketched_account_2.clone(), @@ -1532,7 +1533,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1615,28 +1616,28 @@ mod tests { let now = SystemTime::now(); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", - balance_minor: multiply_by_billion(111_000_000), - threshold_intercept_major: 100_000_000, - permanent_debt_allowed_major: 20_000_000, + balance_minor: multiply_by_quintillion_concise(0.111), + threshold_intercept_major: multiply_by_billion_concise(0.1), + permanent_debt_allowed_major: multiply_by_billion_concise(0.02), }; let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", - balance_minor: multiply_by_billion(300_000_000), - threshold_intercept_major: 120_000_000, - permanent_debt_allowed_major: 50_000_000, + balance_minor: multiply_by_quintillion_concise(0.3), + threshold_intercept_major: multiply_by_billion_concise(0.12), + permanent_debt_allowed_major: multiply_by_billion_concise(0.05), }; let sketched_account_3 = SketchedPayableAccount { wallet_addr_seed: "ghi", balance_minor: multiply_by_billion(222_222_222), - threshold_intercept_major: 100_000_000, - permanent_debt_allowed_major: 40_000_000, + threshold_intercept_major: multiply_by_billion_concise(0.1), + permanent_debt_allowed_major: multiply_by_billion_concise(0.04), }; - let total_weight_account_1 = multiply_by_billion(400_000_000); + let total_weight_account_1 = multiply_by_quintillion_concise(0.4); // This account will have to fall off because of its lowest weight and that only two // accounts can be kept according to the limitations detected in the transaction fee // balance - let total_weight_account_2 = multiply_by_billion(200_000_000); - let total_weight_account_3 = multiply_by_billion(300_000_000); + let total_weight_account_2 = multiply_by_quintillion_concise(0.2); + let total_weight_account_3 = multiply_by_quintillion_concise(0.3); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, _actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); @@ -1644,7 +1645,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1706,30 +1707,30 @@ mod tests { // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); let now = SystemTime::now(); - let balance_account_1 = multiply_by_billion(111_000_000); + let balance_account_1 = multiply_by_quintillion_concise(0.111); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", balance_minor: balance_account_1, - threshold_intercept_major: 50_000_000, - permanent_debt_allowed_major: 10_000_000, + threshold_intercept_major: multiply_by_billion_concise(0.05), + permanent_debt_allowed_major: multiply_by_billion_concise(0.010), }; - let balance_account_2 = multiply_by_billion(333_000_000); + let balance_account_2 = multiply_by_quintillion_concise(0.333); let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", balance_minor: balance_account_2, - threshold_intercept_major: 200_000_000, - permanent_debt_allowed_major: 50_000_000, + threshold_intercept_major: multiply_by_billion_concise(0.2), + permanent_debt_allowed_major: multiply_by_billion_concise(0.05), }; - let balance_account_3 = multiply_by_billion(222_000_000); + let balance_account_3 = multiply_by_quintillion_concise(0.222); let sketched_account_3 = SketchedPayableAccount { wallet_addr_seed: "ghi", balance_minor: balance_account_3, - threshold_intercept_major: 100_000_000, - permanent_debt_allowed_major: 35_000_000, + threshold_intercept_major: multiply_by_billion_concise(0.1), + permanent_debt_allowed_major: multiply_by_billion_concise(0.035), }; - let total_weight_account_1 = multiply_by_billion(400_000_000); - let total_weight_account_2 = multiply_by_billion(200_000_000); - let total_weight_account_3 = multiply_by_billion(300_000_000); + let total_weight_account_1 = multiply_by_quintillion_concise(0.4); + let total_weight_account_2 = multiply_by_quintillion_concise(0.2); + let total_weight_account_3 = multiply_by_quintillion_concise(0.3); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); @@ -1737,13 +1738,13 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .build(); let cw_service_fee_balance_minor = actual_disqualification_limits.account_1 + actual_disqualification_limits.account_3 - + multiply_by_billion(10_000_000); + + multiply_by_quintillion_concise(0.01); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1767,13 +1768,13 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); actual_disqualification_limits.validate_against_expected( - 71_000_000_000_000_000, - 183_000_000_000_000_000, - 157_000_000_000_000_000, + multiply_by_quintillion_concise(0.071), + multiply_by_quintillion_concise(0.183), + multiply_by_quintillion_concise(0.157), ); // Account 2, the least important one, was eliminated for a lack of transaction fee in the cw - let expected_adjusted_balance_1 = 81_000_000_000_000_000; - let expected_adjusted_balance_3 = 157_000_000_000_000_000; + let expected_adjusted_balance_1 = multiply_by_quintillion_concise(0.081); + let expected_adjusted_balance_3 = multiply_by_quintillion_concise(0.157); let expected_accounts = { let account_1_adjusted = account_with_new_balance(&analyzed_payables[0], expected_adjusted_balance_1); @@ -1826,7 +1827,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1854,9 +1855,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); actual_disqualification_limits.validate_against_expected( - 183_000_000_000_000_000, - 71_000_000_000_000_000, - 300_000_000_000_000_000, + multiply_by_billion(183_000_000), + multiply_by_billion(71_000_000), + multiply_by_billion(300_000_000), ); let expected_accounts = { let adjusted_account_2 = account_with_new_balance( @@ -1892,35 +1893,35 @@ mod tests { init_test_logging(); let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; let now = SystemTime::now(); - let balance_account_1 = multiply_by_billion(100_000_000_000); + let balance_account_1 = multiply_by_quintillion(100); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", balance_minor: balance_account_1, - threshold_intercept_major: 60_000_000_000, - permanent_debt_allowed_major: 10_000_000_000, + threshold_intercept_major: multiply_by_billion(60), + permanent_debt_allowed_major: multiply_by_billion(10), }; // The second is thrown away first in a response to the shortage of transaction fee, // as its weight is the least significant - let balance_account_2 = multiply_by_billion(500_000_000_000); + let balance_account_2 = multiply_by_quintillion(500); let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", balance_minor: balance_account_2, - threshold_intercept_major: 100_000_000_000, - permanent_debt_allowed_major: 30_000_000_000, + threshold_intercept_major: multiply_by_billion(100), + permanent_debt_allowed_major: multiply_by_billion(30), }; // Thrown away as the second one due to a shortage in the service fee, // listed among accounts to disqualify and picked eventually for its // lowest weight - let balance_account_3 = multiply_by_billion(250_000_000_000); + let balance_account_3 = multiply_by_quintillion(250); let sketched_account_3 = SketchedPayableAccount { wallet_addr_seed: "ghi", balance_minor: balance_account_3, - threshold_intercept_major: 90_000_000_000, - permanent_debt_allowed_major: 20_000_000_000, + threshold_intercept_major: multiply_by_billion(90), + permanent_debt_allowed_major: multiply_by_billion(20), }; - let total_weight_account_1 = multiply_by_billion(900_000_000_000); - let total_weight_account_2 = multiply_by_billion(500_000_000_000); - let total_weight_account_3 = multiply_by_billion(750_000_000_000); + let total_weight_account_1 = multiply_by_quintillion(900); + let total_weight_account_2 = multiply_by_quintillion(500); + let total_weight_account_3 = multiply_by_quintillion(750); let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = make_analyzed_accounts_and_show_their_actual_disqualification_limits(sketched_accounts); @@ -1928,12 +1929,12 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) .build(); - let service_fee_balance_in_minor = balance_account_1 - multiply_by_billion(10_000_000_000); + let service_fee_balance_in_minor = balance_account_1 - multiply_by_quintillion(10); let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() .set_arbitrary_id_stamp(agent_id_stamp) @@ -1953,9 +1954,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now).unwrap(); actual_disqualification_limits.validate_against_expected( - 50_000_000_000_000_000_000, - 460_000_000_000_000_000_000, - 200_000_000_000_000_000_000, + multiply_by_quintillion(50), + multiply_by_quintillion(460), + multiply_by_quintillion(200), ); let expected_accounts = vec![account_with_new_balance( &analyzed_payables[0], @@ -2083,27 +2084,27 @@ mod tests { let test_name = "late_error_after_tx_fee_adjusted_but_rechecked_service_fee_found_fatally_insufficient"; let now = SystemTime::now(); - let balance_account_1 = multiply_by_billion(500_000_000_000); + let balance_account_1 = multiply_by_quintillion(500); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", balance_minor: balance_account_1, - threshold_intercept_major: 300_000_000_000, - permanent_debt_allowed_major: 100_000_000_000, + threshold_intercept_major: multiply_by_billion(300), + permanent_debt_allowed_major: multiply_by_billion(100), }; // This account is eliminated in the transaction fee cut - let balance_account_2 = multiply_by_billion(111_000_000_000); + let balance_account_2 = multiply_by_quintillion(111); let sketched_account_2 = SketchedPayableAccount { wallet_addr_seed: "def", balance_minor: balance_account_2, - threshold_intercept_major: 50_000_000_000, - permanent_debt_allowed_major: 10_000_000_000, + threshold_intercept_major: multiply_by_billion(50), + permanent_debt_allowed_major: multiply_by_billion(10), }; - let balance_account_3 = multiply_by_billion(300_000_000_000); + let balance_account_3 = multiply_by_quintillion(300); let sketched_account_3 = SketchedPayableAccount { wallet_addr_seed: "ghi", balance_minor: balance_account_3, - threshold_intercept_major: 150_000_000_000, - permanent_debt_allowed_major: 50_000_000_000, + threshold_intercept_major: multiply_by_billion(150), + permanent_debt_allowed_major: multiply_by_billion(50), }; let sketched_accounts = [sketched_account_1, sketched_account_2, sketched_account_3]; let (analyzed_payables, actual_disqualification_limits) = @@ -2129,9 +2130,9 @@ mod tests { let result = subject.adjust_payments(adjustment_setup, now); actual_disqualification_limits.validate_against_expected( - multiply_by_billion(300_000_000_000), - multiply_by_billion(71_000_000_000), - multiply_by_billion(250_000_000_000), + multiply_by_quintillion(300), + multiply_by_quintillion(71), + multiply_by_quintillion(250), ); let err = match result { Ok(_) => panic!("expected an error but got Ok()"), @@ -2333,7 +2334,8 @@ mod tests { cw_service_fee_balance_minor, exceeding_balance, ); - let _ = payment_adjuster + + payment_adjuster .calculators .into_iter() .map(|calculator| calculator.calculate(&qualified_payable, &context)) @@ -2375,9 +2377,9 @@ mod tests { // This puts only the first calculator on test, the BalanceCalculator... { let mut account_1 = nominal_account_1; - account_1.bare_account.balance_wei += 123_456_789; + account_1.bare_account.balance_wei += 123456789; let mut account_2 = nominal_account_2; - account_2.bare_account.balance_wei += 999_999_999; + account_2.bare_account.balance_wei += 999999999; [ CalculatorTestScenario { payable: account_1, @@ -2458,12 +2460,12 @@ mod tests { let make_qualified_payable = |wallet| QualifiedPayableAccount { bare_account: PayableAccount { wallet, - balance_wei: multiply_by_billion(20_000_000), + balance_wei: multiply_by_quintillion_concise(0.02), last_paid_timestamp: now.checked_sub(Duration::from_secs(10_000)).unwrap(), pending_payable_opt: None, }, - payment_threshold_intercept_minor: multiply_by_billion(12_000_000), - creditor_thresholds: CreditorThresholds::new(multiply_by_billion(1_000_000)), + payment_threshold_intercept_minor: multiply_by_quintillion_concise(0.012), + creditor_thresholds: CreditorThresholds::new(multiply_by_quintillion_concise(0.001)), }; [ @@ -2477,7 +2479,7 @@ mod tests { now: SystemTime, cw_service_fee_balance_minor: u128, ) -> TemplateComputedWeight { - let template_results = exercise_production_code_to_get_weighted_accounts( + let template_results = exercise_production_code_to_get_weighed_accounts( template_accounts.to_vec(), now, cw_service_fee_balance_minor, @@ -2498,15 +2500,15 @@ mod tests { } } - fn exercise_production_code_to_get_weighted_accounts( + fn exercise_production_code_to_get_weighed_accounts( qualified_payables: Vec, now: SystemTime, cw_service_fee_balance_minor: u128, - ) -> Vec { + ) -> Vec { let analyzed_payables = convert_collection(qualified_payables); let max_debt_above_threshold_in_qualified_payables = find_largest_exceeding_balance(&analyzed_payables); - let mut subject = PaymentAdjusterTestBuilder::default() + let mut subject = PaymentAdjusterBuilder::default() .now(now) .cw_service_fee_balance_minor(cw_service_fee_balance_minor) .max_debt_above_threshold_in_qualified_payables( @@ -2527,18 +2529,18 @@ mod tests { let result = subject.run_adjustment(analyzed_payables); - less_important_constant_assertions_and_weighted_accounts_extraction( + less_important_constant_assertions_and_weighed_accounts_extraction( result, perform_adjustment_by_service_fee_params_arc, cw_service_fee_balance_minor, ) } - fn less_important_constant_assertions_and_weighted_accounts_extraction( + fn less_important_constant_assertions_and_weighed_accounts_extraction( actual_result: Result, PaymentAdjusterError>, - perform_adjustment_by_service_fee_params_arc: Arc, u128)>>>, + perform_adjustment_by_service_fee_params_arc: Arc, u128)>>>, cw_service_fee_balance_minor: u128, - ) -> Vec { + ) -> Vec { // This error should be ignored, as it has no real meaning. // It allows to halt the code executions without a dive in the recursion assert_eq!( @@ -2547,14 +2549,14 @@ mod tests { ); let mut perform_adjustment_by_service_fee_params = perform_adjustment_by_service_fee_params_arc.lock().unwrap(); - let (weighted_accounts, captured_cw_service_fee_balance_minor) = + let (weighed_accounts, captured_cw_service_fee_balance_minor) = perform_adjustment_by_service_fee_params.remove(0); assert_eq!( captured_cw_service_fee_balance_minor, cw_service_fee_balance_minor ); assert!(perform_adjustment_by_service_fee_params.is_empty()); - weighted_accounts + weighed_accounts } fn test_accounts_from_input_matrix( @@ -2590,14 +2592,14 @@ mod tests { .into_iter() .unzip(); - let actual_weighted_accounts = exercise_production_code_to_get_weighted_accounts( + let actual_weighed_accounts = exercise_production_code_to_get_weighed_accounts( qualified_payments, now, cw_service_fee_balance_minor, ); assert_results( - actual_weighted_accounts, + actual_weighed_accounts, expected_computed_weights, template_computed_weight, ) @@ -2605,25 +2607,25 @@ mod tests { } fn make_comparison_hashmap( - weighted_accounts: Vec, - ) -> HashMap { - let feeding_iterator = weighted_accounts + weighed_accounts: Vec, + ) -> HashMap { + let feeding_iterator = weighed_accounts .into_iter() .map(|account| (account.wallet().clone(), account)); HashMap::from_iter(feeding_iterator) } fn assert_results( - weighted_accounts: Vec, + weighed_accounts: Vec, expected_computed_weights: Vec, template_computed_weight: TemplateComputedWeight, ) { - let weighted_accounts_as_hash_map = make_comparison_hashmap(weighted_accounts); + let weighed_accounts_as_hash_map = make_comparison_hashmap(weighed_accounts); expected_computed_weights.into_iter().fold( 0, |previous_account_actual_weight, expected_account_weight| { let wallet = expected_account_weight.wallet; - let actual_account = weighted_accounts_as_hash_map + let actual_account = weighed_accounts_as_hash_map .get(&wallet) .unwrap_or_else(|| panic!("Account for wallet {:?} disappeared", wallet)); assert_ne!( diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs index fb433a756..4ac4fb25a 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/accounts_abstraction.rs @@ -1,13 +1,13 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeightedPayable; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeighedPayable; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; pub trait BalanceProvidingAccount { fn initial_balance_minor(&self) -> u128; } -impl BalanceProvidingAccount for WeightedPayable { +impl BalanceProvidingAccount for WeighedPayable { fn initial_balance_minor(&self) -> u128 { self.analyzed_account.initial_balance_minor() } @@ -29,7 +29,7 @@ pub trait DisqualificationLimitProvidingAccount { fn disqualification_limit(&self) -> u128; } -impl DisqualificationLimitProvidingAccount for WeightedPayable { +impl DisqualificationLimitProvidingAccount for WeighedPayable { fn disqualification_limit(&self) -> u128 { self.analyzed_account.disqualification_limit() } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 1dc672aef..b6c9375f9 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -8,7 +8,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions: log_transaction_fee_adjustment_ok_but_by_service_fee_undoable, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AffordableAndRequiredTxCounts, WeightedPayable, + AffordableAndRequiredTxCounts, WeighedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ @@ -137,18 +137,18 @@ impl PreparatoryAnalyzer { pub fn recheck_if_service_fee_adjustment_is_needed( &self, - weighted_accounts: &[WeightedPayable], + weighed_accounts: &[WeighedPayable], cw_service_fee_balance_minor: u128, error_factory: LateServiceFeeSingleTxErrorFactory, logger: &Logger, ) -> Result { if Self::is_service_fee_adjustment_needed( - weighted_accounts, + weighed_accounts, cw_service_fee_balance_minor, logger, ) { if let Err(e) = Self::check_adjustment_possibility( - weighted_accounts, + weighed_accounts, cw_service_fee_balance_minor, error_factory, ) { @@ -333,7 +333,7 @@ pub struct LateServiceFeeSingleTxErrorFactory { } impl LateServiceFeeSingleTxErrorFactory { - pub fn new(unadjusted_accounts: &[WeightedPayable]) -> Self { + pub fn new(unadjusted_accounts: &[WeighedPayable]) -> Self { let original_number_of_accounts = unadjusted_accounts.len(); let original_service_fee_required_total_minor = sum_as(unadjusted_accounts, |account| { account.initial_balance_minor() @@ -373,7 +373,8 @@ mod tests { PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, }; use crate::accountant::payment_adjuster::test_utils::{ - make_weighed_account, multiply_by_billion, DisqualificationGaugeMock, + make_weighed_account, multiply_by_billion, multiply_by_billion_concise, + multiply_by_quintillion, multiply_by_quintillion_concise, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, @@ -457,13 +458,13 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { let mut account_1 = make_meaningless_qualified_payable(111); - account_1.bare_account.balance_wei = 1_000_000_000; + account_1.bare_account.balance_wei = multiply_by_billion_concise(1.0); let mut account_2 = make_meaningless_qualified_payable(333); - account_2.bare_account.balance_wei = 2_000_000_000; - let cw_service_fee_balance = 750_000_001; + account_2.bare_account.balance_wei = multiply_by_billion_concise(2.0); + let cw_service_fee_balance = multiply_by_billion_concise(0.75) + 1; let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(750_000_000) - .determine_limit_result(1_500_000_000); + .determine_limit_result(multiply_by_billion_concise(0.75)) + .determine_limit_result(multiply_by_billion_concise(1.5)); let original_accounts = [account_1, account_2]; test_adjustment_possibility_nearly_rejected( @@ -477,13 +478,13 @@ mod tests { #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_equal() { let mut account_1 = make_meaningless_qualified_payable(111); - account_1.bare_account.balance_wei = 2_000_000_000; + account_1.bare_account.balance_wei = multiply_by_billion_concise(2.0); let mut account_2 = make_meaningless_qualified_payable(333); - account_2.bare_account.balance_wei = 1_000_000_000; - let cw_service_fee_balance = 750_000_000; + account_2.bare_account.balance_wei = multiply_by_billion_concise(1.0); + let cw_service_fee_balance = multiply_by_billion_concise(0.75); let disqualification_gauge = DisqualificationGaugeMock::default() - .determine_limit_result(1_500_000_000) - .determine_limit_result(750_000_000); + .determine_limit_result(multiply_by_billion_concise(1.5)) + .determine_limit_result(multiply_by_billion_concise(0.75)); let original_accounts = [account_1, account_2]; test_adjustment_possibility_nearly_rejected( @@ -577,24 +578,24 @@ mod tests { } #[test] - fn accounts_analyzing_works_even_for_weighted_payable() { + fn accounts_analyzing_works_even_for_weighed_payable() { init_test_logging(); - let test_name = "accounts_analyzing_works_even_for_weighted_payable"; + let test_name = "accounts_analyzing_works_even_for_weighed_payable"; let balance_1 = multiply_by_billion(2_000_000); - let mut weighted_account_1 = make_weighed_account(123); - weighted_account_1 + let mut weighed_account_1 = make_weighed_account(123); + weighed_account_1 .analyzed_account .qualified_as .bare_account .balance_wei = balance_1; let balance_2 = multiply_by_billion(3_456_000); - let mut weighted_account_2 = make_weighed_account(456); - weighted_account_2 + let mut weighed_account_2 = make_weighed_account(456); + weighed_account_2 .analyzed_account .qualified_as .bare_account .balance_wei = balance_2; - let accounts = vec![weighted_account_1, weighted_account_2]; + let accounts = vec![weighed_account_1, weighed_account_2]; let service_fee_totally_required_minor = balance_1 + balance_2; let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1; let error_factory = LateServiceFeeSingleTxErrorFactory::new(&accounts); @@ -639,9 +640,9 @@ mod tests { .qualified_as .bare_account .balance_wei = balance_2; - let weighted_accounts = vec![account_1, account_2]; + let weighed_accounts = vec![account_1, account_2]; - let result = LateServiceFeeSingleTxErrorFactory::new(&weighted_accounts); + let result = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); assert_eq!( result, diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 66879305c..3e5f45d72 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -3,7 +3,7 @@ use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, AdjustmentIterationResult, UnconfirmedAdjustment, - WeightedPayable, + WeighedPayable, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics:: ordinary_diagnostic_functions::{proposed_adjusted_balance_diagnostics}; @@ -17,7 +17,7 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::o pub trait ServiceFeeAdjuster { fn perform_adjustment_by_service_fee( &self, - weighted_accounts: Vec, + weighed_accounts: Vec, disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, logger: &Logger, @@ -30,23 +30,23 @@ pub struct ServiceFeeAdjusterReal {} impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { fn perform_adjustment_by_service_fee( &self, - weighted_accounts: Vec, + weighed_accounts: Vec, disqualification_arbiter: &DisqualificationArbiter, cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult { let unconfirmed_adjustments = - compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + compute_unconfirmed_adjustments(weighed_accounts, cw_service_fee_balance_minor); let checked_accounts = Self::try_confirm_some_accounts(unconfirmed_adjustments); match checked_accounts { - Either::Left(no_thriving_competitors) => Self::disqualify_single_account( + Either::Left(no_accounts_above_disq_limit) => Self::disqualify_single_account( disqualification_arbiter, - no_thriving_competitors, + no_accounts_above_disq_limit, logger, ), - Either::Right(thriving_competitors) => thriving_competitors, + Either::Right(some_accounts_above_disq_limit) => some_accounts_above_disq_limit, } } } @@ -80,7 +80,7 @@ impl ServiceFeeAdjusterReal { if accounts_above_disq_limit.is_empty() { Either::Left(accounts_below_disq_limit) } else { - let remaining_undecided_accounts: Vec = + let remaining_undecided_accounts: Vec = convert_collection(accounts_below_disq_limit); let pre_processed_decided_accounts: Vec = convert_collection(accounts_above_disq_limit); @@ -147,35 +147,33 @@ impl ServiceFeeAdjusterReal { } fn compute_unconfirmed_adjustments( - weighted_accounts: Vec, + weighed_accounts: Vec, unallocated_cw_service_fee_balance_minor: u128, ) -> Vec { - let weights_total = sum_as(&weighted_accounts, |weighted_account| { - weighted_account.weight - }); - let cw_service_fee_balance = unallocated_cw_service_fee_balance_minor; + let weights_total = sum_as(&weighed_accounts, |weighed_account| weighed_account.weight); - let multiplication_coefficient = - compute_mul_coefficient_preventing_fractional_numbers(cw_service_fee_balance); + let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( + unallocated_cw_service_fee_balance_minor, + ); - let proportional_cw_balance_fragment = compute_proportional_cw_fragment( - cw_service_fee_balance, + let proportional_cw_fragment = compute_proportional_cw_fragment( + unallocated_cw_service_fee_balance_minor, weights_total, multiplication_coefficient, ); let compute_proposed_adjusted_balance = - |weight: u128| weight * proportional_cw_balance_fragment / multiplication_coefficient; + |weight| weight * proportional_cw_fragment / multiplication_coefficient; - weighted_accounts + weighed_accounts .into_iter() - .map(|weighted_account| { + .map(|weighed_account| { let proposed_adjusted_balance = - compute_proposed_adjusted_balance(weighted_account.weight); + compute_proposed_adjusted_balance(weighed_account.weight); - proposed_adjusted_balance_diagnostics(&weighted_account, proposed_adjusted_balance); + proposed_adjusted_balance_diagnostics(&weighed_account, proposed_adjusted_balance); - UnconfirmedAdjustment::new(weighted_account, proposed_adjusted_balance) + UnconfirmedAdjustment::new(weighed_account, proposed_adjusted_balance) }) .collect() } @@ -186,7 +184,7 @@ fn compute_proportional_cw_fragment( multiplication_coefficient: u128, ) -> u128 { cw_service_fee_balance_minor - // Considered safe due to the process of getting this coefficient + // Considered safe for the nature of the calculus producing this coefficient .checked_mul(multiplication_coefficient) .unwrap_or_else(|| { panic!( @@ -203,74 +201,75 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, multiply_by_billion, + make_non_guaranteed_unconfirmed_adjustment, multiply_by_billion, multiply_by_quintillion, + multiply_by_quintillion_concise, }; #[test] - fn filter_and_process_winners_limits_them_by_their_disqualification_edges() { + fn filter_and_process_confirmable_accounts_limits_them_by_their_disqualification_edges() { let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); - let weight_1 = account_1.weighted_account.weight; + let weight_1 = account_1.weighed_account.weight; account_1 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiply_by_billion(2_000_000_000); + .balance_wei = multiply_by_quintillion(2); account_1 - .weighted_account + .weighed_account .analyzed_account - .disqualification_limit_minor = multiply_by_billion(1_800_000_000); - account_1.proposed_adjusted_balance_minor = multiply_by_billion(3_000_000_000); + .disqualification_limit_minor = multiply_by_quintillion_concise(1.8); + account_1.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(3.0); let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); - let weight_2 = account_2.weighted_account.weight; + let weight_2 = account_2.weighed_account.weight; account_2 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiply_by_billion(5_000_000_000); + .balance_wei = multiply_by_quintillion(5); account_2 - .weighted_account + .weighed_account .analyzed_account - .disqualification_limit_minor = multiply_by_billion(4_200_000_000) - 1; - account_2.proposed_adjusted_balance_minor = multiply_by_billion(4_200_000_000); + .disqualification_limit_minor = multiply_by_quintillion_concise(4.2) - 1; + account_2.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(4.2); let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); account_3 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiply_by_billion(3_000_000_000); + .balance_wei = multiply_by_quintillion(3); account_3 - .weighted_account + .weighed_account .analyzed_account - .disqualification_limit_minor = multiply_by_billion(2_000_000_000) + 1; - account_3.proposed_adjusted_balance_minor = multiply_by_billion(2_000_000_000); + .disqualification_limit_minor = multiply_by_quintillion(2) + 1; + account_3.proposed_adjusted_balance_minor = multiply_by_quintillion(2); let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); - let weight_4 = account_4.weighted_account.weight; + let weight_4 = account_4.weighed_account.weight; account_4 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiply_by_billion(1_500_000_000); + .balance_wei = multiply_by_quintillion_concise(1.5); account_4 - .weighted_account + .weighed_account .analyzed_account - .disqualification_limit_minor = multiply_by_billion(500_000_000); - account_4.proposed_adjusted_balance_minor = multiply_by_billion(500_000_000); + .disqualification_limit_minor = multiply_by_quintillion_concise(0.5); + account_4.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(0.5); let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); account_5 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account - .balance_wei = multiply_by_billion(2_000_000_000); + .balance_wei = multiply_by_quintillion(2); account_5 - .weighted_account + .weighed_account .analyzed_account - .disqualification_limit_minor = multiply_by_billion(1_000_000_000) + 1; - account_5.proposed_adjusted_balance_minor = multiply_by_billion(1_000_000_000); + .disqualification_limit_minor = multiply_by_quintillion(1) + 1; + account_5.proposed_adjusted_balance_minor = multiply_by_quintillion(1); let unconfirmed_accounts = vec![ account_1.clone(), account_2.clone(), @@ -286,30 +285,30 @@ mod tests { let expected_adjusted_outweighed_accounts = vec![ AdjustedAccountBeforeFinalization::new( account_1 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account, weight_1, - multiply_by_billion(1_800_000_000), + multiply_by_quintillion_concise(1.8), ), AdjustedAccountBeforeFinalization::new( account_2 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account, weight_2, - multiply_by_billion(4_200_000_000) - 1, + multiply_by_quintillion_concise(4.2) - 1, ), AdjustedAccountBeforeFinalization::new( account_4 - .weighted_account + .weighed_account .analyzed_account .qualified_as .bare_account, weight_4, - multiply_by_billion(500_000_000), + multiply_by_quintillion_concise(0.5), ), ]; assert_eq!(thriving_competitors, expected_adjusted_outweighed_accounts) @@ -318,19 +317,19 @@ mod tests { #[cfg(test)] pub mod test_helpers { - use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeightedPayable; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeighedPayable; use crate::accountant::payment_adjuster::service_fee_adjuster::compute_unconfirmed_adjustments; use crate::sub_lib::wallet::Wallet; use thousands::Separable; pub fn illustrate_why_we_need_to_prevent_exceeding_the_original_value( cw_service_fee_balance_minor: u128, - weighted_accounts: Vec, + weighed_accounts: Vec, wallet_of_expected_outweighed: Wallet, original_balance_of_outweighed_account: u128, ) { let unconfirmed_adjustments = - compute_unconfirmed_adjustments(weighted_accounts, cw_service_fee_balance_minor); + compute_unconfirmed_adjustments(weighed_accounts, cw_service_fee_balance_minor); // The results are sorted from the biggest weights down assert_eq!( unconfirmed_adjustments[1].wallet(), diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index bce1b0085..6e37529d2 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::disqualification_arbiter::{ }; use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, UnconfirmedAdjustment, WeightedPayable, + AdjustmentIterationResult, UnconfirmedAdjustment, WeighedPayable, }; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; use crate::accountant::payment_adjuster::PaymentAdjusterReal; @@ -29,12 +29,12 @@ use std::time::{Duration, SystemTime}; lazy_static! { pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = - multiply_by_billion(multiply_by_billion(MASQ_TOTAL_SUPPLY as u128)); + multiply_by_quintillion(MASQ_TOTAL_SUPPLY as u128); pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; } #[derive(Default)] -pub struct PaymentAdjusterTestBuilder { +pub struct PaymentAdjusterBuilder { start_with_inner_null: bool, now_opt: Option, cw_service_fee_balance_minor_opt: Option, @@ -44,7 +44,7 @@ pub struct PaymentAdjusterTestBuilder { logger_opt: Option, } -impl PaymentAdjusterTestBuilder { +impl PaymentAdjusterBuilder { pub fn build(self) -> PaymentAdjusterReal { let mut payment_adjuster = PaymentAdjusterReal::default(); let logger = self.logger_opt.unwrap_or(Logger::new("test")); @@ -148,14 +148,14 @@ pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHO pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_account = make_meaningless_qualified_payable(n); - let proposed_adjusted_balance_minor = - (qualified_account.bare_account.balance_wei / 2) * (n as f64).sqrt() as u128; + let account_balance = qualified_account.bare_account.balance_wei; + let proposed_adjusted_balance_minor = (2 * account_balance) / 3; let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; let analyzed_account = AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); - let weight = (n as u128).pow(3); + let weight = multiply_by_billion(n as u128); UnconfirmedAdjustment::new( - WeightedPayable::new(analyzed_account, weight), + WeighedPayable::new(analyzed_account, weight), proposed_adjusted_balance_minor, ) } @@ -228,13 +228,13 @@ impl DisqualificationGaugeMock { #[derive(Default)] pub struct ServiceFeeAdjusterMock { - perform_adjustment_by_service_fee_params: Arc, u128)>>>, + perform_adjustment_by_service_fee_params: Arc, u128)>>>, perform_adjustment_by_service_fee_results: RefCell>, } impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { fn perform_adjustment_by_service_fee( &self, - weighted_accounts: Vec, + weighed_accounts: Vec, _disqualification_arbiter: &DisqualificationArbiter, unallocated_cw_service_fee_balance_minor: u128, _logger: &Logger, @@ -242,7 +242,7 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { self.perform_adjustment_by_service_fee_params .lock() .unwrap() - .push((weighted_accounts, unallocated_cw_service_fee_balance_minor)); + .push((weighed_accounts, unallocated_cw_service_fee_balance_minor)); self.perform_adjustment_by_service_fee_results .borrow_mut() .remove(0) @@ -252,7 +252,7 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { impl ServiceFeeAdjusterMock { pub fn perform_adjustment_by_service_fee_params( mut self, - params: &Arc, u128)>>>, + params: &Arc, u128)>>>, ) -> Self { self.perform_adjustment_by_service_fee_params = params.clone(); self @@ -269,10 +269,39 @@ impl ServiceFeeAdjusterMock { } } +// = 1 gwei pub fn multiply_by_billion(num: u128) -> u128 { gwei_to_wei(num) } +// = 1 MASQ +pub fn multiply_by_quintillion(num: u128) -> u128 { + multiply_by_billion(multiply_by_billion(num)) +} + +// = 1 gwei +pub fn multiply_by_billion_concise(num: f64) -> u128 { + multiple_by(num, 9, "billion") +} + +// = 1 MASQ +pub fn multiply_by_quintillion_concise(num: f64) -> u128 { + multiple_by(num, 18, "quintillion") +} + +fn multiple_by( + num_in_concise_form: f64, + desired_increase_in_magnitude: usize, + mathematical_name: &str, +) -> u128 { + if (num_in_concise_form * 1000.0).fract() != 0.0 { + panic!("Multiplying by {mathematical_name}: It's allowed only when applied on numbers with three \ + digits after the decimal point at maximum!") + } + let significant_digits = (num_in_concise_form * 1000.0) as u128; + significant_digits * 10_u128.pow(desired_increase_in_magnitude as u32 - 3) +} + pub fn make_meaningless_analyzed_account_by_wallet( wallet_address_segment: &str, ) -> AnalyzedPayableAccount { @@ -283,8 +312,8 @@ pub fn make_meaningless_analyzed_account_by_wallet( account } -pub fn make_weighed_account(n: u64) -> WeightedPayable { - WeightedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) +pub fn make_weighed_account(n: u64) -> WeighedPayable { + WeighedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) } // Should stay test only!! diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs index 7d8c85cfc..eea1265c5 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_null.rs @@ -28,8 +28,8 @@ impl BlockchainAgent for BlockchainAgentNull { 0 } - fn agreed_fee_per_computation_unit(&self) -> u64 { - self.log_function_call("agreed_fee_per_computation_unit()"); + fn gas_price(&self) -> u64 { + self.log_function_call("gas_price()"); 0 } @@ -163,16 +163,16 @@ mod tests { } #[test] - fn null_agent_agreed_fee_per_computation_unit() { + fn null_agent_gas_price() { init_test_logging(); - let test_name = "null_agent_agreed_fee_per_computation_unit"; + let test_name = "null_agent_gas_price"; let mut subject = BlockchainAgentNull::new(); subject.logger = Logger::new(test_name); - let result = subject.agreed_fee_per_computation_unit(); + let result = subject.gas_price(); assert_eq!(result, 0); - assert_error_log(test_name, "agreed_fee_per_computation_unit") + assert_error_log(test_name, "gas_price") } #[test] diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 959fc29bb..045245124 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -38,7 +38,7 @@ impl BlockchainAgent for BlockchainAgentWeb3 { .service_fee_balance_in_minor_units } - fn agreed_fee_per_computation_unit(&self) -> u64 { + fn gas_price(&self) -> u64 { self.gas_price_gwei } @@ -118,7 +118,7 @@ mod tests { pending_transaction_id, ); - assert_eq!(subject.agreed_fee_per_computation_unit(), gas_price_gwei); + assert_eq!(subject.gas_price(), gas_price_gwei); assert_eq!(subject.consuming_wallet(), &consuming_wallet); assert_eq!( subject.transaction_fee_balance_minor(), diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs index 19b449886..6cea72ebb 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/blockchain_agent.rs @@ -25,7 +25,7 @@ pub trait BlockchainAgent: Send { fn estimated_transaction_fee_per_transaction_minor(&self) -> u128; fn transaction_fee_balance_minor(&self) -> U256; fn service_fee_balance_minor(&self) -> u128; - fn agreed_fee_per_computation_unit(&self) -> u64; + fn gas_price(&self) -> u64; fn gas_price_margin(&self) -> PurePercentage; fn consuming_wallet(&self) -> &Wallet; fn pending_transaction_id(&self) -> U256; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index 49d125edc..ff1f6d3d7 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -15,7 +15,7 @@ pub struct BlockchainAgentMock { estimated_transaction_fee_per_transaction_minor_results: RefCell>, transaction_fee_balance_minor_results: RefCell>, service_fee_balance_minor_results: RefCell>, - agreed_fee_per_computation_unit_results: RefCell>, + gas_price_results: RefCell>, gas_price_margin: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, @@ -41,10 +41,8 @@ impl BlockchainAgent for BlockchainAgentMock { .remove(0) } - fn agreed_fee_per_computation_unit(&self) -> u64 { - self.agreed_fee_per_computation_unit_results - .borrow_mut() - .remove(0) + fn gas_price(&self) -> u64 { + self.gas_price_results.borrow_mut().remove(0) } fn gas_price_margin(&self) -> PurePercentage { @@ -88,10 +86,8 @@ impl BlockchainAgentMock { self } - pub fn agreed_fee_per_computation_unit_result(self, result: u64) -> Self { - self.agreed_fee_per_computation_unit_results - .borrow_mut() - .push(result); + pub fn gas_price_result(self, result: u64) -> Self { + self.gas_price_results.borrow_mut().push(result); self } 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 5bc4d315d..18579e530 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -278,7 +278,7 @@ where accounts: &[PayableAccount], ) -> Result, PayableTransactionError> { let consuming_wallet = agent.consuming_wallet(); - let gas_price = agent.agreed_fee_per_computation_unit(); + let gas_price = agent.gas_price(); let pending_nonce = agent.pending_transaction_id(); debug!( @@ -1065,7 +1065,7 @@ mod tests { result.gas_price_margin(), PurePercentage::try_from(15).unwrap() ); - assert_eq!(result.agreed_fee_per_computation_unit(), 50); + assert_eq!(result.gas_price(), 50); assert_eq!( result.estimated_transaction_fee_per_transaction_minor(), 3666400000000000 @@ -2173,7 +2173,7 @@ mod tests { Box::new( BlockchainAgentMock::default() .consuming_wallet_result(consuming_wallet) - .agreed_fee_per_computation_unit_result(gas_price_gwei) + .gas_price_result(gas_price_gwei) .pending_transaction_id_result(nonce), ) } From d30ccf0cf423efb602caa1dee487628b859c44e2 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 12 Dec 2024 00:51:24 +0100 Subject: [PATCH 226/250] GH-711-review-one: more little improvements; the fuzz test --- .../payment_adjuster/non_unit_tests/mod.rs | 351 ++++++++++-------- .../payable_scanner/agent_web3.rs | 6 +- 2 files changed, 193 insertions(+), 164 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index c4435f7f7..b002c3368 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -20,6 +20,7 @@ use crate::accountant::test_utils::{ make_single_qualified_payable_opt, try_making_guaranteed_qualified_payables, }; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -41,26 +42,40 @@ use web3::types::U256; #[test] // TODO If an option for "occasional tests" is added, this is a good adept -#[ignore] +//#[ignore] fn loading_test_with_randomized_params() { - // This is a fuzz test, a generator of possibly an overwhelming amount of scenarios that could - // get the PaymentAdjuster to be asked to sort them out even in real situations while there - // might be many and many combinations that a human is having a hard time just imagining; of - // them some might be corner cases whose threatening wasn't known when this was being designed. - // This test is to prove that even a huge number of runs, with hopefully highly variable inputs, - // will not shoot the PaymentAdjuster down and the Node with it; on the contrary, it should - // be able to give reasonable results and live up to its original purpose of adjustments. - - // Part of the requested count is rejected before the test begins as there are generated - // scenarios with such parameters that don't fit to a variety of conditions. It's easier to keep - // it this way than setting up an algorithm with enough "tamed" randomness. Other bunch of them - // will likely be marked as legitimate errors that the PaymentAdjuster can detect. - // When the test reaches its end, a text file is filled in with some key figures of the performed - // exercises and finally also an overall summary with useful statistics that can serve to - // evaluate the actual behavior against the desired. + // This is a fuzz test. It generates possibly an overwhelming amount of scenarios that + // the PaymentAdjuster could be given sort them out, as realistic as it can get, while its + // nature of randomness offers chances to have a dense range of combinations that a human fails + // to even try imagining. The hypothesis is that some of those might be corner cases whose + // trickiness wasn't recognized when the functionality was still at design. This test is to + // prove that despite highly variable input over a lot of attempts, the PaymentAdjuster can do + // its job reliably and won't endanger the Node. Also, it is important that it should give + // reasonable payment adjustments. + + // We can consider the test having an exo-parameter. It's the count of scenarios to be generated. + // This number must be thought of just as a rough parameter, because many of those attempted + // scenarios, loosely randomized, will be rejected in the setup stage. + + // The rejection happens before the actual test unwinds as there will always be scenarios with + // attributes that don't fit to a variety of conditions which needs to be insisted on. Those are + // that the accounts under each scenario can hold that they are legitimately qualified payables + // as those to be passed on to the payment adjuster in the real world. It goes much easier if + // we allow this always implied waste than trying to invent an algorithm whose randomness would + // be exercised within strictly controlled boundaries. + + // Some other are lost quite early as legitimate errors that the PaymentAdjuster can detect, + // which would prevent finishing the search for given scenario. + + // When the test reaches its end, it produces important output in a text file, located: + // node/generated/test/payment_adjuster/tests/home/loading_test_output.txt + + // This file begins with some key figures of those exercises just run, which is followed by + // a summary loaded with statistics that can serve well on inspection of the actual behavior + // against the desired. // If you are new to this algorithm, there might be results (maybe rare, but absolutely valid - // and wanted, and so deserving some interest) that can have one puzzled, though. + // and wanted) that can keep one puzzled. // The example further below presents a tricky-to-understand output belonging to one set of // payables. See those percentages. They may not excel at explaining themselves when it comes to @@ -231,57 +246,10 @@ fn make_payable_account( now: SystemTime, gn: &mut ThreadRng, ) -> PayableAccount { - // Why is this construction so complicated? Well, I wanted to get the test showing partially - // fulfilling adjustments where the final accounts can be paid enough but still not all up to - // their formerly claimed balance. It turned out it is very difficult to achieve with the use of - // randomized ranges, I couldn't really come up with parameters that would promise this condition. - // I ended up experimenting and looking for an algorithm that would make the parameters as random - // as possible because the generator alone is not much good at it, using gradually, but - // individually generated parameters that I put together for better chances of randomness. Many - // produced accounts will not make it through into the actual test, filtered out when attempted - // to be converted into a proper QualifiedPayableAccount. This isn't optimal, sure, but it allows - // to observe some of those partial adjustments, however, with rather a low rate of occurrence - // among those all attempts of acceptable scenarios. let wallet = make_wallet(&format!("wallet{}", idx)); - let mut generate_age_segment = || { - generate_non_zero_usize( - gn, - (thresholds.maturity_threshold_sec + thresholds.threshold_interval_sec) as usize, - ) / 2 - }; - let debt_age = generate_age_segment() + generate_age_segment(); - let service_fee_balance_minor = { - let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; - let parameter_a = generate_u128(); - let parameter_b = generate_u128(); - let parameter_c = generate_u128(); - let parameter_d = generate_u128(); - let parameter_e = generate_u128(); - let parameter_f = generate_u128(); - let mut use_variable_exponent = |parameter: u128, up_to: usize| { - parameter.pow(generate_non_zero_usize(gn, up_to) as u32) - }; - let a_b_c_d_e = parameter_a - * use_variable_exponent(parameter_b, 2) - * use_variable_exponent(parameter_c, 3) - * use_variable_exponent(parameter_d, 4) - * use_variable_exponent(parameter_e, 5); - let addition = (0..6).fold(a_b_c_d_e, |so_far, subtrahend| { - if so_far != a_b_c_d_e { - so_far - } else { - if let Some(num) = - a_b_c_d_e.checked_sub(use_variable_exponent(parameter_f, 6 - subtrahend)) - { - num - } else { - so_far - } - } - }); - - thresholds.permanent_debt_allowed_gwei as u128 + addition - }; + let debt_age = generate_debt_age(gn, thresholds); + let service_fee_balance_minor = + generate_highly_randomized_payable_account_balance(gn, thresholds); let last_paid_timestamp = from_time_t(to_time_t(now) - debt_age as i64); PayableAccount { wallet, @@ -291,6 +259,128 @@ fn make_payable_account( } } +fn generate_debt_age(gn: &mut ThreadRng, thresholds: &PaymentThresholds) -> u64 { + generate_range( + gn, + thresholds.maturity_threshold_sec, + thresholds.maturity_threshold_sec + thresholds.threshold_interval_sec, + ) / 2 +} + +fn generate_highly_randomized_payable_account_balance( + gn: &mut ThreadRng, + thresholds: &PaymentThresholds, +) -> u128 { + // This seems overcomplicated, damn. As a result of simple intentions though. I wanted to ensure + // occurrence of accounts with balances having different magnitudes in the frame of a single + // scenario. This was crucial to me so much that I was ready to write even this piece of code + // a bit crazy by look. + // This setup worked well to stress the randomness I needed, a lot more significant compared to + // what the naked number generator can put for you. Using some nesting, it broke the rigid + // pattern and gave an existence to accounts with diverse balances. + let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; + + let parameter_a = generate_u128(); + let parameter_b = generate_u128(); + let parameter_c = generate_u128(); + let parameter_d = generate_u128(); + let parameter_e = generate_u128(); + let parameter_f = generate_u128(); + + let mut use_variable_exponent = + |parameter: u128, up_to: usize| parameter.pow(generate_non_zero_usize(gn, up_to) as u32); + + let a_b_c_d_e = parameter_a + * use_variable_exponent(parameter_b, 2) + * use_variable_exponent(parameter_c, 3) + * use_variable_exponent(parameter_d, 4) + * use_variable_exponent(parameter_e, 5); + let addition = (0..6).fold(a_b_c_d_e, |so_far, subtrahend| { + if so_far != a_b_c_d_e { + so_far + } else { + if let Some(num) = + a_b_c_d_e.checked_sub(use_variable_exponent(parameter_f, 6 - subtrahend)) + { + num + } else { + so_far + } + } + }); + + thresholds.permanent_debt_allowed_gwei as u128 + addition +} + +fn try_make_qualified_payables_by_applied_thresholds( + payable_accounts: Vec, + applied_thresholds: &AppliedThresholds, + now: SystemTime, +) -> ( + Vec, + Vec<(Wallet, PaymentThresholds)>, +) { + let payment_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); + match applied_thresholds { + AppliedThresholds::Defaulted => ( + try_making_guaranteed_qualified_payables( + payable_accounts, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + false, + ), + vec![], + ), + AppliedThresholds::SingleButRandomized { common_thresholds } => ( + try_making_guaranteed_qualified_payables( + payable_accounts, + common_thresholds, + now, + false, + ), + vec![], + ), + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, + } => { + let vec_of_thresholds = individual_thresholds + .thresholds + .as_ref() + .left() + .expect("should be Vec at this stage"); + assert_eq!( + payable_accounts.len(), + vec_of_thresholds.len(), + "The number of generated \ + payables {} differs from their sets of thresholds {}, but one should've been derived \ + from the other", + payable_accounts.len(), + vec_of_thresholds.len() + ); + let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); + zipped.fold( + (vec![], vec![]), + |(mut qualified_payables, mut wallet_thresholds_pairs), + (payable, its_thresholds)| match make_single_qualified_payable_opt( + payable, + &payment_inspector, + &its_thresholds, + false, + now, + ) { + Some(qualified_payable) => { + let wallet = qualified_payable.bare_account.wallet.clone(); + qualified_payables.push(qualified_payable); + wallet_thresholds_pairs.push((wallet, *its_thresholds)); + (qualified_payables, wallet_thresholds_pairs) + } + None => (qualified_payables, wallet_thresholds_pairs), + }, + ) + } + } +} + fn try_generating_qualified_payables_and_cw_balance( gn: &mut ThreadRng, thresholds_to_be_used: &AppliedThresholds, @@ -323,6 +413,7 @@ fn try_generating_qualified_payables_and_cw_balance( let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; balance_average / multiplier as u128 * number_of_pieces }; + // let required_service_fee_total: u128 = sum_as(&qualified_payables, |account| { account.initial_balance_minor() }); @@ -337,6 +428,21 @@ fn try_generating_qualified_payables_and_cw_balance( } } +fn pick_appropriate_cw_service_fee_balance( + gn: &mut ThreadRng, + qualified_payables: &[QualifiedPayableAccount], + accounts_count: usize, +) -> u128 { + // Value picked empirically + const COEFFICIENT: usize = 1000; + let balance_average = sum_as(qualified_payables, |account| { + account.initial_balance_minor() + }) / accounts_count as u128; + let max_pieces = accounts_count * COEFFICIENT; + let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; + balance_average / COEFFICIENT as u128 * number_of_pieces +} + fn make_payables_according_to_thresholds_setup( gn: &mut ThreadRng, thresholds_to_be_used: &AppliedThresholds, @@ -375,7 +481,7 @@ fn make_payables_with_common_thresholds( ) -> Vec { (0..accounts_count) .map(|idx| make_payable_account(idx, common_thresholds, now, gn)) - .collect::>() + .collect() } fn make_payables_with_individual_thresholds( @@ -418,16 +524,13 @@ fn return_single_randomized_thresholds(gn: &mut ThreadRng) -> PaymentThresholds let permanent_debt_allowed_gwei = generate_range(gn, 100, 1_000_000_000); let debt_threshold_gwei = permanent_debt_allowed_gwei + generate_range(gn, 10_000, 10_000_000_000); - let maturity_threshold_sec = generate_range(gn, 100, 10_000); - let threshold_interval_sec = generate_range(gn, 1000, 100_000); - let unban_below_gwei = permanent_debt_allowed_gwei; PaymentThresholds { debt_threshold_gwei, - maturity_threshold_sec, + maturity_threshold_sec: generate_range(gn, 100, 10_000), payment_grace_period_sec: 0, permanent_debt_allowed_gwei, - threshold_interval_sec, - unban_below_gwei, + threshold_interval_sec: generate_range(gn, 1000, 100_000), + unban_below_gwei: permanent_debt_allowed_gwei, } } @@ -443,7 +546,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) - .gas_price_margin_result(PurePercentage::try_from(15).unwrap()) + .gas_price_margin_result(TRANSACTION_FEE_MARGIN.clone()) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { @@ -554,30 +657,29 @@ fn preserve_account_infos( fn render_results_to_file_and_attempt_basic_assertions( scenario_results: Vec, number_of_requested_scenarios: usize, - overall_output_collector: TestOverallOutputCollector, + output_collector: TestOverallOutputCollector, ) { let file_dir = ensure_node_home_directory_exists("payment_adjuster", "tests"); let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); introduction(&mut file); - let test_overall_output_collector = + let output_collector = scenario_results .into_iter() - .fold(overall_output_collector, |acc, scenario_result| { + .fold(output_collector, |acc, scenario_result| { do_final_processing_of_single_scenario(&mut file, acc, scenario_result) }); - let total_scenarios_evaluated = test_overall_output_collector - .scenarios_denied_before_adjustment_started - + test_overall_output_collector.oks - + test_overall_output_collector.all_accounts_eliminated - + test_overall_output_collector.late_immoderately_insufficient_service_fee_balance; + let total_scenarios_evaluated = output_collector.scenarios_denied_before_adjustment_started + + output_collector.oks + + output_collector.all_accounts_eliminated + + output_collector.late_immoderately_insufficient_service_fee_balance; write_brief_test_summary_into_file( &mut file, - &test_overall_output_collector, + &output_collector, number_of_requested_scenarios, total_scenarios_evaluated, ); let total_scenarios_handled_including_invalid_ones = - total_scenarios_evaluated + test_overall_output_collector.invalidly_generated_scenarios; + total_scenarios_evaluated + output_collector.invalidly_generated_scenarios; assert_eq!( total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios, "All handled scenarios including those invalid ones ({}) != requested scenarios count ({})", @@ -589,7 +691,7 @@ fn render_results_to_file_and_attempt_basic_assertions( // to the use case and the expected conditions. Therefore, we insist on making "guaranteed" // QualifiedPayableAccounts out of PayableAccount which is where we take the losses. let entry_check_pass_rate = 100 - - ((test_overall_output_collector.scenarios_denied_before_adjustment_started * 100) + - ((output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); let required_pass_rate = 50; assert!( @@ -601,9 +703,8 @@ fn render_results_to_file_and_attempt_basic_assertions( total_scenarios_evaluated, entry_check_pass_rate ); - let ok_adjustment_percentage = (test_overall_output_collector.oks * 100) - / (total_scenarios_evaluated - - test_overall_output_collector.scenarios_denied_before_adjustment_started); + let ok_adjustment_percentage = (output_collector.oks * 100) + / (total_scenarios_evaluated - output_collector.scenarios_denied_before_adjustment_started); let required_success_rate = 70; assert!( ok_adjustment_percentage >= required_success_rate, @@ -652,7 +753,8 @@ fn write_brief_test_summary_into_file( With 'RecursionDrainedAllAccounts':.......... {}\n\ With late insufficient balance errors:. {}\n\n\ Legend\n\ - Partially adjusted accounts mark:...... {}", + Adjusted balances are highlighted by \ + this mark by the side:................. {}", number_of_requested_scenarios, total_of_scenarios_evaluated, overall_output_collector.oks, @@ -1189,72 +1291,3 @@ impl AppliedThresholds { struct IndividualThresholds { thresholds: Either, HashMap>, } - -fn try_make_qualified_payables_by_applied_thresholds( - payable_accounts: Vec, - applied_thresholds: &AppliedThresholds, - now: SystemTime, -) -> ( - Vec, - Vec<(Wallet, PaymentThresholds)>, -) { - let payment_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); - match applied_thresholds { - AppliedThresholds::Defaulted => ( - try_making_guaranteed_qualified_payables( - payable_accounts, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - false, - ), - vec![], - ), - AppliedThresholds::SingleButRandomized { common_thresholds } => ( - try_making_guaranteed_qualified_payables( - payable_accounts, - common_thresholds, - now, - false, - ), - vec![], - ), - AppliedThresholds::RandomizedForEachAccount { - individual_thresholds, - } => { - let vec_of_thresholds = individual_thresholds - .thresholds - .as_ref() - .left() - .expect("should be Vec at this stage"); - assert_eq!( - payable_accounts.len(), - vec_of_thresholds.len(), - "The number of generated \ - payables {} differs from their sets of thresholds {}, but one should've been derived \ - from the other", - payable_accounts.len(), - vec_of_thresholds.len() - ); - let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); - zipped.fold( - (vec![], vec![]), - |(mut qualified_payables, mut wallet_thresholds_pairs), - (payable, its_thresholds)| match make_single_qualified_payable_opt( - payable, - &payment_inspector, - &its_thresholds, - false, - now, - ) { - Some(qualified_payable) => { - let wallet = qualified_payable.bare_account.wallet.clone(); - qualified_payables.push(qualified_payable); - wallet_thresholds_pairs.push((wallet, *its_thresholds)); - (qualified_payables, wallet_thresholds_pairs) - } - None => (qualified_payables, wallet_thresholds_pairs), - }, - ) - } - } -} diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 045245124..481eec80e 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -154,10 +154,6 @@ mod tests { agent.maximum_added_gas_margin, WEB3_MAXIMAL_GAS_LIMIT_MARGIN ); - let expected_result: u128 = { - let gwei_amount = ((77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) as u128) * 244; - gwei_to_wei(gwei_amount) - }; - assert_eq!(result, expected_result); + assert_eq!(result, 19789620000000000); } } From bacf1703fc09cab56f103742736913b6a46c2065 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 12 Dec 2024 16:35:19 +0100 Subject: [PATCH 227/250] GH-711-review-one: refactoring in the fuzz test --- .../payment_adjuster/non_unit_tests/mod.rs | 299 +++++++----------- .../preparatory_analyser/mod.rs | 2 +- .../payment_adjuster/service_fee_adjuster.rs | 2 +- .../payable_scanner/agent_web3.rs | 3 - node/src/accountant/test_utils.rs | 4 +- 5 files changed, 120 insertions(+), 190 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index b002c3368..3d86e6d42 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -17,7 +17,7 @@ use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ PayableInspector, PayableThresholdsGaugeReal, }; use crate::accountant::test_utils::{ - make_single_qualified_payable_opt, try_making_guaranteed_qualified_payables, + make_single_qualified_payable_opt, try_to_make_guaranteed_qualified_payables, }; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; @@ -26,7 +26,6 @@ use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; -use masq_lib::percentage::PurePercentage; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::convert_collection; use rand; @@ -187,7 +186,7 @@ fn loading_test_with_randomized_params() { administrate_single_scenario_result( payment_adjuster_result, account_infos, - scenario.used_thresholds, + scenario.applied_thresholds, required_adjustment, cw_service_fee_balance_minor, ) @@ -216,16 +215,10 @@ fn try_making_single_valid_scenario( now: SystemTime, ) -> Option { let accounts_count = generate_non_zero_usize(gn, 25) + 1; - let thresholds_to_be_used = choose_thresholds(gn, accounts_count); - let (cw_service_fee_balance, qualified_payables, wallet_and_thresholds_pairs) = - try_generating_qualified_payables_and_cw_balance( - gn, - &thresholds_to_be_used, - accounts_count, - now, - )?; - let used_thresholds = - thresholds_to_be_used.fix_individual_thresholds_if_needed(wallet_and_thresholds_pairs); + + let (cw_service_fee_balance, qualified_payables, applied_thresholds) = + try_generating_qualified_payables_and_cw_balance(gn, accounts_count, now)?; + let analyzed_accounts: Vec = convert_collection(qualified_payables); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, analyzed_accounts.len()); @@ -236,17 +229,16 @@ fn try_making_single_valid_scenario( ); Some(PreparedAdjustmentAndThresholds { prepared_adjustment, - used_thresholds, + applied_thresholds, }) } fn make_payable_account( - idx: usize, + wallet: Wallet, thresholds: &PaymentThresholds, now: SystemTime, gn: &mut ThreadRng, ) -> PayableAccount { - let wallet = make_wallet(&format!("wallet{}", idx)); let debt_age = generate_debt_age(gn, thresholds); let service_fee_balance_minor = generate_highly_randomized_payable_account_balance(gn, thresholds); @@ -316,104 +308,57 @@ fn try_make_qualified_payables_by_applied_thresholds( payable_accounts: Vec, applied_thresholds: &AppliedThresholds, now: SystemTime, -) -> ( - Vec, - Vec<(Wallet, PaymentThresholds)>, -) { +) -> Vec { let payment_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); match applied_thresholds { - AppliedThresholds::Defaulted => ( - try_making_guaranteed_qualified_payables( - payable_accounts, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - false, - ), - vec![], + AppliedThresholds::Defaulted => try_to_make_guaranteed_qualified_payables( + payable_accounts, + &PRESERVED_TEST_PAYMENT_THRESHOLDS, + now, + false, ), - AppliedThresholds::SingleButRandomized { common_thresholds } => ( - try_making_guaranteed_qualified_payables( + AppliedThresholds::CommonButRandomized { common_thresholds } => { + try_to_make_guaranteed_qualified_payables( payable_accounts, common_thresholds, now, false, - ), - vec![], - ), + ) + } AppliedThresholds::RandomizedForEachAccount { individual_thresholds, } => { - let vec_of_thresholds = individual_thresholds - .thresholds - .as_ref() - .left() - .expect("should be Vec at this stage"); - assert_eq!( - payable_accounts.len(), - vec_of_thresholds.len(), - "The number of generated \ - payables {} differs from their sets of thresholds {}, but one should've been derived \ - from the other", - payable_accounts.len(), - vec_of_thresholds.len() - ); + let vec_of_thresholds = individual_thresholds.values().collect_vec(); let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); - zipped.fold( - (vec![], vec![]), - |(mut qualified_payables, mut wallet_thresholds_pairs), - (payable, its_thresholds)| match make_single_qualified_payable_opt( - payable, - &payment_inspector, - &its_thresholds, - false, - now, - ) { - Some(qualified_payable) => { - let wallet = qualified_payable.bare_account.wallet.clone(); - qualified_payables.push(qualified_payable); - wallet_thresholds_pairs.push((wallet, *its_thresholds)); - (qualified_payables, wallet_thresholds_pairs) - } - None => (qualified_payables, wallet_thresholds_pairs), - }, - ) + zipped + .flat_map(|(qualified_payable, thresholds)| { + make_single_qualified_payable_opt( + qualified_payable, + &payment_inspector, + &thresholds, + false, + now, + ) + }) + .collect() } } } fn try_generating_qualified_payables_and_cw_balance( gn: &mut ThreadRng, - thresholds_to_be_used: &AppliedThresholds, accounts_count: usize, now: SystemTime, -) -> Option<( - u128, - Vec, - Vec<(Wallet, PaymentThresholds)>, -)> { - let payables = make_payables_according_to_thresholds_setup( - gn, - &thresholds_to_be_used, - accounts_count, - now, - ); +) -> Option<(u128, Vec, AppliedThresholds)> { + let (payables, applied_thresholds) = + make_payables_according_to_thresholds_setup(gn, accounts_count, now); - let (qualified_payables, wallet_and_thresholds_pairs) = - try_make_qualified_payables_by_applied_thresholds(payables, &thresholds_to_be_used, now); + let qualified_payables = + try_make_qualified_payables_by_applied_thresholds(payables, &applied_thresholds, now); + + let cw_service_fee_balance_minor = + pick_appropriate_cw_service_fee_balance(gn, &qualified_payables, accounts_count); - let balance_average = { - let sum: u128 = sum_as(&qualified_payables, |account| { - account.initial_balance_minor() - }); - sum / accounts_count as u128 - }; - let cw_service_fee_balance_minor = { - let multiplier = 1000; - let max_pieces = accounts_count * multiplier; - let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; - balance_average / multiplier as u128 * number_of_pieces - }; - // let required_service_fee_total: u128 = sum_as(&qualified_payables, |account| { account.initial_balance_minor() }); @@ -423,7 +368,7 @@ fn try_generating_qualified_payables_and_cw_balance( Some(( cw_service_fee_balance_minor, qualified_payables, - wallet_and_thresholds_pairs, + applied_thresholds, )) } } @@ -445,74 +390,52 @@ fn pick_appropriate_cw_service_fee_balance( fn make_payables_according_to_thresholds_setup( gn: &mut ThreadRng, - thresholds_to_be_used: &AppliedThresholds, accounts_count: usize, now: SystemTime, -) -> Vec { - match thresholds_to_be_used { +) -> (Vec, AppliedThresholds) { + let wallets = prepare_account_wallets(accounts_count); + + let nominated_thresholds = choose_thresholds(gn, &wallets); + + let payables = match &nominated_thresholds { AppliedThresholds::Defaulted => make_payables_with_common_thresholds( gn, + wallets, &PRESERVED_TEST_PAYMENT_THRESHOLDS, - accounts_count, now, ), - AppliedThresholds::SingleButRandomized { common_thresholds } => { - make_payables_with_common_thresholds(gn, common_thresholds, accounts_count, now) + AppliedThresholds::CommonButRandomized { common_thresholds } => { + make_payables_with_common_thresholds(gn, wallets, common_thresholds, now) } AppliedThresholds::RandomizedForEachAccount { individual_thresholds, - } => { - let vec_of_thresholds = individual_thresholds - .thresholds - .as_ref() - .left() - .expect("should be Vec at this stage"); - assert_eq!(vec_of_thresholds.len(), accounts_count); - make_payables_with_individual_thresholds(gn, vec_of_thresholds, now) - } - } -} + } => make_payables_with_individual_thresholds(gn, &individual_thresholds, now), + }; -fn make_payables_with_common_thresholds( - gn: &mut ThreadRng, - common_thresholds: &PaymentThresholds, - accounts_count: usize, - now: SystemTime, -) -> Vec { - (0..accounts_count) - .map(|idx| make_payable_account(idx, common_thresholds, now, gn)) - .collect() + (payables, nominated_thresholds) } -fn make_payables_with_individual_thresholds( - gn: &mut ThreadRng, - individual_thresholds: &[PaymentThresholds], - now: SystemTime, -) -> Vec { - individual_thresholds - .iter() - .enumerate() - .map(|(idx, thresholds)| make_payable_account(idx, thresholds, now, gn)) +fn prepare_account_wallets(accounts_count: usize) -> Vec { + (0..accounts_count) + .map(|idx| make_wallet(&format!("wallet{}", idx))) .collect() } -fn choose_thresholds(gn: &mut ThreadRng, accounts_count: usize) -> AppliedThresholds { +fn choose_thresholds(gn: &mut ThreadRng, prepared_wallets: &[Wallet]) -> AppliedThresholds { let be_defaulted = generate_boolean(gn); if be_defaulted { AppliedThresholds::Defaulted } else { - let be_common_for_all = generate_boolean(gn); - if be_common_for_all { - AppliedThresholds::SingleButRandomized { + let be_same_for_all_accounts = generate_boolean(gn); + if be_same_for_all_accounts { + AppliedThresholds::CommonButRandomized { common_thresholds: return_single_randomized_thresholds(gn), } } else { - let thresholds_set = (0..accounts_count) - .map(|_| return_single_randomized_thresholds(gn)) - .collect(); - let individual_thresholds = IndividualThresholds { - thresholds: Either::Left(thresholds_set), - }; + let individual_thresholds = prepared_wallets + .iter() + .map(|wallet| (wallet.clone(), return_single_randomized_thresholds(gn))) + .collect::>(); AppliedThresholds::RandomizedForEachAccount { individual_thresholds, } @@ -520,6 +443,29 @@ fn choose_thresholds(gn: &mut ThreadRng, accounts_count: usize) -> AppliedThresh } } +fn make_payables_with_common_thresholds( + gn: &mut ThreadRng, + prepared_wallets: Vec, + common_thresholds: &PaymentThresholds, + now: SystemTime, +) -> Vec { + prepared_wallets + .into_iter() + .map(|wallet| make_payable_account(wallet, common_thresholds, now, gn)) + .collect() +} + +fn make_payables_with_individual_thresholds( + gn: &mut ThreadRng, + wallets_and_thresholds: &HashMap, + now: SystemTime, +) -> Vec { + wallets_and_thresholds + .iter() + .map(|(wallet, thresholds)| make_payable_account(wallet.clone(), thresholds, now, gn)) + .collect() +} + fn return_single_randomized_thresholds(gn: &mut ThreadRng) -> PaymentThresholds { let permanent_debt_allowed_gwei = generate_range(gn, 100, 1_000_000_000); let debt_threshold_gwei = @@ -849,8 +795,8 @@ fn render_scenario_header( fn resolve_comment_on_thresholds(applied_thresholds: &AppliedThresholds) -> String { match applied_thresholds { - AppliedThresholds::Defaulted | AppliedThresholds::SingleButRandomized { .. } => { - if let AppliedThresholds::SingleButRandomized { common_thresholds } = applied_thresholds + AppliedThresholds::Defaulted | AppliedThresholds::CommonButRandomized { .. } => { + if let AppliedThresholds::CommonButRandomized { common_thresholds } = applied_thresholds { format!("SHARED BUT CUSTOM\n{}", common_thresholds) } else { @@ -887,41 +833,56 @@ fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { ) } -fn render_accounts( +fn render_accounts( file: &mut File, - accounts: &[A], + accounts: &[Account], used_thresholds: &AppliedThresholds, mut render_account: F, ) where - A: AccountWithWallet, - F: FnMut(&mut File, &A, Option<&PaymentThresholds>), + Account: AccountWithWallet, + F: FnMut(&mut File, &Account, Option<&PaymentThresholds>), { - let set_of_individual_thresholds_opt = if let AppliedThresholds::RandomizedForEachAccount { + let individual_thresholds_opt = if let AppliedThresholds::RandomizedForEachAccount { individual_thresholds, } = used_thresholds { - Some(individual_thresholds.thresholds.as_ref().right().unwrap()) + Some(individual_thresholds) } else { None }; + accounts .iter() .map(|account| { ( account, - set_of_individual_thresholds_opt.map(|thresholds| { - thresholds - .get(&account.wallet()) - .expect("Original thresholds missing") - }), + fetch_individual_thresholds_for_account_if_appropriate( + individual_thresholds_opt, + account, + ), ) }) .for_each(|(account, individual_thresholds_opt)| { render_account(file, account, individual_thresholds_opt) }); + file.write(b"\n").unwrap(); } +fn fetch_individual_thresholds_for_account_if_appropriate<'a, Account>( + individual_thresholds_opt: Option<&'a HashMap>, + account: &'a Account, +) -> Option<&'a PaymentThresholds> +where + Account: AccountWithWallet, +{ + individual_thresholds_opt.map(|wallets_and_thresholds| { + wallets_and_thresholds + .get(&account.wallet()) + .expect("Original thresholds missing") + }) +} + trait AccountWithWallet { fn wallet(&self) -> &Wallet; } @@ -1222,7 +1183,7 @@ impl PercentageFulfillmentDistribution { struct PreparedAdjustmentAndThresholds { prepared_adjustment: PreparedAdjustment, - used_thresholds: AppliedThresholds, + applied_thresholds: AppliedThresholds, } struct CommonScenarioInfo { @@ -1256,38 +1217,10 @@ impl AccountWithWallet for AccountInfo { enum AppliedThresholds { Defaulted, - SingleButRandomized { + CommonButRandomized { common_thresholds: PaymentThresholds, }, RandomizedForEachAccount { - individual_thresholds: IndividualThresholds, + individual_thresholds: HashMap, }, } - -impl AppliedThresholds { - fn fix_individual_thresholds_if_needed( - self, - wallet_and_thresholds_pairs: Vec<(Wallet, PaymentThresholds)>, - ) -> Self { - match self { - AppliedThresholds::RandomizedForEachAccount { .. } => { - assert!( - !wallet_and_thresholds_pairs.is_empty(), - "Pairs should be missing by now" - ); - let hash_map = HashMap::from_iter(wallet_and_thresholds_pairs); - let individual_thresholds = IndividualThresholds { - thresholds: Either::Right(hash_map), - }; - AppliedThresholds::RandomizedForEachAccount { - individual_thresholds, - } - } - x => x, - } - } -} - -struct IndividualThresholds { - thresholds: Either, HashMap>, -} diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index b6c9375f9..70652e984 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -374,7 +374,7 @@ mod tests { }; use crate::accountant::payment_adjuster::test_utils::{ make_weighed_account, multiply_by_billion, multiply_by_billion_concise, - multiply_by_quintillion, multiply_by_quintillion_concise, DisqualificationGaugeMock, + DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 3e5f45d72..6b42e1a0d 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -201,7 +201,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, multiply_by_billion, multiply_by_quintillion, + make_non_guaranteed_unconfirmed_adjustment, multiply_by_quintillion, multiply_by_quintillion_concise, }; diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index 481eec80e..d16423d13 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -87,11 +87,8 @@ mod tests { BlockchainAgentWeb3, WEB3_MAXIMAL_GAS_LIMIT_MARGIN, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; - use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::test_utils::make_wallet; - - use crate::accountant::gwei_to_wei; use web3::types::U256; #[test] diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index d243b76d1..4e5ab644f 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1754,7 +1754,7 @@ pub fn make_qualified_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - try_making_guaranteed_qualified_payables(payables, payment_thresholds, now, true) + try_to_make_guaranteed_qualified_payables(payables, payment_thresholds, now, true) } pub fn make_analyzed_payables( @@ -1765,7 +1765,7 @@ pub fn make_analyzed_payables( convert_collection(make_qualified_payables(payables, payment_thresholds, now)) } -pub fn try_making_guaranteed_qualified_payables( +pub fn try_to_make_guaranteed_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, now: SystemTime, From 4602f2d818d456ecb0cc6150abe238944666e7f3 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 13 Dec 2024 16:30:32 +0100 Subject: [PATCH 228/250] GH-711-review-one: finished refactoring the fuzz test --- .../payment_adjuster/non_unit_tests/mod.rs | 459 ++++++++++-------- 1 file changed, 256 insertions(+), 203 deletions(-) diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 3d86e6d42..267efe08f 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -33,6 +33,7 @@ use rand::distributions::uniform::SampleUniform; use rand::rngs::ThreadRng; use rand::{thread_rng, Rng}; use std::collections::HashMap; +use std::fmt::{Display, Formatter}; use std::fs::File; use std::io::Write; use std::time::SystemTime; @@ -94,7 +95,7 @@ fn loading_test_with_randomized_params() { // CW service fee balance: 32,041,461,894,055,482 wei // Portion of CW balance used: 100% - // Maximal txt count due to CW txt fee balance: UNLIMITED + // Maximal txn count due to CW txn fee balance: UNLIMITED // Used PaymentThresholds: DEFAULTED // 2000000|1000|1000|1000000|500000|1000000 // _____________________________________________________________________________________________ @@ -116,16 +117,15 @@ fn loading_test_with_randomized_params() { let number_of_requested_scenarios = 2000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); - let test_overall_output_collector = - TestOverallOutputCollector::new(invalidly_generated_scenarios); + let output_collector = TestOverallOutputCollector::new(invalidly_generated_scenarios); struct FirstStageOutput { - test_overall_output_collector: TestOverallOutputCollector, + output_collector: TestOverallOutputCollector, allowed_scenarios: Vec, } let init = FirstStageOutput { - test_overall_output_collector, + output_collector, allowed_scenarios: vec![], }; let first_stage_output = scenarios @@ -155,7 +155,7 @@ fn loading_test_with_randomized_params() { } Err(_) => { output_collector - .test_overall_output_collector + .output_collector .scenarios_denied_before_adjustment_started += 1; None } @@ -170,7 +170,7 @@ fn loading_test_with_randomized_params() { }); let second_stage_scenarios = first_stage_output.allowed_scenarios; - let test_overall_output_collector = first_stage_output.test_overall_output_collector; + let test_overall_output_collector = first_stage_output.output_collector; let scenario_adjustment_results = second_stage_scenarios .into_iter() .map(|scenario| { @@ -518,24 +518,26 @@ fn administrate_single_scenario_result( let common = CommonScenarioInfo { cw_service_fee_balance_minor, required_adjustment, - used_thresholds, + payment_thresholds: used_thresholds, }; let reinterpreted_result = match payment_adjuster_result { Ok(outbound_payment_instructions) => { - let mut adjusted_accounts = outbound_payment_instructions.affordable_accounts; - let portion_of_cw_cumulatively_used_percents = { - let used_absolute: u128 = sum_as(&adjusted_accounts, |account| account.balance_wei); - ((100 * used_absolute) / common.cw_service_fee_balance_minor) as u8 - }; - let adjusted_accounts = - interpretable_adjustment_results(account_infos, &mut adjusted_accounts); + let adjusted_accounts = outbound_payment_instructions.affordable_accounts; + let portion_of_cw_cumulatively_used_percents = + PercentPortionOfCWUsed::new(&adjusted_accounts, &common); + let merged = + merge_information_about_particular_account(account_infos, adjusted_accounts); + let interpretable_adjustments = merged + .into_iter() + .map(InterpretableAccountAdjustmentResult::new) + .collect_vec(); let (partially_sorted_interpretable_adjustments, were_no_accounts_eliminated) = - sort_interpretable_adjustments(adjusted_accounts); + sort_interpretable_adjustments(interpretable_adjustments); Ok(SuccessfulAdjustment { common, portion_of_cw_cumulatively_used_percents, partially_sorted_interpretable_adjustments, - were_no_accounts_eliminated, + no_accounts_eliminated: were_no_accounts_eliminated, }) } Err(adjuster_error) => Err(FailedAdjustment { @@ -548,18 +550,57 @@ fn administrate_single_scenario_result( ScenarioResult::new(reinterpreted_result) } -fn interpretable_adjustment_results( - account_infos: Vec, - adjusted_accounts: &mut Vec, -) -> Vec { - account_infos +fn merge_information_about_particular_account( + accounts_infos: Vec, + accounts_after_adjustment: Vec, +) -> Vec<(AccountInfo, Option)> { + let mut accounts_hashmap = accounts_after_adjustment + .into_iter() + .map(|account| (account.wallet.clone(), account)) + .collect::>(); + + accounts_infos .into_iter() - .map(|account_info| { - prepare_interpretable_adjustment_result(account_info, adjusted_accounts) + .map(|info| { + let adjusted_account_opt = accounts_hashmap.remove(&info.wallet); + (info, adjusted_account_opt) }) .collect() } +enum PercentPortionOfCWUsed { + Percents(u8), + LessThanOnePercent, +} + +impl PercentPortionOfCWUsed { + fn new(adjusted_accounts: &[PayableAccount], common: &CommonScenarioInfo) -> Self { + let used_absolute: u128 = sum_as(adjusted_accounts, |account| account.balance_wei); + let percents = ((100 * used_absolute) / common.cw_service_fee_balance_minor) as u8; + if percents >= 1 { + PercentPortionOfCWUsed::Percents(percents) + } else { + PercentPortionOfCWUsed::LessThanOnePercent + } + } + + fn as_plain_number(&self) -> u8 { + match self { + Self::Percents(percents) => *percents, + Self::LessThanOnePercent => 1, + } + } +} + +impl Display for PercentPortionOfCWUsed { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Percents(percents) => write!(f, "{percents}"), + Self::LessThanOnePercent => write!(f, "< 1"), + } + } +} + struct ScenarioResult { result: Result, } @@ -572,9 +613,9 @@ impl ScenarioResult { struct SuccessfulAdjustment { common: CommonScenarioInfo, - portion_of_cw_cumulatively_used_percents: u8, - partially_sorted_interpretable_adjustments: Vec, - were_no_accounts_eliminated: bool, + portion_of_cw_cumulatively_used_percents: PercentPortionOfCWUsed, + partially_sorted_interpretable_adjustments: Vec, + no_accounts_eliminated: bool, } struct FailedAdjustment { @@ -606,26 +647,24 @@ fn render_results_to_file_and_attempt_basic_assertions( output_collector: TestOverallOutputCollector, ) { let file_dir = ensure_node_home_directory_exists("payment_adjuster", "tests"); - let mut file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); - introduction(&mut file); + let mut output_file = File::create(file_dir.join("loading_test_output.txt")).unwrap(); + introduction(&mut output_file); let output_collector = scenario_results .into_iter() .fold(output_collector, |acc, scenario_result| { - do_final_processing_of_single_scenario(&mut file, acc, scenario_result) + do_final_processing_of_single_scenario(&mut output_file, acc, scenario_result) }); - let total_scenarios_evaluated = output_collector.scenarios_denied_before_adjustment_started - + output_collector.oks - + output_collector.all_accounts_eliminated - + output_collector.late_immoderately_insufficient_service_fee_balance; - write_brief_test_summary_into_file( - &mut file, + let total_scenarios_evaluated = + output_collector.total_evaluated_scenarios_except_those_discarded_early(); + write_brief_test_summary_at_file_s_tail( + &mut output_file, &output_collector, number_of_requested_scenarios, total_scenarios_evaluated, ); let total_scenarios_handled_including_invalid_ones = - total_scenarios_evaluated + output_collector.invalidly_generated_scenarios; + output_collector.total_evaluated_scenarios_including_invalid_ones(); assert_eq!( total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios, "All handled scenarios including those invalid ones ({}) != requested scenarios count ({})", @@ -636,26 +675,27 @@ fn render_results_to_file_and_attempt_basic_assertions( // so that they are picked up and let in the PaymentAdjuster. We'll be better off truly faithful // to the use case and the expected conditions. Therefore, we insist on making "guaranteed" // QualifiedPayableAccounts out of PayableAccount which is where we take the losses. - let entry_check_pass_rate = 100 + let actual_entry_check_pass_percentage = 100 - ((output_collector.scenarios_denied_before_adjustment_started * 100) / total_scenarios_evaluated); - let required_pass_rate = 50; + const REQUIRED_ENTRY_CHECK_PASS_PERCENTAGE: usize = 50; assert!( - entry_check_pass_rate >= required_pass_rate, - "Not at least {}% from those {} scenarios \ - generated for this test allows PaymentAdjuster to continue doing its job and ends too early. \ - Instead only {}%. Setup of the test might be needed", - required_pass_rate, + actual_entry_check_pass_percentage >= REQUIRED_ENTRY_CHECK_PASS_PERCENTAGE, + "Not at least {}% from those {} scenarios generated for this test allows PaymentAdjuster to \ + continue doing its job and ends too early. Instead only {}%. Setup of the test might be \ + needed", + REQUIRED_ENTRY_CHECK_PASS_PERCENTAGE, total_scenarios_evaluated, - entry_check_pass_rate + actual_entry_check_pass_percentage ); let ok_adjustment_percentage = (output_collector.oks * 100) / (total_scenarios_evaluated - output_collector.scenarios_denied_before_adjustment_started); - let required_success_rate = 70; + const REQUIRED_SUCCESSFUL_ADJUSTMENT_PERCENTAGE: usize = 70; assert!( - ok_adjustment_percentage >= required_success_rate, - "Not at least {}% from {} adjustment procedures from PaymentAdjuster runs finished with success, only {}%", - required_success_rate, + ok_adjustment_percentage >= REQUIRED_SUCCESSFUL_ADJUSTMENT_PERCENTAGE, + "Not at least {}% from {} adjustment procedures from PaymentAdjuster runs finished with \ + success, only {}%", + REQUIRED_SUCCESSFUL_ADJUSTMENT_PERCENTAGE, total_scenarios_evaluated, ok_adjustment_percentage ); @@ -674,11 +714,11 @@ fn introduction(file: &mut File) { write_thick_dividing_line(file) } -fn write_brief_test_summary_into_file( +fn write_brief_test_summary_at_file_s_tail( file: &mut File, - overall_output_collector: &TestOverallOutputCollector, - number_of_requested_scenarios: usize, - total_of_scenarios_evaluated: usize, + output_collector: &TestOverallOutputCollector, + scenarios_requested: usize, + scenarios_evaluated: usize, ) { write_thick_dividing_line(file); file.write_fmt(format_args!( @@ -696,30 +736,30 @@ fn write_brief_test_summary_into_file( {}\n\n\ Unsuccessful\n\ Caught by the entry check:............. {}\n\ - With 'RecursionDrainedAllAccounts':.......... {}\n\ + With 'RecursionDrainedAllAccounts':.... {}\n\ With late insufficient balance errors:. {}\n\n\ Legend\n\ Adjusted balances are highlighted by \ - this mark by the side:................. {}", - number_of_requested_scenarios, - total_of_scenarios_evaluated, - overall_output_collector.oks, - overall_output_collector.with_no_accounts_eliminated, - overall_output_collector + these marks by the side:............. . {}", + scenarios_requested, + scenarios_evaluated, + output_collector.oks, + output_collector.with_no_accounts_eliminated, + output_collector .fulfillment_distribution_for_transaction_fee_adjustments .total_scenarios(), - overall_output_collector + output_collector .fulfillment_distribution_for_transaction_fee_adjustments .render_in_two_lines(), - overall_output_collector + output_collector .fulfillment_distribution_for_service_fee_adjustments .total_scenarios(), - overall_output_collector + output_collector .fulfillment_distribution_for_service_fee_adjustments .render_in_two_lines(), - overall_output_collector.scenarios_denied_before_adjustment_started, - overall_output_collector.all_accounts_eliminated, - overall_output_collector.late_immoderately_insufficient_service_fee_balance, + output_collector.scenarios_denied_before_adjustment_started, + output_collector.all_accounts_eliminated, + output_collector.late_immoderately_insufficient_service_fee_balance, NON_EXHAUSTED_ACCOUNT_MARKER )) .unwrap() @@ -727,32 +767,43 @@ fn write_brief_test_summary_into_file( fn do_final_processing_of_single_scenario( file: &mut File, - mut test_overall_output: TestOverallOutputCollector, + mut output_collector: TestOverallOutputCollector, scenario: ScenarioResult, ) -> TestOverallOutputCollector { match scenario.result { Ok(positive) => { - if positive.were_no_accounts_eliminated { - test_overall_output.with_no_accounts_eliminated += 1 + if positive.no_accounts_eliminated { + output_collector.with_no_accounts_eliminated += 1 } if matches!( positive.common.required_adjustment, Adjustment::BeginByTransactionFee { .. } ) { - test_overall_output + output_collector .fulfillment_distribution_for_transaction_fee_adjustments .collected_fulfillment_percentages - .push(positive.portion_of_cw_cumulatively_used_percents) + .push( + positive + .portion_of_cw_cumulatively_used_percents + .as_plain_number(), + ) } - if positive.common.required_adjustment == Adjustment::ByServiceFee { - test_overall_output + if matches!( + positive.common.required_adjustment, + Adjustment::ByServiceFee + ) { + output_collector .fulfillment_distribution_for_service_fee_adjustments .collected_fulfillment_percentages - .push(positive.portion_of_cw_cumulatively_used_percents) + .push( + positive + .portion_of_cw_cumulatively_used_percents + .as_plain_number(), + ) } render_positive_scenario(file, positive); - test_overall_output.oks += 1; - test_overall_output + output_collector.oks += 1; + output_collector } Err(negative) => { match negative.adjuster_error { @@ -760,14 +811,14 @@ fn do_final_processing_of_single_scenario( panic!("Such errors should be already filtered out") } PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => { - test_overall_output.late_immoderately_insufficient_service_fee_balance += 1 + output_collector.late_immoderately_insufficient_service_fee_balance += 1 } PaymentAdjusterError::RecursionDrainedAllAccounts => { - test_overall_output.all_accounts_eliminated += 1 + output_collector.all_accounts_eliminated += 1 } } render_negative_scenario(file, negative); - test_overall_output + output_collector } } } @@ -775,64 +826,70 @@ fn do_final_processing_of_single_scenario( fn render_scenario_header( file: &mut File, scenario_common: &CommonScenarioInfo, - portion_of_cw_used_percents: u8, + portion_of_cw_used_percents: PercentPortionOfCWUsed, ) { write_thick_dividing_line(file); file.write_fmt(format_args!( - "CW service fee balance: {} wei\n\ - Portion of CW balance used: {}%\n\ - Maximal txt count due to CW txt fee balance: {}\n\ - Used PaymentThresholds: {}\n", + "CW service fee balance: {} wei\n\ + Portion of CW balance used: {} %\n\ + Maximal txn count due to CW txn fee balance: {}\n\ + Used PaymentThresholds: {}\n\n", scenario_common .cw_service_fee_balance_minor .separate_with_commas(), portion_of_cw_used_percents, - resolve_affordable_transaction_count(&scenario_common.required_adjustment), - resolve_comment_on_thresholds(&scenario_common.used_thresholds) + scenario_common.resolve_affordable_tx_count_by_tx_fee(), + scenario_common.resolve_thresholds_description() )) .unwrap(); } -fn resolve_comment_on_thresholds(applied_thresholds: &AppliedThresholds) -> String { - match applied_thresholds { - AppliedThresholds::Defaulted | AppliedThresholds::CommonButRandomized { .. } => { - if let AppliedThresholds::CommonButRandomized { common_thresholds } = applied_thresholds - { - format!("SHARED BUT CUSTOM\n{}", common_thresholds) - } else { - format!("DEFAULTED\n{}", PRESERVED_TEST_PAYMENT_THRESHOLDS) - } - } - AppliedThresholds::RandomizedForEachAccount { .. } => "INDIVIDUAL".to_string(), - } -} - fn render_positive_scenario(file: &mut File, result: SuccessfulAdjustment) { render_scenario_header( file, &result.common, result.portion_of_cw_cumulatively_used_percents, ); - write_thin_dividing_line(file); let adjusted_accounts = result.partially_sorted_interpretable_adjustments; render_accounts( file, &adjusted_accounts, - &result.common.used_thresholds, + &result.common.payment_thresholds, |file, account, individual_thresholds_opt| { single_account_output( file, - account.info.initially_requested_service_fee_minor, - account.info.debt_age_s, individual_thresholds_opt, - account.bills_coverage_in_percentage_opt, + &account.info, + account.bill_coverage_in_percentage_opt, ) }, ) } +fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) { + render_scenario_header( + file, + &negative_result.common, + PercentPortionOfCWUsed::Percents(0), + ); + render_accounts( + file, + &negative_result.account_infos, + &negative_result.common.payment_thresholds, + |file, account, individual_thresholds_opt| { + single_account_output(file, individual_thresholds_opt, account, None) + }, + ); + write_thin_dividing_line(file); + write_error(file, negative_result.adjuster_error) +} + +trait AccountWithWallet { + fn wallet(&self) -> &Wallet; +} + fn render_accounts( file: &mut File, accounts: &[Account], @@ -883,45 +940,40 @@ where }) } -trait AccountWithWallet { - fn wallet(&self) -> &Wallet; -} - -const FIRST_COLUMN_WIDTH: usize = 50; +const FIRST_COLUMN_WIDTH: usize = 34; const AGE_COLUMN_WIDTH: usize = 8; - const STARTING_GAP: usize = 6; fn single_account_output( file: &mut File, - balance_minor: u128, - age_s: u64, individual_thresholds_opt: Option<&PaymentThresholds>, + account_info: &AccountInfo, bill_coverage_in_percentage_opt: Option, ) { let first_column_width = FIRST_COLUMN_WIDTH; let age_width = AGE_COLUMN_WIDTH; let starting_gap = STARTING_GAP; - let _ = file - .write_fmt(format_args!( - "{}{:first_column_width$} wei | {:>age_width$} s | {}\n", - individual_thresholds_opt - .map(|thresholds| format!( - "{:first_column_width$}\n", - "", thresholds - )) - .unwrap_or("".to_string()), - "", - balance_minor.separate_with_commas(), - age_s.separate_with_commas(), - resolve_account_ending_status_graphically(bill_coverage_in_percentage_opt), - )) - .unwrap(); + file.write_fmt(format_args!( + "{}{:first_column_width$} wei | {:>age_width$} s | {}\n", + individual_thresholds_opt + .map(|thresholds| format!( + "{:first_column_width$}\n", + "", thresholds + )) + .unwrap_or("".to_string()), + "", + account_info + .initially_requested_service_fee_minor + .separate_with_commas(), + account_info.debt_age_s.separate_with_commas(), + resolve_account_fulfilment_status_graphically(bill_coverage_in_percentage_opt), + )) + .unwrap(); } const NON_EXHAUSTED_ACCOUNT_MARKER: &str = "# # # # # # # #"; -fn resolve_account_ending_status_graphically( +fn resolve_account_fulfilment_status_graphically( bill_coverage_in_percentage_opt: Option, ) -> String { match bill_coverage_in_percentage_opt { @@ -937,27 +989,6 @@ fn resolve_account_ending_status_graphically( } } -fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) { - render_scenario_header(file, &negative_result.common, 0); - write_thin_dividing_line(file); - render_accounts( - file, - &negative_result.account_infos, - &negative_result.common.used_thresholds, - |file, account, individual_thresholds_opt| { - single_account_output( - file, - account.initially_requested_service_fee_minor, - account.debt_age_s, - individual_thresholds_opt, - None, - ) - }, - ); - write_thin_dividing_line(file); - write_error(file, negative_result.adjuster_error) -} - fn write_error(file: &mut File, error: PaymentAdjusterError) { file.write_fmt(format_args!( "Scenario resulted in a failure: {:?}\n", @@ -966,15 +997,6 @@ fn write_error(file: &mut File, error: PaymentAdjusterError) { .unwrap() } -fn resolve_affordable_transaction_count(adjustment: &Adjustment) -> String { - match adjustment { - Adjustment::ByServiceFee => "UNLIMITED".to_string(), - Adjustment::BeginByTransactionFee { - transaction_count_limit, - } => transaction_count_limit.to_string(), - } -} - fn write_thick_dividing_line(file: &mut dyn Write) { write_ln_made_of(file, '=') } @@ -991,64 +1013,34 @@ fn write_ln_made_of(file: &mut dyn Write, char: char) { .unwrap(); } -fn prepare_interpretable_adjustment_result( - account_info: AccountInfo, - resulted_affordable_accounts: &mut Vec, -) -> InterpretableAdjustmentResult { - let adjusted_account_idx_opt = resulted_affordable_accounts - .iter() - .position(|account| account.wallet == account_info.wallet); - let bills_coverage_in_percentage_opt = match adjusted_account_idx_opt { - Some(idx) => { - let adjusted_account = resulted_affordable_accounts.remove(idx); - assert_eq!(adjusted_account.wallet, account_info.wallet); - let bill_coverage_in_percentage = { - let percentage = (adjusted_account.balance_wei * 100) - / account_info.initially_requested_service_fee_minor; - u8::try_from(percentage).unwrap() - }; - Some(bill_coverage_in_percentage) - } - None => None, - }; - InterpretableAdjustmentResult { - info: AccountInfo { - wallet: account_info.wallet, - debt_age_s: account_info.debt_age_s, - initially_requested_service_fee_minor: account_info - .initially_requested_service_fee_minor, - }, - - bills_coverage_in_percentage_opt, - } -} - fn sort_interpretable_adjustments( - interpretable_adjustments: Vec, -) -> (Vec, bool) { + interpretable_adjustments: Vec, +) -> (Vec, bool) { let (finished, eliminated): ( - Vec, - Vec, + Vec, + Vec, ) = interpretable_adjustments .into_iter() - .partition(|adjustment| adjustment.bills_coverage_in_percentage_opt.is_some()); + .partition(|adjustment| adjustment.bill_coverage_in_percentage_opt.is_some()); let were_no_accounts_eliminated = eliminated.is_empty(); + // Sorting in descending order by bills coverage in percentage and ascending by balances let finished_sorted = finished.into_iter().sorted_by(|result_a, result_b| { Ord::cmp( &( - result_b.bills_coverage_in_percentage_opt, + result_b.bill_coverage_in_percentage_opt, result_a.info.initially_requested_service_fee_minor, ), &( - result_a.bills_coverage_in_percentage_opt, + result_a.bill_coverage_in_percentage_opt, result_b.info.initially_requested_service_fee_minor, ), ) }); + // Sorting in descending order let eliminated_sorted = eliminated.into_iter().sorted_by(|result_a, result_b| { Ord::cmp( - &result_a.info.initially_requested_service_fee_minor, &result_b.info.initially_requested_service_fee_minor, + &result_a.info.initially_requested_service_fee_minor, ) }); let all_results = finished_sorted.chain(eliminated_sorted).collect(); @@ -1103,6 +1095,18 @@ impl TestOverallOutputCollector { late_immoderately_insufficient_service_fee_balance: 0, } } + + fn total_evaluated_scenarios_except_those_discarded_early(&self) -> usize { + self.scenarios_denied_before_adjustment_started + + self.oks + + self.all_accounts_eliminated + + self.late_immoderately_insufficient_service_fee_balance + } + + fn total_evaluated_scenarios_including_invalid_ones(&self) -> usize { + self.total_evaluated_scenarios_except_those_discarded_early() + + self.invalidly_generated_scenarios + } } #[derive(Default)] @@ -1189,20 +1193,69 @@ struct PreparedAdjustmentAndThresholds { struct CommonScenarioInfo { cw_service_fee_balance_minor: u128, required_adjustment: Adjustment, - used_thresholds: AppliedThresholds, + payment_thresholds: AppliedThresholds, } -struct InterpretableAdjustmentResult { + +impl CommonScenarioInfo { + fn resolve_affordable_tx_count_by_tx_fee(&self) -> String { + match self.required_adjustment { + Adjustment::ByServiceFee => "UNLIMITED".to_string(), + Adjustment::BeginByTransactionFee { + transaction_count_limit, + } => transaction_count_limit.to_string(), + } + } + + fn resolve_thresholds_description(&self) -> String { + match self.payment_thresholds { + AppliedThresholds::Defaulted => { + format!("DEFAULTED\n{}", PRESERVED_TEST_PAYMENT_THRESHOLDS) + } + AppliedThresholds::CommonButRandomized { common_thresholds } => { + format!("SHARED BUT CUSTOM\n{}", common_thresholds) + } + AppliedThresholds::RandomizedForEachAccount { .. } => "INDIVIDUAL".to_string(), + } + } +} + +struct InterpretableAccountAdjustmentResult { info: AccountInfo, // Account was eliminated from payment if None - bills_coverage_in_percentage_opt: Option, + bill_coverage_in_percentage_opt: Option, } -impl AccountWithWallet for InterpretableAdjustmentResult { +impl AccountWithWallet for InterpretableAccountAdjustmentResult { fn wallet(&self) -> &Wallet { &self.info.wallet } } +impl InterpretableAccountAdjustmentResult { + fn new((info, non_eliminated_payable): (AccountInfo, Option)) -> Self { + let bill_coverage_in_percentage_opt = match &non_eliminated_payable { + Some(payable) => { + let bill_coverage_in_percentage = { + let percentage = + (payable.balance_wei * 100) / info.initially_requested_service_fee_minor; + u8::try_from(percentage).unwrap() + }; + Some(bill_coverage_in_percentage) + } + None => None, + }; + InterpretableAccountAdjustmentResult { + info: AccountInfo { + wallet: info.wallet, + debt_age_s: info.debt_age_s, + initially_requested_service_fee_minor: info.initially_requested_service_fee_minor, + }, + + bill_coverage_in_percentage_opt, + } + } +} + struct AccountInfo { wallet: Wallet, initially_requested_service_fee_minor: u128, From f316ccef27d5acdf6079a1441cb9886c36d30633 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:30:25 +0100 Subject: [PATCH 229/250] GH-711-review-one: interim commit --- node/src/accountant/payment_adjuster/mod.rs | 18 +++++++++--------- .../payment_adjuster/non_unit_tests/mod.rs | 4 ++-- .../preparatory_analyser/mod.rs | 4 ++-- .../payable_scanner/agent_web3.rs | 4 ++-- .../blockchain_interface_web3/mod.rs | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index a28e2fa33..43ce82503 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -591,7 +591,7 @@ mod tests { use crate::accountant::{ AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, }; - use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; @@ -686,14 +686,14 @@ mod tests { // Service fee balance == payments let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - payable_account_balances_minor: vec![85, 15], + payable_account_balances_minor: vec![multiply_by_billion(85), multiply_by_billion(15)], cw_balance_minor: multiply_by_billion(100), }), None, ); let transaction_fee_balance_exactly_required_minor: u128 = { let base_value = (100 * 6 * 53_000) as u128; - let with_margin = TRANSACTION_FEE_MARGIN.add_percent_to(base_value); + let with_margin = TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_value); multiply_by_billion(with_margin) }; // Transaction fee balance > payments @@ -746,7 +746,7 @@ mod tests { gas_price_major: 100, number_of_accounts, tx_computation_units: 55_000, - cw_transaction_fee_balance_minor: TRANSACTION_FEE_MARGIN + cw_transaction_fee_balance_minor: TX_FEE_MARGIN_IN_PERCENT .add_percent_to(multiply_by_billion(100 * 3 * 55_000)) - 1, }), @@ -824,7 +824,7 @@ mod tests { let number_of_accounts = 3; let tx_fee_exactly_required_for_single_tx = { let base_minor = multiply_by_billion(55_000 * 100); - TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) + TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_minor) }; let cw_transaction_fee_balance_minor = tx_fee_exactly_required_for_single_tx - 1; let (qualified_payables, agent) = make_input_for_initial_check_tests( @@ -844,7 +844,7 @@ mod tests { let per_transaction_requirement_minor = { let base_minor = multiply_by_billion(55_000 * 100); - TRANSACTION_FEE_MARGIN.add_percent_to(base_minor) + TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_minor) }; assert_eq!( result, @@ -932,7 +932,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = - TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * multiply_by_billion(123)); + TX_FEE_MARGIN_IN_PERCENT.add_percent_to(55_000 * multiply_by_billion(123)); assert_eq!( result, Err( @@ -1196,7 +1196,7 @@ mod tests { let cw_service_fee_balance_minor = balance_2; let disqualification_arbiter = &subject.disqualification_arbiter; let agent_for_analysis = BlockchainAgentMock::default() - .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) + .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(12356); @@ -2275,7 +2275,7 @@ mod tests { multiply_by_billion((tx_computation_units * gas_price) as u128); let blockchain_agent = BlockchainAgentMock::default() - .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) + .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) .transaction_fee_balance_minor_result(cw_transaction_fee_minor.into()) .service_fee_balance_minor_result(cw_service_fee_balance_minor) .estimated_transaction_fee_per_transaction_minor_result( diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 267efe08f..79761905d 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -20,7 +20,7 @@ use crate::accountant::test_utils::{ make_single_qualified_payable_opt, try_to_make_guaranteed_qualified_payables, }; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; -use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; use crate::sub_lib::wallet::Wallet; @@ -492,7 +492,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) - .gas_price_margin_result(TRANSACTION_FEE_MARGIN.clone()) + .gas_price_margin_result(TX_FEE_MARGIN_IN_PERCENT.clone()) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 70652e984..9bbbfa159 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -385,7 +385,7 @@ mod tests { make_meaningless_analyzed_account, make_meaningless_qualified_payable, }; use crate::accountant::QualifiedPayableAccount; - use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; + use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; use itertools::Either; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; @@ -411,7 +411,7 @@ mod tests { DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; let blockchain_agent = BlockchainAgentMock::default() - .gas_price_margin_result(*TRANSACTION_FEE_MARGIN) + .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) .transaction_fee_balance_minor_result(U256::MAX) .estimated_transaction_fee_per_transaction_minor_result(123456) .service_fee_balance_minor_result(cw_service_fee_balance); diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs index d16423d13..dc7d33c64 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/agent_web3.rs @@ -6,7 +6,7 @@ use crate::sub_lib::blockchain_bridge::ConsumingWalletBalances; use crate::sub_lib::wallet::Wallet; use crate::accountant::gwei_to_wei; -use crate::blockchain::blockchain_interface::blockchain_interface_web3::TRANSACTION_FEE_MARGIN; +use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; use masq_lib::percentage::PurePercentage; use web3::types::U256; @@ -67,7 +67,7 @@ impl BlockchainAgentWeb3 { consuming_wallet_balances: ConsumingWalletBalances, pending_transaction_id: U256, ) -> Self { - let gas_price_margin = *TRANSACTION_FEE_MARGIN; + let gas_price_margin = *TX_FEE_MARGIN_IN_PERCENT; let maximum_added_gas_margin = WEB3_MAXIMAL_GAS_LIMIT_MARGIN; Self { gas_price_gwei, 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 18579e530..4887122b4 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -71,7 +71,7 @@ pub const REQUESTS_IN_PARALLEL: usize = 1; lazy_static! { // TODO In the future, we'll replace this by a dynamical value of the user's choice. - pub static ref TRANSACTION_FEE_MARGIN: PurePercentage = PurePercentage::try_from(15).expect("Value below 100 should cause no issue"); + pub static ref TX_FEE_MARGIN_IN_PERCENT: PurePercentage = PurePercentage::try_from(15).expect("Value below 100 should cause no issue"); } pub struct BlockchainInterfaceWeb3 @@ -612,7 +612,7 @@ mod tests { use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ - BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_FEE_MARGIN, + BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TX_FEE_MARGIN_IN_PERCENT, TRANSACTION_LITERAL, }; use crate::blockchain::blockchain_interface::test_utils::{ @@ -702,7 +702,7 @@ mod tests { assert_eq!(TRANSACTION_LITERAL, transaction_literal_expected); assert_eq!(REQUESTS_IN_PARALLEL, 1); assert_eq!( - *TRANSACTION_FEE_MARGIN, + *TX_FEE_MARGIN_IN_PERCENT, PurePercentage::try_from(15).unwrap() ); } From db2c2221f0d7d254610c8e9e62aecab394e2f681 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:30:10 +0100 Subject: [PATCH 230/250] GH-711: Now PA is immutable at outside --- .../balance_calculator.rs | 14 +- .../criterion_calculators/mod.rs | 6 +- node/src/accountant/payment_adjuster/inner.rs | 352 +++++++++++------- node/src/accountant/payment_adjuster/mod.rs | 151 +++----- .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../accountant/payment_adjuster/test_utils.rs | 20 +- node/src/accountant/scanners/mod.rs | 11 +- node/src/accountant/test_utils.rs | 2 +- .../blockchain_interface_web3/mod.rs | 4 +- 9 files changed, 293 insertions(+), 269 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index bd808301d..b812a5894 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -8,12 +8,8 @@ use crate::accountant::QualifiedPayableAccount; pub struct BalanceCriterionCalculator {} impl CriterionCalculator for BalanceCriterionCalculator { - fn calculate( - &self, - account: &QualifiedPayableAccount, - context: &dyn PaymentAdjusterInner, - ) -> u128 { - let largest = context.max_debt_above_threshold_in_qualified_payables(); + fn calculate(&self, account: &QualifiedPayableAccount, context: &PaymentAdjusterInner) -> u128 { + let largest = context.max_debt_above_threshold_in_qualified_payables_minor(); let this_account = account.bare_account.balance_wei - account.payment_threshold_intercept_minor; @@ -32,7 +28,7 @@ impl CriterionCalculator for BalanceCriterionCalculator { mod tests { use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; - use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiply_by_billion; use crate::accountant::test_utils::make_meaningless_analyzed_account; @@ -64,8 +60,8 @@ mod tests { }) .collect::>(); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); - let payment_adjuster_inner = - PaymentAdjusterInnerReal::new(now, None, 123456789, largest_exceeding_balance); + let payment_adjuster_inner = PaymentAdjusterInner::default(); + payment_adjuster_inner.initialize_guts(None, 123456789, largest_exceeding_balance, now); let subject = BalanceCriterionCalculator::default(); let computed_criteria = analyzed_accounts diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index 69853cde2..b28e7c1a5 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -7,11 +7,7 @@ use crate::accountant::QualifiedPayableAccount; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { - fn calculate( - &self, - account: &QualifiedPayableAccount, - context: &dyn PaymentAdjusterInner, - ) -> u128; + fn calculate(&self, account: &QualifiedPayableAccount, context: &PaymentAdjusterInner) -> u128; fn parameter_name(&self) -> &'static str; } diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 2dc3c8a4a..130c0b9c5 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,209 +1,301 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use std::cell::RefCell; use std::time::SystemTime; -pub trait PaymentAdjusterInner { - fn now(&self) -> SystemTime; - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128; - fn transaction_fee_count_limit_opt(&self) -> Option; - fn original_cw_service_fee_balance_minor(&self) -> u128; - fn unallocated_cw_service_fee_balance_minor(&self) -> u128; - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128); +pub struct PaymentAdjusterInner { + initialized_guts_opt: RefCell>, } -pub struct PaymentAdjusterInnerReal { +impl Default for PaymentAdjusterInner { + fn default() -> Self { + PaymentAdjusterInner { + initialized_guts_opt: RefCell::new(None), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct GutsOfPaymentAdjusterInner { now: SystemTime, - transaction_fee_count_limit_opt: Option, - max_debt_above_threshold_in_qualified_payables: u128, + transaction_count_limit_opt: Option, + max_debt_above_threshold_in_qualified_payables_minor: u128, original_cw_service_fee_balance_minor: u128, unallocated_cw_service_fee_balance_minor: u128, } -impl PaymentAdjusterInnerReal { +impl GutsOfPaymentAdjusterInner { pub fn new( now: SystemTime, - transaction_fee_count_limit_opt: Option, + transaction_count_limit_opt: Option, cw_service_fee_balance_minor: u128, - max_debt_above_threshold_in_qualified_payables: u128, + max_debt_above_threshold_in_qualified_payables_minor: u128, ) -> Self { Self { now, - transaction_fee_count_limit_opt, - max_debt_above_threshold_in_qualified_payables, + transaction_count_limit_opt, + max_debt_above_threshold_in_qualified_payables_minor, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } } } -impl PaymentAdjusterInner for PaymentAdjusterInnerReal { - fn now(&self) -> SystemTime { - self.now +impl PaymentAdjusterInner { + pub fn now(&self) -> SystemTime { + self.get_value("now", |guts_ref| guts_ref.now) } - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { - self.max_debt_above_threshold_in_qualified_payables + pub fn initialize_guts( + &self, + tx_count_limit_opt: Option, + cw_service_fee_balance: u128, + max_debt_above_threshold_in_qualified_payables_minor: u128, + now: SystemTime, + ) { + let initialized_guts = GutsOfPaymentAdjusterInner::new( + now, + tx_count_limit_opt, + cw_service_fee_balance, + max_debt_above_threshold_in_qualified_payables_minor, + ); + + self.initialized_guts_opt + .borrow_mut() + .replace(initialized_guts); } - fn transaction_fee_count_limit_opt(&self) -> Option { - self.transaction_fee_count_limit_opt + pub fn max_debt_above_threshold_in_qualified_payables_minor(&self) -> u128 { + self.get_value( + "max_debt_above_threshold_in_qualified_payables_minor", + |guts_ref| guts_ref.max_debt_above_threshold_in_qualified_payables_minor, + ) } - fn original_cw_service_fee_balance_minor(&self) -> u128 { - self.original_cw_service_fee_balance_minor + + pub fn transaction_count_limit_opt(&self) -> Option { + self.get_value("transaction_count_limit_opt", |guts_ref| { + guts_ref.transaction_count_limit_opt + }) } - fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { - self.unallocated_cw_service_fee_balance_minor + pub fn original_cw_service_fee_balance_minor(&self) -> u128 { + self.get_value("original_cw_service_fee_balance_minor", |guts_ref| { + guts_ref.original_cw_service_fee_balance_minor + }) } - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128) { - let updated_thought_cw_balance = self - .unallocated_cw_service_fee_balance_minor - .checked_sub(subtrahend) - .expect("should never go beyond zero"); - self.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance + pub fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { + self.get_value("unallocated_cw_service_fee_balance_minor", |guts_ref| { + guts_ref.unallocated_cw_service_fee_balance_minor + }) } -} - -#[derive(Default)] -pub struct PaymentAdjusterInnerNull {} - -impl PaymentAdjusterInnerNull { - fn panicking_operation(operation: &str) -> ! { - panic!( - "The PaymentAdjuster Inner is uninitialised. It was detected while executing {}", - operation + pub fn subtract_from_unallocated_cw_service_fee_balance_minor(&self, subtrahend: u128) { + let updated_thought_cw_balance = self.get_value( + "subtract_from_unallocated_cw_service_fee_balance_minor", + |guts_ref| { + guts_ref + .unallocated_cw_service_fee_balance_minor + .checked_sub(subtrahend) + .expect("should never go beyond zero") + }, + ); + self.set_value( + "subtract_from_unallocated_cw_service_fee_balance_minor", + |guts_mut| { + guts_mut.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance + }, ) } -} -impl PaymentAdjusterInner for PaymentAdjusterInnerNull { - fn now(&self) -> SystemTime { - PaymentAdjusterInnerNull::panicking_operation("now()") + pub fn invalidate_guts(&self) { + self.initialized_guts_opt.replace(None); } - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation( - "max_debt_above_threshold_in_qualified_payables()", - ) - } + fn get_value(&self, method: &str, getter: F) -> T + where + F: FnOnce(&GutsOfPaymentAdjusterInner) -> T, + { + let guts_borrowed_opt = self.initialized_guts_opt.borrow(); - fn transaction_fee_count_limit_opt(&self) -> Option { - PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") - } - fn original_cw_service_fee_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("original_cw_service_fee_balance_minor()") + let guts_ref = guts_borrowed_opt + .as_ref() + .unwrap_or_else(|| Self::uninitialized_panic(method)); + + getter(guts_ref) } - fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_service_fee_balance_minor()") + + fn set_value(&self, method: &str, mut setter: F) + where + F: FnMut(&mut GutsOfPaymentAdjusterInner), + { + let mut guts_borrowed_mut_opt = self.initialized_guts_opt.borrow_mut(); + + let guts_mut = guts_borrowed_mut_opt + .as_mut() + .unwrap_or_else(|| Self::uninitialized_panic(method)); + + setter(guts_mut) } - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation( - "subtract_from_unallocated_cw_service_fee_balance_minor()", - ) + + fn uninitialized_panic(method: &str) -> ! { + panic!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method}()'") } } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + GutsOfPaymentAdjusterInner, PaymentAdjusterInner, }; + use std::panic::{catch_unwind, AssertUnwindSafe}; use std::time::SystemTime; #[test] - fn inner_real_is_constructed_correctly() { + fn defaulted_payment_adjuster_inner() { + let subject = PaymentAdjusterInner::default(); + + let guts_is_none = subject.initialized_guts_opt.borrow().is_none(); + assert_eq!(guts_is_none, true) + } + + #[test] + fn initialization_and_getters_of_payment_adjuster_inner_work() { + let subject = PaymentAdjusterInner::default(); let now = SystemTime::now(); - let transaction_fee_count_limit_opt = Some(3); + let tx_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; - let max_debt_above_threshold_in_qualified_payables = 44_555_666; - let result = PaymentAdjusterInnerReal::new( - now, - transaction_fee_count_limit_opt, + let max_debt_above_threshold_in_qualified_payables_minor = 44_555_666; + + subject.initialize_guts( + tx_count_limit_opt, cw_service_fee_balance, - max_debt_above_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables_minor, + now, ); + let read_now = subject.now(); + let read_max_debt_above_threshold_in_qualified_payables_minor = + subject.max_debt_above_threshold_in_qualified_payables_minor(); + let read_tx_count_limit_opt = subject.transaction_count_limit_opt(); + let read_original_cw_service_fee_balance_minor = + subject.original_cw_service_fee_balance_minor(); + let read_unallocated_cw_service_fee_balance_minor = + subject.unallocated_cw_service_fee_balance_minor(); - assert_eq!(result.now, now); + assert_eq!(read_now, now); assert_eq!( - result.transaction_fee_count_limit_opt, - transaction_fee_count_limit_opt + read_max_debt_above_threshold_in_qualified_payables_minor, + max_debt_above_threshold_in_qualified_payables_minor ); + assert_eq!(read_tx_count_limit_opt, tx_count_limit_opt); assert_eq!( - result.original_cw_service_fee_balance_minor, + read_original_cw_service_fee_balance_minor, cw_service_fee_balance ); assert_eq!( - result.unallocated_cw_service_fee_balance_minor, + read_unallocated_cw_service_fee_balance_minor, cw_service_fee_balance ); - assert_eq!( - result.max_debt_above_threshold_in_qualified_payables, - max_debt_above_threshold_in_qualified_payables - ) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - now()" - )] - fn inner_null_calling_now() { - let subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.now(); - } + fn reducing_unallocated_cw_service_fee_balance_works() { + let initial_cw_service_fee_balance_minor = 123_123_678_678; + let subject = PaymentAdjusterInner::default(); + subject.initialize_guts( + None, + initial_cw_service_fee_balance_minor, + 12345, + SystemTime::now(), + ); + let amount_to_subtract = 555_666_777; - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - max_debt_above_threshold_in_qualified_payables()" - )] - fn inner_null_calling_max_debt_above_threshold_in_qualified_payables() { - let subject = PaymentAdjusterInnerNull::default(); + subject.subtract_from_unallocated_cw_service_fee_balance_minor(amount_to_subtract); - let _ = subject.max_debt_above_threshold_in_qualified_payables(); + let unallocated_cw_service_fee_balance_minor = + subject.unallocated_cw_service_fee_balance_minor(); + assert_eq!( + unallocated_cw_service_fee_balance_minor, + initial_cw_service_fee_balance_minor - amount_to_subtract + ) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - transaction_fee_count_limit_opt()" - )] - fn inner_null_calling_transaction_fee_count_limit_opt() { - let subject = PaymentAdjusterInnerNull::default(); + fn inner_can_be_invalidated_by_removing_its_guts() { + let subject = PaymentAdjusterInner::default(); + subject + .initialized_guts_opt + .replace(Some(GutsOfPaymentAdjusterInner { + now: SystemTime::now(), + transaction_count_limit_opt: None, + max_debt_above_threshold_in_qualified_payables_minor: 0, + original_cw_service_fee_balance_minor: 0, + unallocated_cw_service_fee_balance_minor: 0, + })); - let _ = subject.transaction_fee_count_limit_opt(); - } - - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - original_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_original_cw_service_fee_balance_minor() { - let subject = PaymentAdjusterInnerNull::default(); + subject.invalidate_guts(); - let _ = subject.original_cw_service_fee_balance_minor(); + let guts_removed = subject.initialized_guts_opt.borrow().is_none(); + assert_eq!(guts_removed, true) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - unallocated_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_unallocated_cw_balance() { - let subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.unallocated_cw_service_fee_balance_minor(); + fn reasonable_panics_about_lacking_initialization_for_respective_methods() { + let uninitialized_subject = PaymentAdjusterInner::default(); + test_properly_implemented_panic( + &uninitialized_subject, + "now", + Box::new(|subject| { + subject.now(); + }), + ); + test_properly_implemented_panic( + &uninitialized_subject, + "max_debt_above_threshold_in_qualified_payables_minor", + Box::new(|subject| { + subject.max_debt_above_threshold_in_qualified_payables_minor(); + }), + ); + test_properly_implemented_panic( + &uninitialized_subject, + "transaction_count_limit_opt", + Box::new(|subject| { + subject.transaction_count_limit_opt(); + }), + ); + test_properly_implemented_panic( + &uninitialized_subject, + "original_cw_service_fee_balance_minor", + Box::new(|subject| { + subject.original_cw_service_fee_balance_minor(); + }), + ); + test_properly_implemented_panic( + &uninitialized_subject, + "unallocated_cw_service_fee_balance_minor", + Box::new(|subject| { + subject.unallocated_cw_service_fee_balance_minor(); + }), + ); + test_properly_implemented_panic( + &uninitialized_subject, + "subtract_from_unallocated_cw_service_fee_balance_minor", + Box::new(|subject| { + subject.subtract_from_unallocated_cw_service_fee_balance_minor(123456); + }), + ) } - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - subtract_from_unallocated_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_subtract_from_unallocated_cw_service_fee_balance_minor() { - let mut subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.subtract_from_unallocated_cw_service_fee_balance_minor(123); + fn test_properly_implemented_panic( + subject: &PaymentAdjusterInner, + method_name: &str, + call_panicking_method: Box, + ) { + let caught_panic = + catch_unwind(AssertUnwindSafe(|| call_panicking_method(subject))).unwrap_err(); + let actual_panic_msg = caught_panic.downcast_ref::().unwrap().to_owned(); + let expected_msg = format!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method_name}()'"); + assert_eq!( + actual_panic_msg, expected_msg, + "We expected this panic message: {}, but the panic looked different: {}", + expected_msg, actual_panic_msg + ) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 43ce82503..48b91c250 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -12,7 +12,6 @@ mod preparatory_analyser; mod service_fee_adjuster; #[cfg(test)] mod test_utils; - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; @@ -22,7 +21,7 @@ use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, }; use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + PaymentAdjusterInner, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, @@ -77,7 +76,7 @@ pub trait PaymentAdjuster { ) -> AdjustmentAnalysisResult; fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result; @@ -88,7 +87,7 @@ pub struct PaymentAdjusterReal { disqualification_arbiter: DisqualificationArbiter, service_fee_adjuster: Box, calculators: Vec>, - inner: Box, + inner: PaymentAdjusterInner, logger: Logger, } @@ -106,7 +105,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { } fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result { @@ -115,13 +114,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment_analysis.adjustment; - let max_debt_above_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); self.initialize_inner( - initial_service_fee_balance_minor, required_adjustment, - max_debt_above_threshold_in_qualified_payables, + initial_service_fee_balance_minor, + max_debt_above_threshold_in_qualified_payables_minor, now, ); @@ -131,7 +130,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.complete_debug_log_if_enabled(sketched_debug_log_opt, &affordable_accounts); - self.reset_inner(); + self.inner.invalidate_guts(); Ok(OutboundPaymentsInstructions::new( Either::Right(affordable_accounts), @@ -154,16 +153,17 @@ impl PaymentAdjusterReal { disqualification_arbiter: DisqualificationArbiter::default(), service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), calculators: vec![Box::new(BalanceCriterionCalculator::default())], - inner: Box::new(PaymentAdjusterInnerNull::default()), + inner: PaymentAdjusterInner::default(), logger: Logger::new("PaymentAdjuster"), } } fn initialize_inner( - &mut self, - cw_service_fee_balance: u128, + &self, required_adjustment: Adjustment, - max_debt_above_threshold_in_qualified_payables: u128, + initial_service_fee_balance_minor: u128, + //TODO use 'of qualified payables' instead of 'in' + max_debt_above_threshold_in_qualified_payables_minor_minor: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { @@ -173,22 +173,16 @@ impl PaymentAdjusterReal { Adjustment::ByServiceFee => None, }; - let inner = PaymentAdjusterInnerReal::new( - now, + self.inner.initialize_guts( transaction_fee_limitation_opt, - cw_service_fee_balance, - max_debt_above_threshold_in_qualified_payables, - ); - - self.inner = Box::new(inner); - } - - fn reset_inner(&mut self) { - self.inner = Box::new(PaymentAdjusterInnerNull::default()) + initial_service_fee_balance_minor, + max_debt_above_threshold_in_qualified_payables_minor_minor, + now, + ) } fn run_adjustment( - &mut self, + &self, analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { let weighed_accounts = self.calculate_weights(analyzed_accounts); @@ -213,13 +207,13 @@ impl PaymentAdjusterReal { } fn resolve_initial_adjustment_dispatch( - &mut self, + &self, weighed_payables: Vec, ) -> Result< Either, Vec>, PaymentAdjusterError, > { - if let Some(limit) = self.inner.transaction_fee_count_limit_opt() { + if let Some(limit) = self.inner.transaction_count_limit_opt() { return self.begin_with_adjustment_by_transaction_fee(weighed_payables, limit); } @@ -229,7 +223,7 @@ impl PaymentAdjusterReal { } fn begin_with_adjustment_by_transaction_fee( - &mut self, + &self, weighed_accounts: Vec, transaction_count_limit: u16, ) -> Result< @@ -269,7 +263,7 @@ impl PaymentAdjusterReal { } fn propose_possible_adjustment_recursively( - &mut self, + &self, weighed_accounts: Vec, ) -> Vec { diagnostics!( @@ -357,8 +351,8 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = criterion_calculator - .calculate(&payable.qualified_as, self.inner.as_ref()); + let new_criterion = + criterion_calculator.calculate(&payable.qualified_as, &self.inner); let summed_up = weight + new_criterion; @@ -378,7 +372,7 @@ impl PaymentAdjusterReal { } fn adjust_remaining_unallocated_cw_balance_down( - &mut self, + &self, decided_accounts: &[AdjustedAccountBeforeFinalization], ) { let subtrahend_total: u128 = sum_as(decided_accounts, |account| { @@ -561,7 +555,7 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, WeighedPayable, @@ -618,54 +612,6 @@ mod tests { let _ = subject.inner.unallocated_cw_service_fee_balance_minor(); } - fn test_initialize_inner_works( - required_adjustment: Adjustment, - expected_tx_fee_limit_opt_result: Option, - ) { - let mut subject = PaymentAdjusterReal::default(); - let cw_service_fee_balance = 111_222_333_444; - let max_debt_above_threshold_in_qualified_payables = 3_555_666; - let now = SystemTime::now(); - - subject.initialize_inner( - cw_service_fee_balance, - required_adjustment, - max_debt_above_threshold_in_qualified_payables, - now, - ); - - assert_eq!(subject.inner.now(), now); - assert_eq!( - subject.inner.transaction_fee_count_limit_opt(), - expected_tx_fee_limit_opt_result - ); - assert_eq!( - subject.inner.original_cw_service_fee_balance_minor(), - cw_service_fee_balance - ); - assert_eq!( - subject.inner.unallocated_cw_service_fee_balance_minor(), - cw_service_fee_balance - ); - assert_eq!( - subject - .inner - .max_debt_above_threshold_in_qualified_payables(), - max_debt_above_threshold_in_qualified_payables - ) - } - - #[test] - fn initialize_inner_works() { - test_initialize_inner_works(Adjustment::ByServiceFee, None); - test_initialize_inner_works( - Adjustment::BeginByTransactionFee { - transaction_count_limit: 5, - }, - Some(5), - ); - } - #[test] fn consider_adjustment_happy_path() { init_test_logging(); @@ -686,7 +632,10 @@ mod tests { // Service fee balance == payments let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - payable_account_balances_minor: vec![multiply_by_billion(85), multiply_by_billion(15)], + payable_account_balances_minor: vec![ + multiply_by_billion(85), + multiply_by_billion(15), + ], cw_balance_minor: multiply_by_billion(100), }), None, @@ -1102,9 +1051,9 @@ mod tests { let largest_exceeding_balance = (balance_1 - account_1.qualified_as.payment_threshold_intercept_minor) .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .cw_service_fee_balance_minor(cw_service_fee_balance_minor) - .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) + .max_debt_above_threshold_in_qualified_payables_minor(largest_exceeding_balance) .build(); let weighed_payables = vec![ WeighedPayable::new(account_1, weight_account_1), @@ -1307,7 +1256,7 @@ mod tests { let sum_of_disqualification_limits = sum_as(&analyzed_accounts, |account| { account.disqualification_limit_minor }); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1425,10 +1374,10 @@ mod tests { untaken_cw_service_fee_balance_minor: u128, expected_result: bool, ) { - let mut subject = PaymentAdjusterReal::new(); + let subject = PaymentAdjusterReal::new(); subject.initialize_inner( - untaken_cw_service_fee_balance_minor, Adjustment::ByServiceFee, + untaken_cw_service_fee_balance_minor, 1234567, SystemTime::now(), ); @@ -1533,7 +1482,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1645,7 +1594,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1738,7 +1687,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .build(); @@ -1827,7 +1776,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1929,7 +1878,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -2303,8 +2252,8 @@ mod tests { let panic_msg = err.downcast_ref::().unwrap(); assert_eq!( panic_msg, - "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - original_cw_service_fee_balance_minor()" + "PaymentAdjusterInner is uninitialized. It was detected while calling method + 'original_cw_service_fee_balance_minor'" ) } @@ -2328,12 +2277,8 @@ mod tests { let cw_service_fee_balance_minor = multiply_by_billion(3_000); let exceeding_balance = qualified_payable.bare_account.balance_wei - qualified_payable.payment_threshold_intercept_minor; - let context = PaymentAdjusterInnerReal::new( - now, - None, - cw_service_fee_balance_minor, - exceeding_balance, - ); + let context = PaymentAdjusterInner::default(); + context.initialize_guts(None, cw_service_fee_balance_minor, exceeding_balance, now); payment_adjuster .calculators @@ -2506,13 +2451,13 @@ mod tests { cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = convert_collection(qualified_payables); - let max_debt_above_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() .now(now) .cw_service_fee_balance_minor(cw_service_fee_balance_minor) - .max_debt_above_threshold_in_qualified_payables( - max_debt_above_threshold_in_qualified_payables, + .max_debt_above_threshold_in_qualified_payables_minor( + max_debt_above_threshold_in_qualified_payables_minor, ) .build(); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 79761905d..da2480e88 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -113,7 +113,7 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); - let mut subject = PaymentAdjusterReal::new(); + let subject = PaymentAdjusterReal::new(); let number_of_requested_scenarios = 2000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 6e37529d2..37118b573 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalcula use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, }; -use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; +use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, UnconfirmedAdjustment, WeighedPayable, }; @@ -39,7 +39,7 @@ pub struct PaymentAdjusterBuilder { now_opt: Option, cw_service_fee_balance_minor_opt: Option, mock_replacing_calculators_opt: Option, - max_debt_above_threshold_in_qualified_payables_opt: Option, + max_debt_above_threshold_in_qualified_payables_minor_opt: Option, transaction_limit_count_opt: Option, logger_opt: Option, } @@ -50,14 +50,13 @@ impl PaymentAdjusterBuilder { let logger = self.logger_opt.unwrap_or(Logger::new("test")); payment_adjuster.logger = logger; if !self.start_with_inner_null { - let inner = Box::new(PaymentAdjusterInnerReal::new( - self.now_opt.unwrap_or(SystemTime::now()), + payment_adjuster.inner.initialize_guts( self.transaction_limit_count_opt, self.cw_service_fee_balance_minor_opt.unwrap_or(0), - self.max_debt_above_threshold_in_qualified_payables_opt + self.max_debt_above_threshold_in_qualified_payables_minor_opt .unwrap_or(0), - )); - payment_adjuster.inner = inner; + self.now_opt.unwrap_or(SystemTime::now()), + ); } if let Some(calculator) = self.mock_replacing_calculators_opt { payment_adjuster.calculators = vec![Box::new(calculator)] @@ -83,11 +82,12 @@ impl PaymentAdjusterBuilder { self } - pub fn max_debt_above_threshold_in_qualified_payables( + pub fn max_debt_above_threshold_in_qualified_payables_minor( mut self, max_exceeding_part_of_debt: u128, ) -> Self { - self.max_debt_above_threshold_in_qualified_payables_opt = Some(max_exceeding_part_of_debt); + self.max_debt_above_threshold_in_qualified_payables_minor_opt = + Some(max_exceeding_part_of_debt); self } @@ -170,7 +170,7 @@ impl CriterionCalculator for CriterionCalculatorMock { fn calculate( &self, account: &QualifiedPayableAccount, - _context: &dyn PaymentAdjusterInner, + _context: &PaymentAdjusterInner, ) -> u128 { self.calculate_params.lock().unwrap().push(account.clone()); self.calculate_results.borrow_mut().remove(0) diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 2d30d293b..f83414bbd 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -190,7 +190,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_inspector: PayableInspector, - pub payment_adjuster: RefCell>, + pub payment_adjuster: Box, } impl Scanner for PayableScanner { @@ -274,7 +274,6 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { match self .payment_adjuster - .borrow() .consider_adjustment(unprotected, &*msg.agent) { Ok(processed) => { @@ -315,11 +314,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { logger: &Logger, ) -> Option { let now = SystemTime::now(); - match self - .payment_adjuster - .borrow_mut() - .adjust_payments(setup, now) - { + match self.payment_adjuster.adjust_payments(setup, now) { Ok(instructions) => Some(instructions), Err(e) => { warning!( @@ -358,7 +353,7 @@ impl PayableScanner { payable_dao, pending_payable_dao, payable_inspector, - payment_adjuster: RefCell::new(payment_adjuster), + payment_adjuster, } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 4e5ab644f..5db6ac84d 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1488,7 +1488,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { } fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result { 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 4887122b4..580e3d606 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -612,8 +612,8 @@ mod tests { use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ - BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TX_FEE_MARGIN_IN_PERCENT, - TRANSACTION_LITERAL, + BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, + TX_FEE_MARGIN_IN_PERCENT, }; use crate::blockchain::blockchain_interface::test_utils::{ test_blockchain_interface_is_connected_and_functioning, LowBlockchainIntMock, From bd1e6781b3c5df0822f27b2215506e8ef6b186ce Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:30:10 +0100 Subject: [PATCH 231/250] GH-711: Now PA is immutable at outside --- .../balance_calculator.rs | 14 +- .../criterion_calculators/mod.rs | 6 +- node/src/accountant/payment_adjuster/inner.rs | 348 +++++++++++------- node/src/accountant/payment_adjuster/mod.rs | 151 +++----- .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../accountant/payment_adjuster/test_utils.rs | 20 +- node/src/accountant/scanners/mod.rs | 11 +- node/src/accountant/test_utils.rs | 2 +- .../blockchain_interface_web3/mod.rs | 4 +- 9 files changed, 289 insertions(+), 269 deletions(-) diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index bd808301d..b812a5894 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -8,12 +8,8 @@ use crate::accountant::QualifiedPayableAccount; pub struct BalanceCriterionCalculator {} impl CriterionCalculator for BalanceCriterionCalculator { - fn calculate( - &self, - account: &QualifiedPayableAccount, - context: &dyn PaymentAdjusterInner, - ) -> u128 { - let largest = context.max_debt_above_threshold_in_qualified_payables(); + fn calculate(&self, account: &QualifiedPayableAccount, context: &PaymentAdjusterInner) -> u128 { + let largest = context.max_debt_above_threshold_in_qualified_payables_minor(); let this_account = account.bare_account.balance_wei - account.payment_threshold_intercept_minor; @@ -32,7 +28,7 @@ impl CriterionCalculator for BalanceCriterionCalculator { mod tests { use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; - use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::multiply_by_billion; use crate::accountant::test_utils::make_meaningless_analyzed_account; @@ -64,8 +60,8 @@ mod tests { }) .collect::>(); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); - let payment_adjuster_inner = - PaymentAdjusterInnerReal::new(now, None, 123456789, largest_exceeding_balance); + let payment_adjuster_inner = PaymentAdjusterInner::default(); + payment_adjuster_inner.initialize_guts(None, 123456789, largest_exceeding_balance, now); let subject = BalanceCriterionCalculator::default(); let computed_criteria = analyzed_accounts diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs index 69853cde2..b28e7c1a5 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/mod.rs @@ -7,11 +7,7 @@ use crate::accountant::QualifiedPayableAccount; // Caution: always remember to use checked math operations in the criteria formulas! pub trait CriterionCalculator { - fn calculate( - &self, - account: &QualifiedPayableAccount, - context: &dyn PaymentAdjusterInner, - ) -> u128; + fn calculate(&self, account: &QualifiedPayableAccount, context: &PaymentAdjusterInner) -> u128; fn parameter_name(&self) -> &'static str; } diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 2dc3c8a4a..d1fba592b 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,209 +1,297 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use std::cell::RefCell; use std::time::SystemTime; -pub trait PaymentAdjusterInner { - fn now(&self) -> SystemTime; - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128; - fn transaction_fee_count_limit_opt(&self) -> Option; - fn original_cw_service_fee_balance_minor(&self) -> u128; - fn unallocated_cw_service_fee_balance_minor(&self) -> u128; - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128); +pub struct PaymentAdjusterInner { + initialized_guts_opt: RefCell>, } -pub struct PaymentAdjusterInnerReal { +impl Default for PaymentAdjusterInner { + fn default() -> Self { + PaymentAdjusterInner { + initialized_guts_opt: RefCell::new(None), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct GutsOfPaymentAdjusterInner { now: SystemTime, - transaction_fee_count_limit_opt: Option, - max_debt_above_threshold_in_qualified_payables: u128, + transaction_count_limit_opt: Option, + max_debt_above_threshold_in_qualified_payables_minor: u128, original_cw_service_fee_balance_minor: u128, unallocated_cw_service_fee_balance_minor: u128, } -impl PaymentAdjusterInnerReal { +impl GutsOfPaymentAdjusterInner { pub fn new( now: SystemTime, - transaction_fee_count_limit_opt: Option, + transaction_count_limit_opt: Option, cw_service_fee_balance_minor: u128, - max_debt_above_threshold_in_qualified_payables: u128, + max_debt_above_threshold_in_qualified_payables_minor: u128, ) -> Self { Self { now, - transaction_fee_count_limit_opt, - max_debt_above_threshold_in_qualified_payables, + transaction_count_limit_opt, + max_debt_above_threshold_in_qualified_payables_minor, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } } } -impl PaymentAdjusterInner for PaymentAdjusterInnerReal { - fn now(&self) -> SystemTime { - self.now +impl PaymentAdjusterInner { + pub fn now(&self) -> SystemTime { + self.get_value("now", |guts_ref| guts_ref.now) } - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { - self.max_debt_above_threshold_in_qualified_payables + pub fn initialize_guts( + &self, + tx_count_limit_opt: Option, + cw_service_fee_balance: u128, + max_debt_above_threshold_in_qualified_payables_minor: u128, + now: SystemTime, + ) { + let initialized_guts = GutsOfPaymentAdjusterInner::new( + now, + tx_count_limit_opt, + cw_service_fee_balance, + max_debt_above_threshold_in_qualified_payables_minor, + ); + + self.initialized_guts_opt + .borrow_mut() + .replace(initialized_guts); } - fn transaction_fee_count_limit_opt(&self) -> Option { - self.transaction_fee_count_limit_opt + pub fn max_debt_above_threshold_in_qualified_payables_minor(&self) -> u128 { + self.get_value( + "max_debt_above_threshold_in_qualified_payables_minor", + |guts_ref| guts_ref.max_debt_above_threshold_in_qualified_payables_minor, + ) } - fn original_cw_service_fee_balance_minor(&self) -> u128 { - self.original_cw_service_fee_balance_minor + + pub fn transaction_count_limit_opt(&self) -> Option { + self.get_value("transaction_count_limit_opt", |guts_ref| { + guts_ref.transaction_count_limit_opt + }) } - fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { - self.unallocated_cw_service_fee_balance_minor + pub fn original_cw_service_fee_balance_minor(&self) -> u128 { + self.get_value("original_cw_service_fee_balance_minor", |guts_ref| { + guts_ref.original_cw_service_fee_balance_minor + }) } - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, subtrahend: u128) { - let updated_thought_cw_balance = self - .unallocated_cw_service_fee_balance_minor - .checked_sub(subtrahend) - .expect("should never go beyond zero"); - self.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance + pub fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { + self.get_value("unallocated_cw_service_fee_balance_minor", |guts_ref| { + guts_ref.unallocated_cw_service_fee_balance_minor + }) } -} - -#[derive(Default)] -pub struct PaymentAdjusterInnerNull {} - -impl PaymentAdjusterInnerNull { - fn panicking_operation(operation: &str) -> ! { - panic!( - "The PaymentAdjuster Inner is uninitialised. It was detected while executing {}", - operation + pub fn subtract_from_unallocated_cw_service_fee_balance_minor(&self, subtrahend: u128) { + let updated_thought_cw_balance = self.get_value( + "subtract_from_unallocated_cw_service_fee_balance_minor", + |guts_ref| { + guts_ref + .unallocated_cw_service_fee_balance_minor + .checked_sub(subtrahend) + .expect("should never go beyond zero") + }, + ); + self.set_value( + "subtract_from_unallocated_cw_service_fee_balance_minor", + |guts_mut| { + guts_mut.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance + }, ) } -} -impl PaymentAdjusterInner for PaymentAdjusterInnerNull { - fn now(&self) -> SystemTime { - PaymentAdjusterInnerNull::panicking_operation("now()") + pub fn invalidate_guts(&self) { + self.initialized_guts_opt.replace(None); } - fn max_debt_above_threshold_in_qualified_payables(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation( - "max_debt_above_threshold_in_qualified_payables()", - ) - } + fn get_value(&self, method: &str, getter: F) -> T + where + F: FnOnce(&GutsOfPaymentAdjusterInner) -> T, + { + let guts_borrowed_opt = self.initialized_guts_opt.borrow(); - fn transaction_fee_count_limit_opt(&self) -> Option { - PaymentAdjusterInnerNull::panicking_operation("transaction_fee_count_limit_opt()") - } - fn original_cw_service_fee_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("original_cw_service_fee_balance_minor()") + let guts_ref = guts_borrowed_opt + .as_ref() + .unwrap_or_else(|| Self::uninitialized_panic(method)); + + getter(guts_ref) } - fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { - PaymentAdjusterInnerNull::panicking_operation("unallocated_cw_service_fee_balance_minor()") + + fn set_value(&self, method: &str, mut setter: F) + where + F: FnMut(&mut GutsOfPaymentAdjusterInner), + { + let mut guts_borrowed_mut_opt = self.initialized_guts_opt.borrow_mut(); + + let guts_mut = guts_borrowed_mut_opt + .as_mut() + .unwrap_or_else(|| Self::uninitialized_panic(method)); + + setter(guts_mut) } - fn subtract_from_unallocated_cw_service_fee_balance_minor(&mut self, _subtrahend: u128) { - PaymentAdjusterInnerNull::panicking_operation( - "subtract_from_unallocated_cw_service_fee_balance_minor()", - ) + + fn uninitialized_panic(method: &str) -> ! { + panic!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method}()'") } } #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + GutsOfPaymentAdjusterInner, PaymentAdjusterInner, }; + use std::panic::{catch_unwind, AssertUnwindSafe}; use std::time::SystemTime; #[test] - fn inner_real_is_constructed_correctly() { + fn defaulted_payment_adjuster_inner() { + let subject = PaymentAdjusterInner::default(); + + let guts_is_none = subject.initialized_guts_opt.borrow().is_none(); + assert_eq!(guts_is_none, true) + } + + #[test] + fn initialization_and_getters_of_payment_adjuster_inner_work() { + let subject = PaymentAdjusterInner::default(); let now = SystemTime::now(); - let transaction_fee_count_limit_opt = Some(3); + let tx_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; - let max_debt_above_threshold_in_qualified_payables = 44_555_666; - let result = PaymentAdjusterInnerReal::new( - now, - transaction_fee_count_limit_opt, + let max_debt_above_threshold_in_qualified_payables_minor = 44_555_666; + + subject.initialize_guts( + tx_count_limit_opt, cw_service_fee_balance, - max_debt_above_threshold_in_qualified_payables, + max_debt_above_threshold_in_qualified_payables_minor, + now, ); + let read_now = subject.now(); + let read_max_debt_above_threshold_in_qualified_payables_minor = + subject.max_debt_above_threshold_in_qualified_payables_minor(); + let read_tx_count_limit_opt = subject.transaction_count_limit_opt(); + let read_original_cw_service_fee_balance_minor = + subject.original_cw_service_fee_balance_minor(); + let read_unallocated_cw_service_fee_balance_minor = + subject.unallocated_cw_service_fee_balance_minor(); - assert_eq!(result.now, now); + assert_eq!(read_now, now); assert_eq!( - result.transaction_fee_count_limit_opt, - transaction_fee_count_limit_opt + read_max_debt_above_threshold_in_qualified_payables_minor, + max_debt_above_threshold_in_qualified_payables_minor ); + assert_eq!(read_tx_count_limit_opt, tx_count_limit_opt); assert_eq!( - result.original_cw_service_fee_balance_minor, + read_original_cw_service_fee_balance_minor, cw_service_fee_balance ); assert_eq!( - result.unallocated_cw_service_fee_balance_minor, + read_unallocated_cw_service_fee_balance_minor, cw_service_fee_balance ); - assert_eq!( - result.max_debt_above_threshold_in_qualified_payables, - max_debt_above_threshold_in_qualified_payables - ) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - now()" - )] - fn inner_null_calling_now() { - let subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.now(); - } + fn reducing_unallocated_cw_service_fee_balance_works() { + let initial_cw_service_fee_balance_minor = 123_123_678_678; + let subject = PaymentAdjusterInner::default(); + subject.initialize_guts( + None, + initial_cw_service_fee_balance_minor, + 12345, + SystemTime::now(), + ); + let amount_to_subtract = 555_666_777; - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - max_debt_above_threshold_in_qualified_payables()" - )] - fn inner_null_calling_max_debt_above_threshold_in_qualified_payables() { - let subject = PaymentAdjusterInnerNull::default(); + subject.subtract_from_unallocated_cw_service_fee_balance_minor(amount_to_subtract); - let _ = subject.max_debt_above_threshold_in_qualified_payables(); + let unallocated_cw_service_fee_balance_minor = + subject.unallocated_cw_service_fee_balance_minor(); + assert_eq!( + unallocated_cw_service_fee_balance_minor, + initial_cw_service_fee_balance_minor - amount_to_subtract + ) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - transaction_fee_count_limit_opt()" - )] - fn inner_null_calling_transaction_fee_count_limit_opt() { - let subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.transaction_fee_count_limit_opt(); - } + fn inner_can_be_invalidated_by_removing_its_guts() { + let subject = PaymentAdjusterInner::default(); + subject + .initialized_guts_opt + .replace(Some(GutsOfPaymentAdjusterInner { + now: SystemTime::now(), + transaction_count_limit_opt: None, + max_debt_above_threshold_in_qualified_payables_minor: 0, + original_cw_service_fee_balance_minor: 0, + unallocated_cw_service_fee_balance_minor: 0, + })); - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - original_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_original_cw_service_fee_balance_minor() { - let subject = PaymentAdjusterInnerNull::default(); + subject.invalidate_guts(); - let _ = subject.original_cw_service_fee_balance_minor(); + let guts_removed = subject.initialized_guts_opt.borrow().is_none(); + assert_eq!(guts_removed, true) } #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - unallocated_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_unallocated_cw_balance() { - let subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.unallocated_cw_service_fee_balance_minor(); + fn reasonable_panics_about_lacking_initialization_for_respective_methods() { + let uninitialized_subject = PaymentAdjusterInner::default(); + test_properly_implemented_panic(&uninitialized_subject, "now", |subject| { + subject.now(); + }); + test_properly_implemented_panic( + &uninitialized_subject, + "max_debt_above_threshold_in_qualified_payables_minor", + |subject| { + subject.max_debt_above_threshold_in_qualified_payables_minor(); + }, + ); + test_properly_implemented_panic( + &uninitialized_subject, + "transaction_count_limit_opt", + |subject| { + subject.transaction_count_limit_opt(); + }, + ); + test_properly_implemented_panic( + &uninitialized_subject, + "original_cw_service_fee_balance_minor", + |subject| { + subject.original_cw_service_fee_balance_minor(); + }, + ); + test_properly_implemented_panic( + &uninitialized_subject, + "unallocated_cw_service_fee_balance_minor", + |subject| { + subject.unallocated_cw_service_fee_balance_minor(); + }, + ); + test_properly_implemented_panic( + &uninitialized_subject, + "subtract_from_unallocated_cw_service_fee_balance_minor", + |subject| { + subject.subtract_from_unallocated_cw_service_fee_balance_minor(123456); + }, + ) } - #[test] - #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - subtract_from_unallocated_cw_service_fee_balance_minor()" - )] - fn inner_null_calling_subtract_from_unallocated_cw_service_fee_balance_minor() { - let mut subject = PaymentAdjusterInnerNull::default(); - - let _ = subject.subtract_from_unallocated_cw_service_fee_balance_minor(123); + fn test_properly_implemented_panic( + subject: &PaymentAdjusterInner, + method_name: &str, + call_panicking_method: fn(&PaymentAdjusterInner), + ) { + let caught_panic = + catch_unwind(AssertUnwindSafe(|| call_panicking_method(subject))).unwrap_err(); + let actual_panic_msg = caught_panic.downcast_ref::().unwrap().to_owned(); + let expected_msg = format!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method_name}()'"); + assert_eq!( + actual_panic_msg, expected_msg, + "We expected this panic message: {}, but the panic looked different: {}", + expected_msg, actual_panic_msg + ) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 43ce82503..48b91c250 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -12,7 +12,6 @@ mod preparatory_analyser; mod service_fee_adjuster; #[cfg(test)] mod test_utils; - use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; @@ -22,7 +21,7 @@ use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, }; use crate::accountant::payment_adjuster::inner::{ - PaymentAdjusterInner, PaymentAdjusterInnerNull, PaymentAdjusterInnerReal, + PaymentAdjusterInner, }; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, @@ -77,7 +76,7 @@ pub trait PaymentAdjuster { ) -> AdjustmentAnalysisResult; fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result; @@ -88,7 +87,7 @@ pub struct PaymentAdjusterReal { disqualification_arbiter: DisqualificationArbiter, service_fee_adjuster: Box, calculators: Vec>, - inner: Box, + inner: PaymentAdjusterInner, logger: Logger, } @@ -106,7 +105,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { } fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result { @@ -115,13 +114,13 @@ impl PaymentAdjuster for PaymentAdjusterReal { let agent = setup.agent; let initial_service_fee_balance_minor = agent.service_fee_balance_minor(); let required_adjustment = setup.adjustment_analysis.adjustment; - let max_debt_above_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); self.initialize_inner( - initial_service_fee_balance_minor, required_adjustment, - max_debt_above_threshold_in_qualified_payables, + initial_service_fee_balance_minor, + max_debt_above_threshold_in_qualified_payables_minor, now, ); @@ -131,7 +130,7 @@ impl PaymentAdjuster for PaymentAdjusterReal { self.complete_debug_log_if_enabled(sketched_debug_log_opt, &affordable_accounts); - self.reset_inner(); + self.inner.invalidate_guts(); Ok(OutboundPaymentsInstructions::new( Either::Right(affordable_accounts), @@ -154,16 +153,17 @@ impl PaymentAdjusterReal { disqualification_arbiter: DisqualificationArbiter::default(), service_fee_adjuster: Box::new(ServiceFeeAdjusterReal::default()), calculators: vec![Box::new(BalanceCriterionCalculator::default())], - inner: Box::new(PaymentAdjusterInnerNull::default()), + inner: PaymentAdjusterInner::default(), logger: Logger::new("PaymentAdjuster"), } } fn initialize_inner( - &mut self, - cw_service_fee_balance: u128, + &self, required_adjustment: Adjustment, - max_debt_above_threshold_in_qualified_payables: u128, + initial_service_fee_balance_minor: u128, + //TODO use 'of qualified payables' instead of 'in' + max_debt_above_threshold_in_qualified_payables_minor_minor: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { @@ -173,22 +173,16 @@ impl PaymentAdjusterReal { Adjustment::ByServiceFee => None, }; - let inner = PaymentAdjusterInnerReal::new( - now, + self.inner.initialize_guts( transaction_fee_limitation_opt, - cw_service_fee_balance, - max_debt_above_threshold_in_qualified_payables, - ); - - self.inner = Box::new(inner); - } - - fn reset_inner(&mut self) { - self.inner = Box::new(PaymentAdjusterInnerNull::default()) + initial_service_fee_balance_minor, + max_debt_above_threshold_in_qualified_payables_minor_minor, + now, + ) } fn run_adjustment( - &mut self, + &self, analyzed_accounts: Vec, ) -> Result, PaymentAdjusterError> { let weighed_accounts = self.calculate_weights(analyzed_accounts); @@ -213,13 +207,13 @@ impl PaymentAdjusterReal { } fn resolve_initial_adjustment_dispatch( - &mut self, + &self, weighed_payables: Vec, ) -> Result< Either, Vec>, PaymentAdjusterError, > { - if let Some(limit) = self.inner.transaction_fee_count_limit_opt() { + if let Some(limit) = self.inner.transaction_count_limit_opt() { return self.begin_with_adjustment_by_transaction_fee(weighed_payables, limit); } @@ -229,7 +223,7 @@ impl PaymentAdjusterReal { } fn begin_with_adjustment_by_transaction_fee( - &mut self, + &self, weighed_accounts: Vec, transaction_count_limit: u16, ) -> Result< @@ -269,7 +263,7 @@ impl PaymentAdjusterReal { } fn propose_possible_adjustment_recursively( - &mut self, + &self, weighed_accounts: Vec, ) -> Vec { diagnostics!( @@ -357,8 +351,8 @@ impl PaymentAdjusterReal { criteria_calculators .iter() .fold(0_u128, |weight, criterion_calculator| { - let new_criterion = criterion_calculator - .calculate(&payable.qualified_as, self.inner.as_ref()); + let new_criterion = + criterion_calculator.calculate(&payable.qualified_as, &self.inner); let summed_up = weight + new_criterion; @@ -378,7 +372,7 @@ impl PaymentAdjusterReal { } fn adjust_remaining_unallocated_cw_balance_down( - &mut self, + &self, decided_accounts: &[AdjustedAccountBeforeFinalization], ) { let subtrahend_total: u128 = sum_as(decided_accounts, |account| { @@ -561,7 +555,7 @@ impl Display for PaymentAdjusterError { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::inner::PaymentAdjusterInnerReal; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, WeighedPayable, @@ -618,54 +612,6 @@ mod tests { let _ = subject.inner.unallocated_cw_service_fee_balance_minor(); } - fn test_initialize_inner_works( - required_adjustment: Adjustment, - expected_tx_fee_limit_opt_result: Option, - ) { - let mut subject = PaymentAdjusterReal::default(); - let cw_service_fee_balance = 111_222_333_444; - let max_debt_above_threshold_in_qualified_payables = 3_555_666; - let now = SystemTime::now(); - - subject.initialize_inner( - cw_service_fee_balance, - required_adjustment, - max_debt_above_threshold_in_qualified_payables, - now, - ); - - assert_eq!(subject.inner.now(), now); - assert_eq!( - subject.inner.transaction_fee_count_limit_opt(), - expected_tx_fee_limit_opt_result - ); - assert_eq!( - subject.inner.original_cw_service_fee_balance_minor(), - cw_service_fee_balance - ); - assert_eq!( - subject.inner.unallocated_cw_service_fee_balance_minor(), - cw_service_fee_balance - ); - assert_eq!( - subject - .inner - .max_debt_above_threshold_in_qualified_payables(), - max_debt_above_threshold_in_qualified_payables - ) - } - - #[test] - fn initialize_inner_works() { - test_initialize_inner_works(Adjustment::ByServiceFee, None); - test_initialize_inner_works( - Adjustment::BeginByTransactionFee { - transaction_count_limit: 5, - }, - Some(5), - ); - } - #[test] fn consider_adjustment_happy_path() { init_test_logging(); @@ -686,7 +632,10 @@ mod tests { // Service fee balance == payments let input_2 = make_input_for_initial_check_tests( Some(TestConfigForServiceFeeBalances { - payable_account_balances_minor: vec![multiply_by_billion(85), multiply_by_billion(15)], + payable_account_balances_minor: vec![ + multiply_by_billion(85), + multiply_by_billion(15), + ], cw_balance_minor: multiply_by_billion(100), }), None, @@ -1102,9 +1051,9 @@ mod tests { let largest_exceeding_balance = (balance_1 - account_1.qualified_as.payment_threshold_intercept_minor) .max(balance_2 - account_2.qualified_as.payment_threshold_intercept_minor); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .cw_service_fee_balance_minor(cw_service_fee_balance_minor) - .max_debt_above_threshold_in_qualified_payables(largest_exceeding_balance) + .max_debt_above_threshold_in_qualified_payables_minor(largest_exceeding_balance) .build(); let weighed_payables = vec![ WeighedPayable::new(account_1, weight_account_1), @@ -1307,7 +1256,7 @@ mod tests { let sum_of_disqualification_limits = sum_as(&analyzed_accounts, |account| { account.disqualification_limit_minor }); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1425,10 +1374,10 @@ mod tests { untaken_cw_service_fee_balance_minor: u128, expected_result: bool, ) { - let mut subject = PaymentAdjusterReal::new(); + let subject = PaymentAdjusterReal::new(); subject.initialize_inner( - untaken_cw_service_fee_balance_minor, Adjustment::ByServiceFee, + untaken_cw_service_fee_balance_minor, 1234567, SystemTime::now(), ); @@ -1533,7 +1482,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1645,7 +1594,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1738,7 +1687,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .build(); @@ -1827,7 +1776,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -1929,7 +1878,7 @@ mod tests { .calculate_result(total_weight_account_1) .calculate_result(total_weight_account_2) .calculate_result(total_weight_account_3); - let mut subject = PaymentAdjusterBuilder::default() + let subject = PaymentAdjusterBuilder::default() .start_with_inner_null() .replace_calculators_with_mock(calculator_mock) .logger(Logger::new(test_name)) @@ -2303,8 +2252,8 @@ mod tests { let panic_msg = err.downcast_ref::().unwrap(); assert_eq!( panic_msg, - "The PaymentAdjuster Inner is uninitialised. It was detected while executing \ - original_cw_service_fee_balance_minor()" + "PaymentAdjusterInner is uninitialized. It was detected while calling method + 'original_cw_service_fee_balance_minor'" ) } @@ -2328,12 +2277,8 @@ mod tests { let cw_service_fee_balance_minor = multiply_by_billion(3_000); let exceeding_balance = qualified_payable.bare_account.balance_wei - qualified_payable.payment_threshold_intercept_minor; - let context = PaymentAdjusterInnerReal::new( - now, - None, - cw_service_fee_balance_minor, - exceeding_balance, - ); + let context = PaymentAdjusterInner::default(); + context.initialize_guts(None, cw_service_fee_balance_minor, exceeding_balance, now); payment_adjuster .calculators @@ -2506,13 +2451,13 @@ mod tests { cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = convert_collection(qualified_payables); - let max_debt_above_threshold_in_qualified_payables = + let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() .now(now) .cw_service_fee_balance_minor(cw_service_fee_balance_minor) - .max_debt_above_threshold_in_qualified_payables( - max_debt_above_threshold_in_qualified_payables, + .max_debt_above_threshold_in_qualified_payables_minor( + max_debt_above_threshold_in_qualified_payables_minor, ) .build(); let perform_adjustment_by_service_fee_params_arc = Arc::new(Mutex::new(Vec::new())); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 79761905d..da2480e88 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -113,7 +113,7 @@ fn loading_test_with_randomized_params() { let now = SystemTime::now(); let mut gn = thread_rng(); - let mut subject = PaymentAdjusterReal::new(); + let subject = PaymentAdjusterReal::new(); let number_of_requested_scenarios = 2000; let scenarios = generate_scenarios(&mut gn, now, number_of_requested_scenarios); let invalidly_generated_scenarios = number_of_requested_scenarios - scenarios.len(); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 6e37529d2..37118b573 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -7,7 +7,7 @@ use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalcula use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, }; -use crate::accountant::payment_adjuster::inner::{PaymentAdjusterInner, PaymentAdjusterInnerReal}; +use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustmentIterationResult, UnconfirmedAdjustment, WeighedPayable, }; @@ -39,7 +39,7 @@ pub struct PaymentAdjusterBuilder { now_opt: Option, cw_service_fee_balance_minor_opt: Option, mock_replacing_calculators_opt: Option, - max_debt_above_threshold_in_qualified_payables_opt: Option, + max_debt_above_threshold_in_qualified_payables_minor_opt: Option, transaction_limit_count_opt: Option, logger_opt: Option, } @@ -50,14 +50,13 @@ impl PaymentAdjusterBuilder { let logger = self.logger_opt.unwrap_or(Logger::new("test")); payment_adjuster.logger = logger; if !self.start_with_inner_null { - let inner = Box::new(PaymentAdjusterInnerReal::new( - self.now_opt.unwrap_or(SystemTime::now()), + payment_adjuster.inner.initialize_guts( self.transaction_limit_count_opt, self.cw_service_fee_balance_minor_opt.unwrap_or(0), - self.max_debt_above_threshold_in_qualified_payables_opt + self.max_debt_above_threshold_in_qualified_payables_minor_opt .unwrap_or(0), - )); - payment_adjuster.inner = inner; + self.now_opt.unwrap_or(SystemTime::now()), + ); } if let Some(calculator) = self.mock_replacing_calculators_opt { payment_adjuster.calculators = vec![Box::new(calculator)] @@ -83,11 +82,12 @@ impl PaymentAdjusterBuilder { self } - pub fn max_debt_above_threshold_in_qualified_payables( + pub fn max_debt_above_threshold_in_qualified_payables_minor( mut self, max_exceeding_part_of_debt: u128, ) -> Self { - self.max_debt_above_threshold_in_qualified_payables_opt = Some(max_exceeding_part_of_debt); + self.max_debt_above_threshold_in_qualified_payables_minor_opt = + Some(max_exceeding_part_of_debt); self } @@ -170,7 +170,7 @@ impl CriterionCalculator for CriterionCalculatorMock { fn calculate( &self, account: &QualifiedPayableAccount, - _context: &dyn PaymentAdjusterInner, + _context: &PaymentAdjusterInner, ) -> u128 { self.calculate_params.lock().unwrap().push(account.clone()); self.calculate_results.borrow_mut().remove(0) diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 2d30d293b..f83414bbd 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -190,7 +190,7 @@ pub struct PayableScanner { pub payable_dao: Box, pub pending_payable_dao: Box, pub payable_inspector: PayableInspector, - pub payment_adjuster: RefCell>, + pub payment_adjuster: Box, } impl Scanner for PayableScanner { @@ -274,7 +274,6 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { match self .payment_adjuster - .borrow() .consider_adjustment(unprotected, &*msg.agent) { Ok(processed) => { @@ -315,11 +314,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { logger: &Logger, ) -> Option { let now = SystemTime::now(); - match self - .payment_adjuster - .borrow_mut() - .adjust_payments(setup, now) - { + match self.payment_adjuster.adjust_payments(setup, now) { Ok(instructions) => Some(instructions), Err(e) => { warning!( @@ -358,7 +353,7 @@ impl PayableScanner { payable_dao, pending_payable_dao, payable_inspector, - payment_adjuster: RefCell::new(payment_adjuster), + payment_adjuster, } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 4e5ab644f..5db6ac84d 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1488,7 +1488,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { } fn adjust_payments( - &mut self, + &self, setup: PreparedAdjustment, now: SystemTime, ) -> Result { 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 4887122b4..580e3d606 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -612,8 +612,8 @@ mod tests { use crate::blockchain::blockchain_bridge::PendingPayableFingerprintSeeds; use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ - BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TX_FEE_MARGIN_IN_PERCENT, - TRANSACTION_LITERAL, + BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, + TX_FEE_MARGIN_IN_PERCENT, }; use crate::blockchain::blockchain_interface::test_utils::{ test_blockchain_interface_is_connected_and_functioning, LowBlockchainIntMock, From 969f423be8f94f81b7b375771b803f9e1c132e97 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 23 Dec 2024 00:19:45 +0100 Subject: [PATCH 232/250] GH-711: another wholesome fix for two tests --- .../disqualification_arbiter.rs | 10 +- .../miscellaneous/helper_functions.rs | 10 +- .../preparatory_analyser/mod.rs | 194 ++++++++++-------- .../accountant/payment_adjuster/test_utils.rs | 2 +- 4 files changed, 122 insertions(+), 94 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 7e78e815e..51746fab6 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -213,7 +213,7 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::payment_adjuster::test_utils::{ - make_non_guaranteed_unconfirmed_adjustment, make_weighed_account, + make_meaningless_weighed_account, make_non_guaranteed_unconfirmed_adjustment, }; use itertools::Itertools; use masq_lib::logger::Logger; @@ -415,13 +415,13 @@ mod tests { #[test] fn only_account_with_the_smallest_weight_will_be_disqualified_in_single_iteration() { - let mut account_1 = make_weighed_account(123); + let mut account_1 = make_meaningless_weighed_account(123); account_1.analyzed_account.disqualification_limit_minor = 1_000_000; account_1.weight = 1000; - let mut account_2 = make_weighed_account(456); + let mut account_2 = make_meaningless_weighed_account(456); account_2.analyzed_account.disqualification_limit_minor = 1_000_000; account_2.weight = 1002; - let mut account_3 = make_weighed_account(789); + let mut account_3 = make_meaningless_weighed_account(789); account_3.analyzed_account.disqualification_limit_minor = 1_000_000; account_3.weight = 999; let wallet_3 = account_3 @@ -430,7 +430,7 @@ mod tests { .bare_account .wallet .clone(); - let mut account_4 = make_weighed_account(012); + let mut account_4 = make_meaningless_weighed_account(012); account_4.analyzed_account.disqualification_limit_minor = 1_000_000; account_4.weight = 1001; // Notice that each proposed adjustment is below 1_000_000 which makes it clear all these diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index e5632f12a..8f680e1e8 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -191,7 +191,7 @@ mod tests { exhaust_cw_balance_entirely, find_largest_exceeding_balance, no_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; - use crate::accountant::payment_adjuster::test_utils::make_weighed_account; + use crate::accountant::payment_adjuster::test_utils::make_meaningless_weighed_account; use crate::accountant::test_utils::{make_meaningless_analyzed_account, make_payable_account}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; @@ -251,13 +251,13 @@ mod tests { #[test] fn eliminate_accounts_by_tx_fee_limit_works() { - let mut account_1 = make_weighed_account(123); + let mut account_1 = make_meaningless_weighed_account(123); account_1.weight = 1_000_000_000; - let mut account_2 = make_weighed_account(456); + let mut account_2 = make_meaningless_weighed_account(456); account_2.weight = 999_999_999; - let mut account_3 = make_weighed_account(789); + let mut account_3 = make_meaningless_weighed_account(789); account_3.weight = 999_999_999; - let mut account_4 = make_weighed_account(1011); + let mut account_4 = make_meaningless_weighed_account(1011); account_4.weight = 1_000_000_001; let affordable_transaction_count = 2; diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 9bbbfa159..c1e9bde62 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -42,7 +42,7 @@ impl PreparatoryAnalyzer { { let number_of_accounts = qualified_payables.len(); let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor(); - let per_transaction_requirement_minor = + let required_tx_fee_per_transaction_minor = agent.estimated_transaction_fee_per_transaction_minor(); let gas_price_margin = agent.gas_price_margin(); @@ -50,7 +50,7 @@ impl PreparatoryAnalyzer { .determine_transaction_count_limit_by_transaction_fee( cw_transaction_fee_balance_minor, gas_price_margin, - per_transaction_requirement_minor, + required_tx_fee_per_transaction_minor, number_of_accounts, logger, ); @@ -107,31 +107,30 @@ impl PreparatoryAnalyzer { transaction_fee_check_result: Result, TransactionFeeImmoderateInsufficiency>, service_fee_check_result: Result<(), ServiceFeeImmoderateInsufficiency>, ) -> Result, PaymentAdjusterError> { - match (transaction_fee_check_result, service_fee_check_result) { - (Err(transaction_fee_check_error), Ok(_)) => Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts, - transaction_fee_opt: Some(transaction_fee_check_error), - service_fee_opt: None, - }, - ), - (Err(transaction_fee_check_error), Err(service_fee_check_error)) => Err( + let construct_error = + |tx_fee_check_err_opt: Option, + service_fee_check_err_opt: Option| { PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { number_of_accounts, - transaction_fee_opt: Some(transaction_fee_check_error), - service_fee_opt: Some(service_fee_check_error), - }, - ), - (Ok(_), Err(service_fee_check_error)) => Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts, - transaction_fee_opt: None, - service_fee_opt: Some(service_fee_check_error), - }, - ), - (Ok(transaction_fee_limiting_count_opt), Ok(())) => { - Ok(transaction_fee_limiting_count_opt) + transaction_fee_opt: tx_fee_check_err_opt, + service_fee_opt: service_fee_check_err_opt, + } + }; + + match (transaction_fee_check_result, service_fee_check_result) { + (Err(transaction_fee_check_error), Ok(_)) => { + Err(construct_error(Some(transaction_fee_check_error), None)) + } + (Err(transaction_fee_check_error), Err(service_fee_check_error)) => { + Err(construct_error( + Some(transaction_fee_check_error), + Some(service_fee_check_error), + )) + } + (Ok(_), Err(service_fee_check_error)) => { + Err(construct_error(None, Some(service_fee_check_error))) } + (Ok(tx_count_limit_opt), Ok(())) => Ok(tx_count_limit_opt), } } @@ -220,7 +219,7 @@ impl PreparatoryAnalyzer { ) -> Result<(), Error> where AnalyzableAccounts: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, - ErrorFactory: ServiceFeeSingleTXErrorFactory, + ErrorFactory: ServiceFeeSingleTXErrorFactory, { let lowest_disqualification_limit = Self::find_lowest_disqualification_limit(prepared_accounts); @@ -232,14 +231,8 @@ impl PreparatoryAnalyzer { if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(()) } else { - let analyzed_accounts_count = prepared_accounts.len(); - let required_service_fee_total = - Self::compute_total_of_service_fee_required(prepared_accounts); - let err = service_fee_error_factory.make( - analyzed_accounts_count, - required_service_fee_total, - cw_service_fee_balance_minor, - ); + let err = + service_fee_error_factory.make(prepared_accounts, cw_service_fee_balance_minor); Err(err) } } @@ -258,7 +251,7 @@ impl PreparatoryAnalyzer { .collect() } - fn compute_total_of_service_fee_required(payables: &[Account]) -> u128 + fn compute_total_service_fee_required(payables: &[Account]) -> u128 where Account: BalanceProvidingAccount, { @@ -274,7 +267,7 @@ impl PreparatoryAnalyzer { Account: BalanceProvidingAccount, { let service_fee_totally_required_minor = - Self::compute_total_of_service_fee_required(qualified_payables); + Self::compute_total_service_fee_required(qualified_payables); (service_fee_totally_required_minor > cw_service_fee_balance_minor) .then(|| { log_adjustment_by_service_fee_is_required( @@ -298,27 +291,26 @@ impl PreparatoryAnalyzer { } } -pub trait ServiceFeeSingleTXErrorFactory { - fn make( - &self, - number_of_accounts: usize, - required_service_fee_total: u128, - cw_service_fee_balance_minor: u128, - ) -> E; +pub trait ServiceFeeSingleTXErrorFactory +where + AnalyzableAccount: BalanceProvidingAccount, +{ + fn make(&self, accounts: &[AnalyzableAccount], cw_service_fee_balance_minor: u128) -> Error; } #[derive(Default)] pub struct EarlyServiceFeeSingleTXErrorFactory {} -impl ServiceFeeSingleTXErrorFactory +impl ServiceFeeSingleTXErrorFactory for EarlyServiceFeeSingleTXErrorFactory { fn make( &self, - _number_of_accounts: usize, - total_service_fee_required_minor: u128, + accounts: &[AnalyzedPayableAccount], cw_service_fee_balance_minor: u128, ) -> ServiceFeeImmoderateInsufficiency { + let total_service_fee_required_minor = + PreparatoryAnalyzer::compute_total_service_fee_required(accounts); ServiceFeeImmoderateInsufficiency { total_service_fee_required_minor, cw_service_fee_balance_minor, @@ -345,13 +337,15 @@ impl LateServiceFeeSingleTxErrorFactory { } } -impl ServiceFeeSingleTXErrorFactory for LateServiceFeeSingleTxErrorFactory { +impl ServiceFeeSingleTXErrorFactory + for LateServiceFeeSingleTxErrorFactory +{ fn make( &self, - number_of_accounts: usize, - _required_service_fee_total: u128, + current_set_of_accounts: &[WeighedPayable], cw_service_fee_balance_minor: u128, ) -> PaymentAdjusterError { + let number_of_accounts = current_set_of_accounts.len(); PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { original_number_of_accounts: self.original_number_of_accounts, number_of_accounts, @@ -367,13 +361,17 @@ mod tests { use crate::accountant::payment_adjuster::disqualification_arbiter::{ DisqualificationArbiter, DisqualificationGauge, }; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeighedPayable; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; + use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::{ + BalanceProvidingAccount, DisqualificationLimitProvidingAccount, + }; use crate::accountant::payment_adjuster::preparatory_analyser::{ EarlyServiceFeeSingleTXErrorFactory, LateServiceFeeSingleTxErrorFactory, PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, }; use crate::accountant::payment_adjuster::test_utils::{ - make_weighed_account, multiply_by_billion, multiply_by_billion_concise, + make_meaningless_weighed_account, multiply_by_billion, multiply_by_billion_concise, DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ @@ -381,9 +379,7 @@ mod tests { ServiceFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::accountant::test_utils::{ - make_meaningless_analyzed_account, make_meaningless_qualified_payable, - }; + use crate::accountant::test_utils::make_meaningless_qualified_payable; use crate::accountant::QualifiedPayableAccount; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; use itertools::Either; @@ -495,46 +491,75 @@ mod tests { ) } - fn test_not_enough_for_even_the_least_demanding_account_causes_error( + fn test_not_enough_for_the_least_demanding_account_causes_error< + ErrorFactory, + Error, + AccountsRightTypeEnsurer, + ExpectedErrorPreparer, + AnalyzableAccount, + >( error_factory: ErrorFactory, - expected_error_preparer: F, + account_right_type_ensurer: AccountsRightTypeEnsurer, + expected_error_preparer: ExpectedErrorPreparer, ) where - F: FnOnce(usize, u128, u128) -> Error, - ErrorFactory: ServiceFeeSingleTXErrorFactory, + AccountsRightTypeEnsurer: FnOnce(Vec) -> Vec, + ExpectedErrorPreparer: FnOnce(usize, u128, u128) -> Error, + ErrorFactory: ServiceFeeSingleTXErrorFactory, Error: Debug + PartialEq, + AnalyzableAccount: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, { - let mut account_1 = make_meaningless_analyzed_account(111); - account_1.qualified_as.bare_account.balance_wei = 2_000_000_000; - account_1.disqualification_limit_minor = 1_500_000_000; - let mut account_2 = make_meaningless_analyzed_account(222); - account_2.qualified_as.bare_account.balance_wei = 1_000_050_000; - account_2.disqualification_limit_minor = 1_000_000_101; - let mut account_3 = make_meaningless_analyzed_account(333); - account_3.qualified_as.bare_account.balance_wei = 1_000_111_111; - account_3.disqualification_limit_minor = 1_000_000_222; - let cw_service_fee_balance = 1_000_000_100; + let mut account_1 = make_meaningless_weighed_account(111); + account_1 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = 2_000_000_000; + account_1.analyzed_account.disqualification_limit_minor = 1_500_000_000; + let mut account_2 = make_meaningless_weighed_account(222); + account_2 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = 1_000_050_000; + account_2.analyzed_account.disqualification_limit_minor = 1_000_000_101; + let mut account_3 = make_meaningless_weighed_account(333); + account_3 + .analyzed_account + .qualified_as + .bare_account + .balance_wei = 1_000_111_111; + account_3.analyzed_account.disqualification_limit_minor = 1_000_000_222; + let cw_service_fee_balance_minor = 1_000_000_100; + let service_fee_total_of_the_known_set = account_1.initial_balance_minor() + + account_2.initial_balance_minor() + + account_3.initial_balance_minor(); let supplied_accounts = vec![account_1, account_2, account_3]; let supplied_accounts_count = supplied_accounts.len(); - let service_fee_total_of_the_known_set = 2_000_000_000 + 1_000_050_000 + 1_000_111_111; + let rightly_typed_accounts = account_right_type_ensurer(supplied_accounts); let result = PreparatoryAnalyzer::check_adjustment_possibility( - &supplied_accounts, - cw_service_fee_balance, + &rightly_typed_accounts, + cw_service_fee_balance_minor, error_factory, ); let expected_error = expected_error_preparer( supplied_accounts_count, service_fee_total_of_the_known_set, - cw_service_fee_balance, + cw_service_fee_balance_minor, ); assert_eq!(result, Err(expected_error)) } #[test] - fn not_enough_for_even_the_least_demanding_account_error_right_after_positive_tx_fee_check() { + fn not_enough_for_the_least_demanding_account_error_right_after_alarmed_tx_fee_check() { let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); - + let accounts_right_type_ensurer = |weighed_payables: Vec| { + weighed_payables + .into_iter() + .map(|weighed_account| weighed_account.analyzed_account) + .collect() + }; let expected_error_preparer = |_, total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { ServiceFeeImmoderateInsufficiency { @@ -543,25 +568,27 @@ mod tests { } }; - test_not_enough_for_even_the_least_demanding_account_causes_error( + test_not_enough_for_the_least_demanding_account_causes_error( error_factory, + accounts_right_type_ensurer, expected_error_preparer, ) } #[test] - fn not_enough_for_even_the_least_demanding_account_error_right_after_tx_fee_accounts_dump() { + fn not_enough_for_the_least_demanding_account_error_right_after_accounts_dumped_for_tx_fee() { let original_accounts = vec![ - make_weighed_account(123), - make_weighed_account(456), - make_weighed_account(789), - make_weighed_account(1011), + make_meaningless_weighed_account(123), + make_meaningless_weighed_account(456), + make_meaningless_weighed_account(789), + make_meaningless_weighed_account(1011), ]; let original_number_of_accounts = original_accounts.len(); let initial_sum = sum_as(&original_accounts, |account| { account.initial_balance_minor() }); let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); + let accounts_right_type_ensurer = |accounts| accounts; let expected_error_preparer = |number_of_accounts, _, cw_service_fee_balance_minor| { PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { original_number_of_accounts, @@ -571,8 +598,9 @@ mod tests { } }; - test_not_enough_for_even_the_least_demanding_account_causes_error( + test_not_enough_for_the_least_demanding_account_causes_error( error_factory, + accounts_right_type_ensurer, expected_error_preparer, ) } @@ -582,14 +610,14 @@ mod tests { init_test_logging(); let test_name = "accounts_analyzing_works_even_for_weighed_payable"; let balance_1 = multiply_by_billion(2_000_000); - let mut weighed_account_1 = make_weighed_account(123); + let mut weighed_account_1 = make_meaningless_weighed_account(123); weighed_account_1 .analyzed_account .qualified_as .bare_account .balance_wei = balance_1; let balance_2 = multiply_by_billion(3_456_000); - let mut weighed_account_2 = make_weighed_account(456); + let mut weighed_account_2 = make_meaningless_weighed_account(456); weighed_account_2 .analyzed_account .qualified_as @@ -627,14 +655,14 @@ mod tests { #[test] fn construction_of_error_context_with_accounts_dumped_works() { let balance_1 = 1234567; - let mut account_1 = make_weighed_account(123); + let mut account_1 = make_meaningless_weighed_account(123); account_1 .analyzed_account .qualified_as .bare_account .balance_wei = balance_1; let balance_2 = 999888777; - let mut account_2 = make_weighed_account(345); + let mut account_2 = make_meaningless_weighed_account(345); account_2 .analyzed_account .qualified_as diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 37118b573..f0ff6a89e 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -312,7 +312,7 @@ pub fn make_meaningless_analyzed_account_by_wallet( account } -pub fn make_weighed_account(n: u64) -> WeighedPayable { +pub fn make_meaningless_weighed_account(n: u64) -> WeighedPayable { WeighedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) } From 2178e117201926a2c8bb1d4960d5779197414038 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:01:55 +0100 Subject: [PATCH 233/250] GH-711: a few more little fixes --- .../preparatory_analyser/mod.rs | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index c1e9bde62..3ef3fa2f9 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -394,23 +394,20 @@ mod tests { test_name: &str, disqualification_gauge: DisqualificationGaugeMock, original_accounts: [QualifiedPayableAccount; 2], - cw_service_fee_balance: u128, + cw_service_fee_balance_minor: u128, ) { init_test_logging(); let determine_limit_params_arc = Arc::new(Mutex::new(vec![])); - let disqualification_gauge = double_mock_results_queue(disqualification_gauge) - .determine_limit_params(&determine_limit_params_arc); + let disqualification_gauge = + make_mock_with_two_results_doubled_into_four(disqualification_gauge) + .determine_limit_params(&determine_limit_params_arc); let total_amount_required: u128 = sum_as(original_accounts.as_slice(), |account| { account.bare_account.balance_wei }); let disqualification_arbiter = DisqualificationArbiter::new(Box::new(disqualification_gauge)); let subject = PreparatoryAnalyzer {}; - let blockchain_agent = BlockchainAgentMock::default() - .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) - .transaction_fee_balance_minor_result(U256::MAX) - .estimated_transaction_fee_per_transaction_minor_result(123456) - .service_fee_balance_minor_result(cw_service_fee_balance); + let blockchain_agent = make_populated_blockchain_agent(cw_service_fee_balance_minor); let result = subject.analyze_accounts( &blockchain_agent, @@ -419,13 +416,12 @@ mod tests { &Logger::new(test_name), ); - let expected_adjustment_analysis = { - let analyzed_accounts = PreparatoryAnalyzer::pre_process_accounts_for_adjustments( - original_accounts.to_vec(), - &disqualification_arbiter, - ); - AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts) - }; + let analyzed_accounts = PreparatoryAnalyzer::pre_process_accounts_for_adjustments( + original_accounts.to_vec(), + &disqualification_arbiter, + ); + let expected_adjustment_analysis = + AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts); assert_eq!(result, Ok(Either::Right(expected_adjustment_analysis))); let determine_limit_params = determine_limit_params_arc.lock().unwrap(); let account_1 = &original_accounts[0]; @@ -447,10 +443,18 @@ mod tests { "WARN: {test_name}: Mature payables amount to {} MASQ wei while the consuming wallet \ holds only {} wei. Adjustment in their count or balances is necessary.", total_amount_required.separate_with_commas(), - cw_service_fee_balance.separate_with_commas() + cw_service_fee_balance_minor.separate_with_commas() )); } + fn make_populated_blockchain_agent(cw_service_fee_balance_minor: u128) -> BlockchainAgentMock { + BlockchainAgentMock::default() + .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) + .transaction_fee_balance_minor_result(U256::MAX) + .estimated_transaction_fee_per_transaction_minor_result(123456) + .service_fee_balance_minor_result(cw_service_fee_balance_minor) + } + #[test] fn adjustment_possibility_nearly_rejected_when_cw_balance_slightly_bigger() { let mut account_1 = make_meaningless_qualified_payable(111); @@ -491,19 +495,19 @@ mod tests { ) } - fn test_not_enough_for_the_least_demanding_account_causes_error< + fn test_not_enough_even_for_the_smallest_account_error< ErrorFactory, Error, - AccountsRightTypeEnsurer, - ExpectedErrorPreparer, + EnsureAccountsRightType, + PrepareExpectedError, AnalyzableAccount, >( error_factory: ErrorFactory, - account_right_type_ensurer: AccountsRightTypeEnsurer, - expected_error_preparer: ExpectedErrorPreparer, + ensure_account_right_type: EnsureAccountsRightType, + prepare_expected_error: PrepareExpectedError, ) where - AccountsRightTypeEnsurer: FnOnce(Vec) -> Vec, - ExpectedErrorPreparer: FnOnce(usize, u128, u128) -> Error, + EnsureAccountsRightType: FnOnce(Vec) -> Vec, + PrepareExpectedError: FnOnce(usize, u128, u128) -> Error, ErrorFactory: ServiceFeeSingleTXErrorFactory, Error: Debug + PartialEq, AnalyzableAccount: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, @@ -535,7 +539,7 @@ mod tests { + account_3.initial_balance_minor(); let supplied_accounts = vec![account_1, account_2, account_3]; let supplied_accounts_count = supplied_accounts.len(); - let rightly_typed_accounts = account_right_type_ensurer(supplied_accounts); + let rightly_typed_accounts = ensure_account_right_type(supplied_accounts); let result = PreparatoryAnalyzer::check_adjustment_possibility( &rightly_typed_accounts, @@ -543,7 +547,7 @@ mod tests { error_factory, ); - let expected_error = expected_error_preparer( + let expected_error = prepare_expected_error( supplied_accounts_count, service_fee_total_of_the_known_set, cw_service_fee_balance_minor, @@ -552,15 +556,15 @@ mod tests { } #[test] - fn not_enough_for_the_least_demanding_account_error_right_after_alarmed_tx_fee_check() { + fn not_enough_for_even_the_smallest_account_error_right_after_alarmed_tx_fee_check() { let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); - let accounts_right_type_ensurer = |weighed_payables: Vec| { + let ensure_accounts_right_type = |weighed_payables: Vec| { weighed_payables .into_iter() .map(|weighed_account| weighed_account.analyzed_account) .collect() }; - let expected_error_preparer = + let prepare_expected_error = |_, total_amount_demanded_in_accounts_in_place, cw_service_fee_balance_minor| { ServiceFeeImmoderateInsufficiency { total_service_fee_required_minor: total_amount_demanded_in_accounts_in_place, @@ -568,15 +572,15 @@ mod tests { } }; - test_not_enough_for_the_least_demanding_account_causes_error( + test_not_enough_even_for_the_smallest_account_error( error_factory, - accounts_right_type_ensurer, - expected_error_preparer, + ensure_accounts_right_type, + prepare_expected_error, ) } #[test] - fn not_enough_for_the_least_demanding_account_error_right_after_accounts_dumped_for_tx_fee() { + fn not_enough_for_even_the_smallest_account_error_right_after_accounts_dumped_for_tx_fee() { let original_accounts = vec![ make_meaningless_weighed_account(123), make_meaningless_weighed_account(456), @@ -588,8 +592,8 @@ mod tests { account.initial_balance_minor() }); let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); - let accounts_right_type_ensurer = |accounts| accounts; - let expected_error_preparer = |number_of_accounts, _, cw_service_fee_balance_minor| { + let ensure_accounts_right_type = |accounts| accounts; + let prepare_expected_error = |number_of_accounts, _, cw_service_fee_balance_minor| { PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { original_number_of_accounts, number_of_accounts, @@ -598,17 +602,18 @@ mod tests { } }; - test_not_enough_for_the_least_demanding_account_causes_error( + test_not_enough_even_for_the_smallest_account_error( error_factory, - accounts_right_type_ensurer, - expected_error_preparer, + ensure_accounts_right_type, + prepare_expected_error, ) } #[test] - fn accounts_analyzing_works_even_for_weighed_payable() { + fn recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighted_payables() { init_test_logging(); - let test_name = "accounts_analyzing_works_even_for_weighed_payable"; + let test_name = + "recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighted_payables"; let balance_1 = multiply_by_billion(2_000_000); let mut weighed_account_1 = make_meaningless_weighed_account(123); weighed_account_1 @@ -625,15 +630,17 @@ mod tests { .balance_wei = balance_2; let accounts = vec![weighed_account_1, weighed_account_2]; let service_fee_totally_required_minor = balance_1 + balance_2; + // We start at a value being one bigger than required, and in the act, we subtract from it + // so that we also get the exact edge and finally also not enough by one. let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1; let error_factory = LateServiceFeeSingleTxErrorFactory::new(&accounts); let logger = Logger::new(test_name); let subject = PreparatoryAnalyzer::new(); [(0, false), (1, false), (2, true)].iter().for_each( - |(subtrahend_from_cw_balance, expected_result)| { + |(subtrahend_from_cw_balance, adjustment_is_needed_expected)| { let service_fee_balance = cw_service_fee_balance_minor - subtrahend_from_cw_balance; - let result = subject + let adjustment_is_needed_actual = subject .recheck_if_service_fee_adjustment_is_needed( &accounts, service_fee_balance, @@ -641,9 +648,10 @@ mod tests { &logger, ) .unwrap(); - assert_eq!(result, *expected_result); + assert_eq!(adjustment_is_needed_actual, *adjustment_is_needed_expected); }, ); + TestLogHandler::new().exists_log_containing(&format!( "WARN: {test_name}: Mature payables amount to {} MASQ wei while the consuming wallet \ holds only {}", @@ -681,16 +689,18 @@ mod tests { ) } - fn double_mock_results_queue(mock: DisqualificationGaugeMock) -> DisqualificationGaugeMock { - let originally_prepared_results = (0..2) + fn make_mock_with_two_results_doubled_into_four( + mock: DisqualificationGaugeMock, + ) -> DisqualificationGaugeMock { + let popped_results = (0..2) .map(|_| mock.determine_limit(0, 0, 0)) .collect::>(); - originally_prepared_results + popped_results .into_iter() .cycle() .take(4) - .fold(mock, |mock, result_to_be_added| { - mock.determine_limit_result(result_to_be_added) + .fold(mock, |mock, single_result| { + mock.determine_limit_result(single_result) }) } } From 200ea4f589ca629af464bf54ed1f7300bc8c586e Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:26:16 +0100 Subject: [PATCH 234/250] GH-711: repaired panic messages --- node/src/accountant/payment_adjuster/inner.rs | 10 ++++++++-- node/src/accountant/payment_adjuster/mod.rs | 8 ++++---- node/src/accountant/scanners/mod.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index d1fba592b..18c2d6868 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -136,7 +136,10 @@ impl PaymentAdjusterInner { } fn uninitialized_panic(method: &str) -> ! { - panic!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method}()'") + panic!( + "PaymentAdjusterInner is uninitialized. It was identified during the execution of \ + '{method}()'" + ) } } @@ -287,7 +290,10 @@ mod tests { let caught_panic = catch_unwind(AssertUnwindSafe(|| call_panicking_method(subject))).unwrap_err(); let actual_panic_msg = caught_panic.downcast_ref::().unwrap().to_owned(); - let expected_msg = format!("PaymentAdjusterInner is uninitialized. It was detected on the call of '{method_name}()'"); + let expected_msg = format!( + "PaymentAdjusterInner is uninitialized. It was \ + identified during the execution of '{method_name}()'" + ); assert_eq!( actual_panic_msg, expected_msg, "We expected this panic message: {}, but the panic looked different: {}", diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 48b91c250..3d3d884a8 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -603,8 +603,8 @@ mod tests { #[test] #[should_panic( - expected = "The PaymentAdjuster Inner is uninitialised. It was detected while \ - executing unallocated_cw_service_fee_balance_minor()" + expected = "PaymentAdjusterInner is uninitialized. It was identified during \ + the execution of 'unallocated_cw_service_fee_balance_minor()'" )] fn payment_adjuster_new_is_created_with_inner_null() { let subject = PaymentAdjusterReal::new(); @@ -2252,8 +2252,8 @@ mod tests { let panic_msg = err.downcast_ref::().unwrap(); assert_eq!( panic_msg, - "PaymentAdjusterInner is uninitialized. It was detected while calling method - 'original_cw_service_fee_balance_minor'" + "PaymentAdjusterInner is uninitialized. It was identified during the execution of \ + 'original_cw_service_fee_balance_minor()'" ) } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index f83414bbd..97123bb3b 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -131,7 +131,7 @@ pub struct ScannerCommon { initiated_at_opt: Option, // TODO The thresholds probably shouldn't be in ScannerCommon because the PendingPayableScanner // does not need it - pub payment_thresholds: Rc, + payment_thresholds: Rc, } impl ScannerCommon { From f19c8895f3b5409bde19a731cccbb79443f38e81 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 28 Dec 2024 16:00:32 +0100 Subject: [PATCH 235/250] GH-711: some harsh modification for older auxiliary test structures --- node/src/accountant/mod.rs | 14 +++-- node/src/accountant/scanners/mod.rs | 96 +++++++++++++++++++---------- node/src/accountant/test_utils.rs | 95 +++++++++++++++------------- 3 files changed, 124 insertions(+), 81 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 96d450047..f59901386 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1085,6 +1085,9 @@ mod tests { TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; + use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ + PayableInspector, PayableThresholdsGaugeReal, + }; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ @@ -1180,23 +1183,23 @@ mod tests { let receivable_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); let banned_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); let config_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); - let payable_dao_factory = PayableDaoFactoryMock::new() + let payable_dao_factory = PayableDaoFactoryMock::default() .make_params(&payable_dao_factory_params_arc) .make_result(PayableDaoMock::new()) // For Accountant .make_result(PayableDaoMock::new()) // For Payable Scanner .make_result(PayableDaoMock::new()); // For PendingPayable Scanner - let pending_payable_dao_factory = PendingPayableDaoFactoryMock::new() + let pending_payable_dao_factory = PendingPayableDaoFactoryMock::default() .make_params(&pending_payable_dao_factory_params_arc) .make_result(PendingPayableDaoMock::new()) // For Accountant .make_result(PendingPayableDaoMock::new()) // For Payable Scanner .make_result(PendingPayableDaoMock::new()); // For PendingPayable Scanner - let receivable_dao_factory = ReceivableDaoFactoryMock::new() + let receivable_dao_factory = ReceivableDaoFactoryMock::default() .make_params(&receivable_dao_factory_params_arc) .make_result(ReceivableDaoMock::new()) // For Accountant .make_result(ReceivableDaoMock::new()); // For Receivable Scanner let banned_dao_factory = BannedDaoFactoryMock::new() .make_params(&banned_dao_factory_params_arc) - .make_result(BannedDaoMock::new()); // For Receivable Scanner + .make_result(BannedDaoMock::default()); // For Receivable Scanner let config_dao_factory = ConfigDaoFactoryMock::new() .make_params(&config_dao_factory_params_arc) .make_result(ConfigDaoMock::new()); // For receivable scanner @@ -3633,6 +3636,9 @@ mod tests { let payable_scanner = PayableScannerBuilder::new() .payable_dao(payable_dao_for_payable_scanner) .pending_payable_dao(pending_payable_dao_for_payable_scanner) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .payment_adjuster(payment_adjuster) .build(); subject.scanners.payable = Box::new(payable_scanner); diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 97123bb3b..bdb84678e 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -362,21 +362,10 @@ impl PayableScanner { non_pending_payables: Vec, logger: &Logger, ) -> Vec { + let now = SystemTime::now(); let qualified_payables = non_pending_payables .into_iter() - .flat_map(|account| { - self.payable_exceeded_threshold(&account, SystemTime::now()) - .map(|payment_threshold_intercept| { - let creditor_thresholds = CreditorThresholds::new(gwei_to_wei( - self.common.payment_thresholds.permanent_debt_allowed_gwei, - )); - QualifiedPayableAccount::new( - account, - payment_threshold_intercept, - creditor_thresholds, - ) - }) - }) + .flat_map(|account| self.try_qualify_account(account, now)) .collect(); match logger.debug_enabled() { false => qualified_payables, @@ -387,16 +376,29 @@ impl PayableScanner { } } + fn try_qualify_account( + &self, + account: PayableAccount, + now: SystemTime, + ) -> Option { + let intercept_opt = self.payable_exceeded_threshold(&account, now); + + intercept_opt.map(|payment_threshold_intercept| { + let creditor_thresholds = CreditorThresholds::new(gwei_to_wei( + self.common.payment_thresholds.permanent_debt_allowed_gwei, + )); + QualifiedPayableAccount::new(account, payment_threshold_intercept, creditor_thresholds) + }) + } + fn payable_exceeded_threshold( &self, account: &PayableAccount, now: SystemTime, ) -> Option { - self.payable_inspector.payable_exceeded_threshold( - account, - &self.common.payment_thresholds, - now, - ) + let payment_thresholds = self.common.payment_thresholds.as_ref(); + self.payable_inspector + .payable_exceeded_threshold(account, payment_thresholds, now) } fn separate_existent_and_nonexistent_fingerprints<'a>( @@ -812,11 +814,7 @@ impl PendingPayableScanner { records due to {:?}", serialize_hashes(&fingerprints), e ) } else { - self.add_percent_to_the_total_of_paid_payable( - &fingerprints, - serialize_hashes, - logger, - ); + self.add_to_the_total_of_paid_payable(&fingerprints, serialize_hashes, logger); let rowids = fingerprints .iter() .map(|fingerprint| fingerprint.rowid) @@ -835,7 +833,7 @@ impl PendingPayableScanner { } } - fn add_percent_to_the_total_of_paid_payable( + fn add_to_the_total_of_paid_payable( &mut self, fingerprints: &[PendingPayableFingerprint], serialize_hashes: fn(&[PendingPayableFingerprint]) -> String, @@ -1194,14 +1192,15 @@ mod tests { #[test] fn scanners_struct_can_be_constructed_with_the_respective_scanners() { - let payable_dao_factory = PayableDaoFactoryMock::new() + let payable_dao_factory = PayableDaoFactoryMock::default() .make_result(PayableDaoMock::new()) .make_result(PayableDaoMock::new()); - let pending_payable_dao_factory = PendingPayableDaoFactoryMock::new() + let pending_payable_dao_factory = PendingPayableDaoFactoryMock::default() .make_result(PendingPayableDaoMock::new()) .make_result(PendingPayableDaoMock::new()); let receivable_dao = ReceivableDaoMock::new(); - let receivable_dao_factory = ReceivableDaoFactoryMock::new().make_result(receivable_dao); + let receivable_dao_factory = + ReceivableDaoFactoryMock::default().make_result(receivable_dao); let banned_dao_factory = BannedDaoFactoryMock::new().make_result(BannedDaoMock::new()); let set_params_arc = Arc::new(Mutex::new(vec![])); let config_dao_mock = ConfigDaoMock::new() @@ -1330,6 +1329,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let result = subject.begin_scan(now, None, &Logger::new(test_name)); @@ -1363,6 +1365,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let _result = subject.begin_scan(now, None, &Logger::new("test")); @@ -1385,6 +1390,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let result = subject.begin_scan(now, None, &Logger::new("test")); @@ -2180,6 +2188,9 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified_and_the_threshold_value_is_returned"; @@ -2210,6 +2221,9 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified"; let logger = Logger::new(test_name); @@ -2250,6 +2264,9 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let logger = Logger::new(test_name); @@ -2262,7 +2279,7 @@ mod tests { } #[test] - fn sniff_out_alarming_payables_and_maybe_log_them_generates_and_uses_correct_timestamp() { + fn sniff_out_alarming_payables_and_maybe_log_them_computes_debt_age_from_correct_now() { let payment_thresholds = PaymentThresholds { debt_threshold_gwei: 10_000_000_000, maturity_threshold_sec: 100, @@ -2272,10 +2289,10 @@ mod tests { unban_below_gwei: 0, }; let wallet = make_wallet("abc"); - // It is important to have a payable matching the declining part of the thresholds, also it - // will be more believable if the slope is steep because then one second can make the bigger - // difference in the intercept value, which is the value this test compare in order to - // conclude a pass + // It is important to have a payable laying in the declining part of the thresholds, also + // it will be the more believable the steeper we have the slope because then a single second + // can make a certain difference for the intercept value which is the value this test + // compares for carrying out the conclusion let debt_age = payment_thresholds.maturity_threshold_sec + (payment_thresholds.threshold_interval_sec / 2); let payable = PayableAccount { @@ -2286,6 +2303,9 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) + .payable_inspector(PayableInspector::new(Box::new( + PayableThresholdsGaugeReal::default(), + ))) .build(); let intercept_before = subject .payable_exceeded_threshold(&payable, SystemTime::now()) @@ -2301,8 +2321,16 @@ mod tests { .unwrap(); assert_eq!(result.len(), 1); assert_eq!(&result[0].bare_account.wallet, &wallet); - assert!(intercept_before >= result[0].payment_threshold_intercept_minor && result[0].payment_threshold_intercept_minor >= intercept_after, - "Tested intercept {} does not lie between two nows {} and {} while we assume the act generates third timestamp of presence", result[0].payment_threshold_intercept_minor, intercept_before, intercept_after + assert!( + intercept_before >= result[0].payment_threshold_intercept_minor + && result[0].payment_threshold_intercept_minor >= intercept_after, + "Tested intercept {} does not lie between two referring intercepts derived from two \ + calls of now(), intercept before: {} and after: {}, while the act is supposed to \ + generate its own, third timestamp used to compute an intercept somewhere in \ + the middle", + result[0].payment_threshold_intercept_minor, + intercept_before, + intercept_after ) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 5db6ac84d..061620eb9 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -103,25 +103,25 @@ pub fn make_payable_account_with_wallet_and_balance_and_timestamp_opt( } pub struct AccountantBuilder { - config: Option, - logger: Option, - payable_dao_factory: Option, - receivable_dao_factory: Option, - pending_payable_dao_factory: Option, - banned_dao_factory: Option, - config_dao_factory: Option, + config_opt: Option, + logger_opt: Option, + payable_dao_factory_opt: Option, + receivable_dao_factory_opt: Option, + pending_payable_dao_factory_opt: Option, + banned_dao_factory_opt: Option, + config_dao_factory_opt: Option, } impl Default for AccountantBuilder { fn default() -> Self { Self { - config: None, - logger: None, - payable_dao_factory: None, - receivable_dao_factory: None, - pending_payable_dao_factory: None, - banned_dao_factory: None, - config_dao_factory: None, + config_opt: None, + logger_opt: None, + payable_dao_factory_opt: None, + receivable_dao_factory_opt: None, + pending_payable_dao_factory_opt: None, + banned_dao_factory_opt: None, + config_dao_factory_opt: None, } } } @@ -239,12 +239,12 @@ const RECEIVABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER: [DestinationMarker; 2] = impl AccountantBuilder { pub fn bootstrapper_config(mut self, config: BootstrapperConfig) -> Self { - self.config = Some(config); + self.config_opt = Some(config); self } pub fn logger(mut self, logger: Logger) -> Self { - self.logger = Some(logger); + self.logger_opt = Some(logger); self } @@ -255,8 +255,7 @@ impl AccountantBuilder { Self::create_or_update_factory( specially_configured_daos, PENDING_PAYABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - &mut self.pending_payable_dao_factory, - PendingPayableDaoFactoryMock::new(), + &mut self.pending_payable_dao_factory_opt, ); self } @@ -268,8 +267,7 @@ impl AccountantBuilder { Self::create_or_update_factory( specially_configured_daos, PAYABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - &mut self.payable_dao_factory, - PayableDaoFactoryMock::new(), + &mut self.payable_dao_factory_opt, ); self } @@ -281,8 +279,7 @@ impl AccountantBuilder { Self::create_or_update_factory( specially_configured_daos, RECEIVABLE_DAOS_ACCOUNTANT_INITIALIZATION_ORDER, - &mut self.receivable_dao_factory, - ReceivableDaoFactoryMock::new(), + &mut self.receivable_dao_factory_opt, ); self } @@ -290,65 +287,59 @@ impl AccountantBuilder { fn create_or_update_factory( dao_set: Vec>, dao_initialization_order_in_regard_to_accountant: [DestinationMarker; N], - factory_field_in_builder: &mut Option, - dao_factory_mock: DAOFactory, + existing_dao_factory_mock_opt: &mut Option, ) where DAO: Default, - DAOFactory: DaoFactoryWithMakeReplace, + DAOFactory: DaoFactoryWithMakeReplace + Default, { - let make_queue_uncast = fill_vacancies_with_given_or_default_daos( + let finished_make_queue: Vec> = fill_vacancies_with_given_or_default_daos( dao_initialization_order_in_regard_to_accountant, dao_set, ); - let finished_make_queue: Vec> = make_queue_uncast - .into_iter() - .map(|elem| elem as Box) - .collect(); - - let ready_factory = match factory_field_in_builder.take() { + let ready_factory = match existing_dao_factory_mock_opt.take() { Some(existing_factory) => { existing_factory.replace_make_results(finished_make_queue); existing_factory } None => { - let new_factory = dao_factory_mock; + let new_factory = DAOFactory::default(); new_factory.replace_make_results(finished_make_queue); new_factory } }; - factory_field_in_builder.replace(ready_factory); + existing_dao_factory_mock_opt.replace(ready_factory); } pub fn config_dao(mut self, config_dao: ConfigDaoMock) -> Self { - self.config_dao_factory = Some(ConfigDaoFactoryMock::new().make_result(config_dao)); + self.config_dao_factory_opt = Some(ConfigDaoFactoryMock::new().make_result(config_dao)); self } pub fn build(self) -> Accountant { - let config = self.config.unwrap_or(make_bc_with_defaults()); - let payable_dao_factory = self.payable_dao_factory.unwrap_or( + let config = self.config_opt.unwrap_or(make_bc_with_defaults()); + let payable_dao_factory = self.payable_dao_factory_opt.unwrap_or( PayableDaoFactoryMock::new() .make_result(PayableDaoMock::new()) .make_result(PayableDaoMock::new()) .make_result(PayableDaoMock::new()), ); - let receivable_dao_factory = self.receivable_dao_factory.unwrap_or( + let receivable_dao_factory = self.receivable_dao_factory_opt.unwrap_or( ReceivableDaoFactoryMock::new() .make_result(ReceivableDaoMock::new()) .make_result(ReceivableDaoMock::new()), ); - let pending_payable_dao_factory = self.pending_payable_dao_factory.unwrap_or( + let pending_payable_dao_factory = self.pending_payable_dao_factory_opt.unwrap_or( PendingPayableDaoFactoryMock::new() .make_result(PendingPayableDaoMock::new()) .make_result(PendingPayableDaoMock::new()) .make_result(PendingPayableDaoMock::new()), ); let banned_dao_factory = self - .banned_dao_factory + .banned_dao_factory_opt .unwrap_or(BannedDaoFactoryMock::new().make_result(BannedDaoMock::new())); let config_dao_factory = self - .config_dao_factory + .config_dao_factory_opt .unwrap_or(ConfigDaoFactoryMock::new().make_result(ConfigDaoMock::new())); let mut accountant = Accountant::new( config, @@ -360,7 +351,7 @@ impl AccountantBuilder { config_dao_factory: Box::new(config_dao_factory), }, ); - if let Some(logger) = self.logger { + if let Some(logger) = self.logger_opt { accountant.logger = logger; } @@ -373,6 +364,12 @@ pub struct PayableDaoFactoryMock { make_results: RefCell>>, } +impl Default for PayableDaoFactoryMock { + fn default() -> Self { + Self::new() + } +} + impl PayableDaoFactory for PayableDaoFactoryMock { fn make(&self) -> Box { if self.make_results.borrow().len() == 0 { @@ -415,6 +412,12 @@ pub struct PendingPayableDaoFactoryMock { make_results: RefCell>>, } +impl Default for PendingPayableDaoFactoryMock { + fn default() -> Self { + Self::new() + } +} + impl PendingPayableDaoFactory for PendingPayableDaoFactoryMock { fn make(&self) -> Box { if self.make_results.borrow().len() == 0 { @@ -457,6 +460,12 @@ pub struct ReceivableDaoFactoryMock { make_results: RefCell>>, } +impl Default for ReceivableDaoFactoryMock { + fn default() -> Self { + Self::new() + } +} + impl ReceivableDaoFactory for ReceivableDaoFactoryMock { fn make(&self) -> Box { if self.make_results.borrow().len() == 0 { @@ -1100,7 +1109,7 @@ impl PayableScannerBuilder { pending_payable_dao: PendingPayableDaoMock::new(), payment_thresholds: PaymentThresholds::default(), payable_inspector: PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), + PayableThresholdsGaugeMock::default(), )), payment_adjuster: PaymentAdjusterMock::default(), } From fca80718395c7d24c2f97eaa3272bb8cd7e2c889 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sun, 29 Dec 2024 01:13:49 +0100 Subject: [PATCH 236/250] GH-711: this is almost the last batch out of the comments in the review --- node/src/accountant/mod.rs | 5 +- .../balance_calculator.rs | 2 +- .../disqualification_arbiter.rs | 2 +- .../miscellaneous/helper_functions.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 24 +- .../payment_adjuster/non_unit_tests/mod.rs | 8 +- .../preparatory_analyser/mod.rs | 2 +- .../payment_adjuster/service_fee_adjuster.rs | 2 +- .../accountant/payment_adjuster/test_utils.rs | 569 +++++++++--------- node/src/accountant/test_utils.rs | 43 +- 10 files changed, 341 insertions(+), 318 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f59901386..ecb481413 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1080,6 +1080,7 @@ mod tests { }; use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; + use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, @@ -1148,7 +1149,6 @@ mod tests { use masq_lib::ui_gateway::{ MessageBody, MessagePath, MessageTarget, NodeFromUiMessage, NodeToUiMessage, }; - use masq_lib::utils::convert_collection; use std::any::TypeId; use std::ops::{Add, Sub}; use std::sync::Arc; @@ -1622,7 +1622,8 @@ mod tests { agent: Box::new(agent), response_skeleton_opt: Some(response_skeleton), }; - let analyzed_accounts = convert_collection(unadjusted_qualified_accounts.clone()); + let analyzed_accounts = + convert_qualified_into_analyzed_payables_in_test(unadjusted_qualified_accounts.clone()); let adjustment_analysis = AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts.clone()); let payment_adjuster = PaymentAdjusterMock::default() diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index b812a5894..f1dcc05c2 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -30,7 +30,7 @@ mod tests { use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; - use crate::accountant::payment_adjuster::test_utils::multiply_by_billion; + use crate::accountant::payment_adjuster::test_utils::local_utils::multiply_by_billion; use crate::accountant::test_utils::make_meaningless_analyzed_account; use std::time::SystemTime; diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 51746fab6..47efcf874 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -212,7 +212,7 @@ mod tests { DisqualificationSuspectedAccount, }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; - use crate::accountant::payment_adjuster::test_utils::{ + use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_meaningless_weighed_account, make_non_guaranteed_unconfirmed_adjustment, }; use itertools::Itertools; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index 8f680e1e8..c372d5108 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -191,7 +191,7 @@ mod tests { exhaust_cw_balance_entirely, find_largest_exceeding_balance, no_affordable_accounts_found, ConsumingWalletExhaustingStatus, }; - use crate::accountant::payment_adjuster::test_utils::make_meaningless_weighed_account; + use crate::accountant::payment_adjuster::test_utils::local_utils::make_meaningless_weighed_account; use crate::accountant::test_utils::{make_meaningless_analyzed_account, make_payable_account}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 3d3d884a8..d5ab4a699 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -10,8 +10,10 @@ mod miscellaneous; mod non_unit_tests; mod preparatory_analyser; mod service_fee_adjuster; +// Intentionally public #[cfg(test)] -mod test_utils; +pub mod test_utils; + use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::criterion_calculators::balance_calculator::BalanceCriterionCalculator; use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; @@ -564,7 +566,8 @@ mod tests { find_largest_exceeding_balance, sum_as, }; use crate::accountant::payment_adjuster::service_fee_adjuster::test_helpers::illustrate_why_we_need_to_prevent_exceeding_the_original_value; - use crate::accountant::payment_adjuster::test_utils::{ + use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; + use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, multiply_by_billion_concise, multiply_by_quintillion, multiply_by_quintillion_concise, CriterionCalculatorMock, PaymentAdjusterBuilder, ServiceFeeAdjusterMock, @@ -592,7 +595,6 @@ mod tests { use itertools::{Either, Itertools}; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use masq_lib::utils::convert_collection; use std::collections::HashMap; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; @@ -703,7 +705,8 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); - let analyzed_payables = convert_collection(qualified_payables); + let analyzed_payables = + convert_qualified_into_analyzed_payables_in_test(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -746,7 +749,8 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); - let analyzed_payables = convert_collection(qualified_payables); + let analyzed_payables = + convert_qualified_into_analyzed_payables_in_test(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -824,8 +828,8 @@ mod tests { }); let (qualified_payables, boxed_agent) = make_input_for_initial_check_tests(service_fee_balances_config_opt, None); - let analyzed_accounts: Vec = - convert_collection(qualified_payables.clone()); + let analyzed_accounts = + convert_qualified_into_analyzed_payables_in_test(qualified_payables.clone()); let minimal_disqualification_limit = analyzed_accounts .iter() .map(|account| account.disqualification_limit_minor) @@ -2004,7 +2008,8 @@ mod tests { ) }) .collect(); - let analyzed_accounts: Vec = convert_collection(qualified_payables); + let analyzed_accounts = + convert_qualified_into_analyzed_payables_in_test(qualified_payables); let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); (analyzed_accounts, disqualification_limits) @@ -2450,7 +2455,8 @@ mod tests { now: SystemTime, cw_service_fee_balance_minor: u128, ) -> Vec { - let analyzed_payables = convert_collection(qualified_payables); + let analyzed_payables = + convert_qualified_into_analyzed_payables_in_test(qualified_payables); let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index da2480e88..8f17d4bee 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -6,7 +6,8 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::BalanceProvidingAccount; -use crate::accountant::payment_adjuster::test_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; +use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; +use crate::accountant::payment_adjuster::test_utils::local_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, @@ -27,7 +28,6 @@ use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; -use masq_lib::utils::convert_collection; use rand; use rand::distributions::uniform::SampleUniform; use rand::rngs::ThreadRng; @@ -42,7 +42,7 @@ use web3::types::U256; #[test] // TODO If an option for "occasional tests" is added, this is a good adept -//#[ignore] +#[ignore] fn loading_test_with_randomized_params() { // This is a fuzz test. It generates possibly an overwhelming amount of scenarios that // the PaymentAdjuster could be given sort them out, as realistic as it can get, while its @@ -219,7 +219,7 @@ fn try_making_single_valid_scenario( let (cw_service_fee_balance, qualified_payables, applied_thresholds) = try_generating_qualified_payables_and_cw_balance(gn, accounts_count, now)?; - let analyzed_accounts: Vec = convert_collection(qualified_payables); + let analyzed_accounts = convert_qualified_into_analyzed_payables_in_test(qualified_payables); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, analyzed_accounts.len()); let prepared_adjustment = PreparedAdjustment::new( diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 3ef3fa2f9..3c0d26797 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -370,7 +370,7 @@ mod tests { EarlyServiceFeeSingleTXErrorFactory, LateServiceFeeSingleTxErrorFactory, PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, }; - use crate::accountant::payment_adjuster::test_utils::{ + use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_meaningless_weighed_account, multiply_by_billion, multiply_by_billion_concise, DisqualificationGaugeMock, }; diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 6b42e1a0d..a5e5d2984 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -200,7 +200,7 @@ fn compute_proportional_cw_fragment( mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; - use crate::accountant::payment_adjuster::test_utils::{ + use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_non_guaranteed_unconfirmed_adjustment, multiply_by_quintillion, multiply_by_quintillion_concise, }; diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index f0ff6a89e..8b1e6100c 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -2,325 +2,346 @@ #![cfg(test)] -use crate::accountant::db_access_objects::payable_dao::PayableAccount; -use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; -use crate::accountant::payment_adjuster::disqualification_arbiter::{ - DisqualificationArbiter, DisqualificationGauge, -}; -use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, UnconfirmedAdjustment, WeighedPayable, -}; -use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; -use crate::accountant::payment_adjuster::PaymentAdjusterReal; -use crate::accountant::test_utils::{ - make_meaningless_analyzed_account, make_meaningless_qualified_payable, -}; -use crate::accountant::{gwei_to_wei, AnalyzedPayableAccount, QualifiedPayableAccount}; -use crate::sub_lib::accountant::PaymentThresholds; -use crate::test_utils::make_wallet; -use itertools::Either; -use lazy_static::lazy_static; -use masq_lib::constants::MASQ_TOTAL_SUPPLY; -use masq_lib::logger::Logger; -use std::cell::RefCell; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, SystemTime}; - -lazy_static! { - pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = - multiply_by_quintillion(MASQ_TOTAL_SUPPLY as u128); - pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; -} +// This basically says: visible only within the PaymentAdjuster module +pub(super) mod local_utils { + use crate::accountant::db_access_objects::payable_dao::PayableAccount; + use crate::accountant::payment_adjuster::criterion_calculators::CriterionCalculator; + use crate::accountant::payment_adjuster::disqualification_arbiter::{ + DisqualificationArbiter, DisqualificationGauge, + }; + use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AdjustmentIterationResult, UnconfirmedAdjustment, WeighedPayable, + }; + use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjuster; + use crate::accountant::payment_adjuster::PaymentAdjusterReal; + use crate::accountant::test_utils::{ + make_meaningless_analyzed_account, make_meaningless_qualified_payable, + }; + use crate::accountant::{gwei_to_wei, AnalyzedPayableAccount, QualifiedPayableAccount}; + use crate::sub_lib::accountant::PaymentThresholds; + use crate::test_utils::make_wallet; + use itertools::Either; + use lazy_static::lazy_static; + use masq_lib::constants::MASQ_TOTAL_SUPPLY; + use masq_lib::logger::Logger; + use std::cell::RefCell; + use std::sync::{Arc, Mutex}; + use std::time::{Duration, SystemTime}; + + lazy_static! { + pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 = + multiply_by_quintillion(MASQ_TOTAL_SUPPLY as u128); + pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60; + } -#[derive(Default)] -pub struct PaymentAdjusterBuilder { - start_with_inner_null: bool, - now_opt: Option, - cw_service_fee_balance_minor_opt: Option, - mock_replacing_calculators_opt: Option, - max_debt_above_threshold_in_qualified_payables_minor_opt: Option, - transaction_limit_count_opt: Option, - logger_opt: Option, -} + #[derive(Default)] + pub struct PaymentAdjusterBuilder { + start_with_inner_null: bool, + now_opt: Option, + cw_service_fee_balance_minor_opt: Option, + mock_replacing_calculators_opt: Option, + max_debt_above_threshold_in_qualified_payables_minor_opt: Option, + transaction_limit_count_opt: Option, + logger_opt: Option, + } -impl PaymentAdjusterBuilder { - pub fn build(self) -> PaymentAdjusterReal { - let mut payment_adjuster = PaymentAdjusterReal::default(); - let logger = self.logger_opt.unwrap_or(Logger::new("test")); - payment_adjuster.logger = logger; - if !self.start_with_inner_null { - payment_adjuster.inner.initialize_guts( - self.transaction_limit_count_opt, - self.cw_service_fee_balance_minor_opt.unwrap_or(0), - self.max_debt_above_threshold_in_qualified_payables_minor_opt - .unwrap_or(0), - self.now_opt.unwrap_or(SystemTime::now()), - ); + impl PaymentAdjusterBuilder { + pub fn build(self) -> PaymentAdjusterReal { + let mut payment_adjuster = PaymentAdjusterReal::default(); + let logger = self.logger_opt.unwrap_or(Logger::new("test")); + payment_adjuster.logger = logger; + if !self.start_with_inner_null { + payment_adjuster.inner.initialize_guts( + self.transaction_limit_count_opt, + self.cw_service_fee_balance_minor_opt.unwrap_or(0), + self.max_debt_above_threshold_in_qualified_payables_minor_opt + .unwrap_or(0), + self.now_opt.unwrap_or(SystemTime::now()), + ); + } + if let Some(calculator) = self.mock_replacing_calculators_opt { + payment_adjuster.calculators = vec![Box::new(calculator)] + } + payment_adjuster } - if let Some(calculator) = self.mock_replacing_calculators_opt { - payment_adjuster.calculators = vec![Box::new(calculator)] + + pub fn start_with_inner_null(mut self) -> Self { + self.start_with_inner_null = true; + self } - payment_adjuster - } - pub fn start_with_inner_null(mut self) -> Self { - self.start_with_inner_null = true; - self - } + pub fn replace_calculators_with_mock( + mut self, + calculator_mock: CriterionCalculatorMock, + ) -> Self { + self.mock_replacing_calculators_opt = Some(calculator_mock); + self + } - pub fn replace_calculators_with_mock( - mut self, - calculator_mock: CriterionCalculatorMock, - ) -> Self { - self.mock_replacing_calculators_opt = Some(calculator_mock); - self - } + pub fn cw_service_fee_balance_minor(mut self, cw_service_fee_balance_minor: u128) -> Self { + self.cw_service_fee_balance_minor_opt = Some(cw_service_fee_balance_minor); + self + } - pub fn cw_service_fee_balance_minor(mut self, cw_service_fee_balance_minor: u128) -> Self { - self.cw_service_fee_balance_minor_opt = Some(cw_service_fee_balance_minor); - self - } + pub fn max_debt_above_threshold_in_qualified_payables_minor( + mut self, + max_exceeding_part_of_debt: u128, + ) -> Self { + self.max_debt_above_threshold_in_qualified_payables_minor_opt = + Some(max_exceeding_part_of_debt); + self + } - pub fn max_debt_above_threshold_in_qualified_payables_minor( - mut self, - max_exceeding_part_of_debt: u128, - ) -> Self { - self.max_debt_above_threshold_in_qualified_payables_minor_opt = - Some(max_exceeding_part_of_debt); - self - } + pub fn now(mut self, now: SystemTime) -> Self { + self.now_opt = Some(now); + self + } - pub fn now(mut self, now: SystemTime) -> Self { - self.now_opt = Some(now); - self - } + pub fn logger(mut self, logger: Logger) -> Self { + self.logger_opt = Some(logger); + self + } - pub fn logger(mut self, logger: Logger) -> Self { - self.logger_opt = Some(logger); - self + #[allow(dead_code)] + pub fn transaction_limit_count(mut self, tx_limit: u16) -> Self { + self.transaction_limit_count_opt = Some(tx_limit); + self + } } - #[allow(dead_code)] - pub fn transaction_limit_count(mut self, tx_limit: u16) -> Self { - self.transaction_limit_count_opt = Some(tx_limit); - self + pub fn make_mammoth_payables( + months_of_debt_and_balance_minor: Either<(Vec, u128), Vec<(usize, u128)>>, + now: SystemTime, + ) -> Vec { + // What is a mammoth like? Prehistoric, giant, and impossible to meet. Exactly as these payables. + let accounts_seeds: Vec<(usize, u128)> = match months_of_debt_and_balance_minor { + Either::Left((vec_of_months, constant_balance)) => vec_of_months + .into_iter() + .map(|months| (months, constant_balance)) + .collect(), + Either::Right(specific_months_and_specific_balance) => { + specific_months_and_specific_balance + } + }; + accounts_seeds + .into_iter() + .enumerate() + .map(|(idx, (months_count, balance_minor))| PayableAccount { + wallet: make_wallet(&format!("blah{}", idx)), + balance_wei: balance_minor, + last_paid_timestamp: now + .checked_sub(Duration::from_secs( + months_count as u64 * (*ONE_MONTH_LONG_DEBT_SEC), + )) + .unwrap(), + pending_payable_opt: None, + }) + .collect() } -} -pub fn make_mammoth_payables( - months_of_debt_and_balance_minor: Either<(Vec, u128), Vec<(usize, u128)>>, - now: SystemTime, -) -> Vec { - // What is a mammoth like? Prehistoric, giant, and impossible to meet. Exactly as these payables. - let accounts_seeds: Vec<(usize, u128)> = match months_of_debt_and_balance_minor { - Either::Left((vec_of_months, constant_balance)) => vec_of_months - .into_iter() - .map(|months| (months, constant_balance)) - .collect(), - Either::Right(specific_months_and_specific_balance) => specific_months_and_specific_balance, + pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHOLDS: + PaymentThresholds = PaymentThresholds { + debt_threshold_gwei: 2_000_000, + maturity_threshold_sec: 1_000, + payment_grace_period_sec: 1_000, + permanent_debt_allowed_gwei: 1_000_000, + threshold_interval_sec: 500_000, + unban_below_gwei: 1_000_000, }; - accounts_seeds - .into_iter() - .enumerate() - .map(|(idx, (months_count, balance_minor))| PayableAccount { - wallet: make_wallet(&format!("blah{}", idx)), - balance_wei: balance_minor, - last_paid_timestamp: now - .checked_sub(Duration::from_secs( - months_count as u64 * (*ONE_MONTH_LONG_DEBT_SEC), - )) - .unwrap(), - pending_payable_opt: None, - }) - .collect() -} -pub(in crate::accountant::payment_adjuster) const PRESERVED_TEST_PAYMENT_THRESHOLDS: - PaymentThresholds = PaymentThresholds { - debt_threshold_gwei: 2_000_000, - maturity_threshold_sec: 1_000, - payment_grace_period_sec: 1_000, - permanent_debt_allowed_gwei: 1_000_000, - threshold_interval_sec: 500_000, - unban_below_gwei: 1_000_000, -}; - -pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { - let qualified_account = make_meaningless_qualified_payable(n); - let account_balance = qualified_account.bare_account.balance_wei; - let proposed_adjusted_balance_minor = (2 * account_balance) / 3; - let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; - let analyzed_account = - AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); - let weight = multiply_by_billion(n as u128); - UnconfirmedAdjustment::new( - WeighedPayable::new(analyzed_account, weight), - proposed_adjusted_balance_minor, - ) -} + pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { + let qualified_account = make_meaningless_qualified_payable(n); + let account_balance = qualified_account.bare_account.balance_wei; + let proposed_adjusted_balance_minor = (2 * account_balance) / 3; + let disqualification_limit_minor = (3 * proposed_adjusted_balance_minor) / 4; + let analyzed_account = + AnalyzedPayableAccount::new(qualified_account, disqualification_limit_minor); + let weight = multiply_by_billion(n as u128); + UnconfirmedAdjustment::new( + WeighedPayable::new(analyzed_account, weight), + proposed_adjusted_balance_minor, + ) + } -#[derive(Default)] -pub struct CriterionCalculatorMock { - calculate_params: Arc>>, - calculate_results: RefCell>, -} + #[derive(Default)] + pub struct CriterionCalculatorMock { + calculate_params: Arc>>, + calculate_results: RefCell>, + } -impl CriterionCalculator for CriterionCalculatorMock { - fn calculate( - &self, - account: &QualifiedPayableAccount, - _context: &PaymentAdjusterInner, - ) -> u128 { - self.calculate_params.lock().unwrap().push(account.clone()); - self.calculate_results.borrow_mut().remove(0) + impl CriterionCalculator for CriterionCalculatorMock { + fn calculate( + &self, + account: &QualifiedPayableAccount, + _context: &PaymentAdjusterInner, + ) -> u128 { + self.calculate_params.lock().unwrap().push(account.clone()); + self.calculate_results.borrow_mut().remove(0) + } + + fn parameter_name(&self) -> &'static str { + "MOCKED CALCULATOR" + } } - fn parameter_name(&self) -> &'static str { - "MOCKED CALCULATOR" + impl CriterionCalculatorMock { + pub fn calculate_params( + mut self, + params: &Arc>>, + ) -> Self { + self.calculate_params = params.clone(); + self + } + pub fn calculate_result(self, result: u128) -> Self { + self.calculate_results.borrow_mut().push(result); + self + } } -} -impl CriterionCalculatorMock { - pub fn calculate_params(mut self, params: &Arc>>) -> Self { - self.calculate_params = params.clone(); - self + #[derive(Default)] + pub struct DisqualificationGaugeMock { + determine_limit_params: Arc>>, + determine_limit_results: RefCell>, } - pub fn calculate_result(self, result: u128) -> Self { - self.calculate_results.borrow_mut().push(result); - self + + impl DisqualificationGauge for DisqualificationGaugeMock { + fn determine_limit( + &self, + account_balance_wei: u128, + threshold_intercept_wei: u128, + permanent_debt_allowed_wei: u128, + ) -> u128 { + self.determine_limit_params.lock().unwrap().push(( + account_balance_wei, + threshold_intercept_wei, + permanent_debt_allowed_wei, + )); + self.determine_limit_results.borrow_mut().remove(0) + } } -} -#[derive(Default)] -pub struct DisqualificationGaugeMock { - determine_limit_params: Arc>>, - determine_limit_results: RefCell>, -} + impl DisqualificationGaugeMock { + pub fn determine_limit_params( + mut self, + params: &Arc>>, + ) -> Self { + self.determine_limit_params = params.clone(); + self + } -impl DisqualificationGauge for DisqualificationGaugeMock { - fn determine_limit( - &self, - account_balance_wei: u128, - threshold_intercept_wei: u128, - permanent_debt_allowed_wei: u128, - ) -> u128 { - self.determine_limit_params.lock().unwrap().push(( - account_balance_wei, - threshold_intercept_wei, - permanent_debt_allowed_wei, - )); - self.determine_limit_results.borrow_mut().remove(0) + pub fn determine_limit_result(self, result: u128) -> Self { + self.determine_limit_results.borrow_mut().push(result); + self + } } -} -impl DisqualificationGaugeMock { - pub fn determine_limit_params(mut self, params: &Arc>>) -> Self { - self.determine_limit_params = params.clone(); - self + #[derive(Default)] + pub struct ServiceFeeAdjusterMock { + perform_adjustment_by_service_fee_params: Arc, u128)>>>, + perform_adjustment_by_service_fee_results: RefCell>, } - pub fn determine_limit_result(self, result: u128) -> Self { - self.determine_limit_results.borrow_mut().push(result); - self + impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { + fn perform_adjustment_by_service_fee( + &self, + weighed_accounts: Vec, + _disqualification_arbiter: &DisqualificationArbiter, + unallocated_cw_service_fee_balance_minor: u128, + _logger: &Logger, + ) -> AdjustmentIterationResult { + self.perform_adjustment_by_service_fee_params + .lock() + .unwrap() + .push((weighed_accounts, unallocated_cw_service_fee_balance_minor)); + self.perform_adjustment_by_service_fee_results + .borrow_mut() + .remove(0) + } } -} -#[derive(Default)] -pub struct ServiceFeeAdjusterMock { - perform_adjustment_by_service_fee_params: Arc, u128)>>>, - perform_adjustment_by_service_fee_results: RefCell>, -} -impl ServiceFeeAdjuster for ServiceFeeAdjusterMock { - fn perform_adjustment_by_service_fee( - &self, - weighed_accounts: Vec, - _disqualification_arbiter: &DisqualificationArbiter, - unallocated_cw_service_fee_balance_minor: u128, - _logger: &Logger, - ) -> AdjustmentIterationResult { - self.perform_adjustment_by_service_fee_params - .lock() - .unwrap() - .push((weighed_accounts, unallocated_cw_service_fee_balance_minor)); - self.perform_adjustment_by_service_fee_results - .borrow_mut() - .remove(0) - } -} + impl ServiceFeeAdjusterMock { + pub fn perform_adjustment_by_service_fee_params( + mut self, + params: &Arc, u128)>>>, + ) -> Self { + self.perform_adjustment_by_service_fee_params = params.clone(); + self + } -impl ServiceFeeAdjusterMock { - pub fn perform_adjustment_by_service_fee_params( - mut self, - params: &Arc, u128)>>>, - ) -> Self { - self.perform_adjustment_by_service_fee_params = params.clone(); - self + pub fn perform_adjustment_by_service_fee_result( + self, + result: AdjustmentIterationResult, + ) -> Self { + self.perform_adjustment_by_service_fee_results + .borrow_mut() + .push(result); + self + } } - pub fn perform_adjustment_by_service_fee_result( - self, - result: AdjustmentIterationResult, - ) -> Self { - self.perform_adjustment_by_service_fee_results - .borrow_mut() - .push(result); - self + // = 1 gwei + pub fn multiply_by_billion(num: u128) -> u128 { + gwei_to_wei(num) } -} -// = 1 gwei -pub fn multiply_by_billion(num: u128) -> u128 { - gwei_to_wei(num) -} - -// = 1 MASQ -pub fn multiply_by_quintillion(num: u128) -> u128 { - multiply_by_billion(multiply_by_billion(num)) -} + // = 1 MASQ + pub fn multiply_by_quintillion(num: u128) -> u128 { + multiply_by_billion(multiply_by_billion(num)) + } -// = 1 gwei -pub fn multiply_by_billion_concise(num: f64) -> u128 { - multiple_by(num, 9, "billion") -} + // = 1 gwei + pub fn multiply_by_billion_concise(num: f64) -> u128 { + multiple_by(num, 9, "billion") + } -// = 1 MASQ -pub fn multiply_by_quintillion_concise(num: f64) -> u128 { - multiple_by(num, 18, "quintillion") -} + // = 1 MASQ + pub fn multiply_by_quintillion_concise(num: f64) -> u128 { + multiple_by(num, 18, "quintillion") + } -fn multiple_by( - num_in_concise_form: f64, - desired_increase_in_magnitude: usize, - mathematical_name: &str, -) -> u128 { - if (num_in_concise_form * 1000.0).fract() != 0.0 { - panic!("Multiplying by {mathematical_name}: It's allowed only when applied on numbers with three \ + fn multiple_by( + num_in_concise_form: f64, + desired_increase_in_magnitude: usize, + mathematical_name: &str, + ) -> u128 { + if (num_in_concise_form * 1000.0).fract() != 0.0 { + panic!("Multiplying by {mathematical_name}: It's allowed only when applied on numbers with three \ digits after the decimal point at maximum!") + } + let significant_digits = (num_in_concise_form * 1000.0) as u128; + significant_digits * 10_u128.pow(desired_increase_in_magnitude as u32 - 3) } - let significant_digits = (num_in_concise_form * 1000.0) as u128; - significant_digits * 10_u128.pow(desired_increase_in_magnitude as u32 - 3) -} -pub fn make_meaningless_analyzed_account_by_wallet( - wallet_address_segment: &str, -) -> AnalyzedPayableAccount { - let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); - let wallet = make_wallet(wallet_address_segment); - let mut account = make_meaningless_analyzed_account(num); - account.qualified_as.bare_account.wallet = wallet; - account -} + pub fn make_meaningless_analyzed_account_by_wallet( + wallet_address_segment: &str, + ) -> AnalyzedPayableAccount { + let num = u64::from_str_radix(wallet_address_segment, 16).unwrap(); + let wallet = make_wallet(wallet_address_segment); + let mut account = make_meaningless_analyzed_account(num); + account.qualified_as.bare_account.wallet = wallet; + account + } -pub fn make_meaningless_weighed_account(n: u64) -> WeighedPayable { - WeighedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) + pub fn make_meaningless_weighed_account(n: u64) -> WeighedPayable { + WeighedPayable::new(make_meaningless_analyzed_account(n), 123456 * n as u128) + } } -// Should stay test only!! -impl From for AnalyzedPayableAccount { - fn from(qualified_account: QualifiedPayableAccount) -> Self { - let disqualification_limit = - DisqualificationArbiter::default().calculate_disqualification_edge(&qualified_account); - AnalyzedPayableAccount::new(qualified_account, disqualification_limit) +pub mod exposed_utils { + use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; + use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; + + pub fn convert_qualified_into_analyzed_payables_in_test( + qualified_account: Vec, + ) -> Vec { + qualified_account + .into_iter() + .map(|account| { + let disqualification_limit = + DisqualificationArbiter::default().calculate_disqualification_edge(&account); + AnalyzedPayableAccount::new(account, disqualification_limit) + }) + .collect() } } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 061620eb9..399e951b0 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -13,8 +13,9 @@ use crate::accountant::db_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; +use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; use crate::accountant::payment_adjuster::{ - AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, + AdjustmentAnalysisResult, PaymentAdjuster, PaymentAdjusterError, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::msgs::{ @@ -56,7 +57,6 @@ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::messages::ScanType; use masq_lib::ui_gateway::NodeToUiMessage; -use masq_lib::utils::convert_collection; use rusqlite::{Connection, OpenFlags, Row}; use std::any::type_name; use std::cell::RefCell; @@ -1469,14 +1469,7 @@ pub fn trick_rusqlite_with_read_only_conn( #[derive(Default)] pub struct PaymentAdjusterMock { consider_adjustment_params: Arc, ArbitraryIdStamp)>>>, - consider_adjustment_results: RefCell< - Vec< - Result< - Either, AdjustmentAnalysisReport>, - PaymentAdjusterError, - >, - >, - >, + consider_adjustment_results: RefCell>, adjust_payments_params: Arc>>, adjust_payments_results: RefCell>>, @@ -1487,8 +1480,7 @@ impl PaymentAdjuster for PaymentAdjusterMock { &self, qualified_payables: Vec, agent: &dyn BlockchainAgent, - ) -> Result, AdjustmentAnalysisReport>, PaymentAdjusterError> - { + ) -> AdjustmentAnalysisResult { self.consider_adjustment_params .lock() .unwrap() @@ -1518,13 +1510,7 @@ impl PaymentAdjusterMock { self } - pub fn consider_adjustment_result( - self, - result: Result< - Either, AdjustmentAnalysisReport>, - PaymentAdjusterError, - >, - ) -> Self { + pub fn consider_adjustment_result(self, result: AdjustmentAnalysisResult) -> Self { self.consider_adjustment_results.borrow_mut().push(result); self } @@ -1746,16 +1732,21 @@ impl ScanSchedulers { } pub fn make_meaningless_qualified_payable(n: u64) -> QualifiedPayableAccount { - // Without guarantee that the generated payable would cross the given thresholds + // It's not guaranteed that the payables would cross the given thresholds. + let coefficient = (n as f64).sqrt().floor() as u64; + let permanent_deb_allowed_minor = gwei_to_wei(coefficient); + let payment_threshold_intercept = 7_u128 * gwei_to_wei::(n) / 10_u128; QualifiedPayableAccount::new( make_payable_account(n), - n as u128 * 12345, - CreditorThresholds::new(111_111_111), + payment_threshold_intercept, + CreditorThresholds::new(permanent_deb_allowed_minor), ) } pub fn make_meaningless_analyzed_account(n: u64) -> AnalyzedPayableAccount { - AnalyzedPayableAccount::new(make_meaningless_qualified_payable(n), 123456789) + let qualified_account = make_meaningless_qualified_payable(n); + let disqualification_limit = 85 * qualified_account.payment_threshold_intercept_minor / 100; + AnalyzedPayableAccount::new(qualified_account, disqualification_limit) } pub fn make_qualified_payables( @@ -1771,7 +1762,11 @@ pub fn make_analyzed_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - convert_collection(make_qualified_payables(payables, payment_thresholds, now)) + convert_qualified_into_analyzed_payables_in_test(make_qualified_payables( + payables, + payment_thresholds, + now, + )) } pub fn try_to_make_guaranteed_qualified_payables( From a75bfe30b5c3dfdd4a29f459666a93fe5da1b999 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:45:44 +0100 Subject: [PATCH 237/250] GH-711: last case of repair before wallet -> address --- node/src/accountant/mod.rs | 8 +++----- .../scanners/mid_scan_msg_handling/payable_scanner/mod.rs | 2 +- node/src/accountant/scanners/mod.rs | 2 +- node/src/accountant/test_utils.rs | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index ecb481413..a785c15a7 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -720,7 +720,7 @@ impl Accountant { &mut self, msg: BlockchainAgentWithContextMessage, ) -> Option { - let analysed_without_an_error = match self + let successfully_processed = match self .scanners .payable .try_skipping_payment_adjustment(msg, &self.logger) @@ -729,7 +729,7 @@ impl Accountant { None => return None, }; - match analysed_without_an_error { + match successfully_processed { Either::Left(prepared_msg_with_unadjusted_payables) => { Some(prepared_msg_with_unadjusted_payables) } @@ -745,9 +745,7 @@ impl Accountant { } fn handle_obstruction(&mut self, response_skeleton_opt: Option) { - self.scanners - .payable - .scan_canceled_by_payment_instructor(&self.logger); + self.scanners.payable.cancel_scan(&self.logger); if let Some(response_skeleton) = response_skeleton_opt { self.ui_message_sub_opt diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs index e0488eea5..95ac9240c 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/mod.rs @@ -37,7 +37,7 @@ pub trait SolvencySensitivePaymentInstructor { logger: &Logger, ) -> Option; - fn scan_canceled_by_payment_instructor(&mut self, logger: &Logger); + fn cancel_scan(&mut self, logger: &Logger); } pub struct PreparedAdjustment { diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index bdb84678e..1124299bd 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -328,7 +328,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { } } - fn scan_canceled_by_payment_instructor(&mut self, logger: &Logger) { + fn cancel_scan(&mut self, logger: &Logger) { error!( logger, "Payable scanner is blocked from preparing instructions for payments. The cause appears \ diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 399e951b0..fad8ec9f9 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1553,7 +1553,7 @@ macro_rules! formal_traits_for_payable_mid_scan_msg_handling { intentionally_blank!() } - fn scan_canceled_by_payment_instructor(&mut self, _logger: &Logger) { + fn cancel_scan(&mut self, _logger: &Logger) { intentionally_blank!() } } From 9aa24b679fb3cee1fdfeb1e96c5df694502a64bf Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 3 Jan 2025 19:14:28 +0100 Subject: [PATCH 238/250] GH-711-review-one: everything in the review addressed --- .../disqualification_arbiter.rs | 99 +++++++++++-- .../logging_and_diagnostics/diagnostics.rs | 12 +- .../logging_and_diagnostics/log_functions.rs | 27 ++-- .../miscellaneous/data_structures.rs | 13 +- node/src/accountant/payment_adjuster/mod.rs | 140 +++++++++--------- .../payment_adjuster/non_unit_tests/mod.rs | 55 +++---- .../preparatory_analyser/mod.rs | 20 +-- .../payment_adjuster/service_fee_adjuster.rs | 17 ++- 8 files changed, 226 insertions(+), 157 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 47efcf874..119b9c0ba 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -1,5 +1,6 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use web3::types::Address; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ account_nominated_for_disqualification_diagnostics, try_finding_an_account_to_disqualify_diagnostics, @@ -7,7 +8,6 @@ use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::o use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::info_log_for_disqualified_account; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::QualifiedPayableAccount; -use crate::sub_lib::wallet::Wallet; use masq_lib::logger::Logger; pub struct DisqualificationArbiter { @@ -45,18 +45,18 @@ impl DisqualificationArbiter { &self, unconfirmed_adjustments: &[UnconfirmedAdjustment], logger: &Logger, - ) -> Wallet { + ) -> Address { let disqualification_suspected_accounts = Self::list_accounts_nominated_for_disqualification(unconfirmed_adjustments); let account_to_disqualify = Self::find_account_with_smallest_weight(&disqualification_suspected_accounts); - let wallet = account_to_disqualify.wallet.clone(); + let wallet = account_to_disqualify.wallet; try_finding_an_account_to_disqualify_diagnostics( &disqualification_suspected_accounts, - &wallet, + wallet, ); debug!( @@ -97,9 +97,9 @@ impl DisqualificationArbiter { .collect() } - fn find_account_with_smallest_weight<'accounts>( - accounts: &'accounts [DisqualificationSuspectedAccount], - ) -> &'accounts DisqualificationSuspectedAccount<'accounts> { + fn find_account_with_smallest_weight( + accounts: &[DisqualificationSuspectedAccount], + ) -> &DisqualificationSuspectedAccount { accounts .iter() .min_by_key(|account| account.weight) @@ -108,8 +108,8 @@ impl DisqualificationArbiter { } #[derive(Debug, PartialEq, Eq)] -pub struct DisqualificationSuspectedAccount<'account> { - pub wallet: &'account Wallet, +pub struct DisqualificationSuspectedAccount { + pub wallet: Address, pub weight: u128, // The rest serves diagnostics and logging pub proposed_adjusted_balance_minor: u128, @@ -118,7 +118,7 @@ pub struct DisqualificationSuspectedAccount<'account> { } impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment> - for DisqualificationSuspectedAccount<'unconfirmed_accounts> + for DisqualificationSuspectedAccount { fn from(unconfirmed_account: &'unconfirmed_accounts UnconfirmedAdjustment) -> Self { DisqualificationSuspectedAccount { @@ -150,8 +150,8 @@ impl DisqualificationGauge for DisqualificationGaugeReal { threshold_intercept_minor: u128, permanent_debt_allowed_minor: u128, ) -> u128 { - // This signs that the debt lies in the horizontal area of the payment thresholds. - // Such are mandatory to be paid in their whole size. + // This signs that the debt lies in the horizontal area of the payment thresholds, and thus + // should be paid in the whole size. if threshold_intercept_minor == permanent_debt_allowed_minor { return account_balance_minor; } @@ -203,6 +203,73 @@ impl DisqualificationGaugeReal { debt_part_over_the_threshold + permanent_debt_allowed_minor } } + + // This schema shows the conditions used to determine the disqualification limit + // (or minimal acceptable payment) + // + // | A + + // | | P -----------+ + // | | P | + // | | P | + // | | P | + // | B | P P -----+ | + // | + P P | | + // | |\ P P X Y + // | | \P P | | + // | | P P -----+ | + // | B'+ P\ P | + // | |\ P \P | + // | | \P P -----+-----+ + // | | U P\ + // | B"+ U\ P \ + // | \ U \P +C + // | \U P |\ + // | U P\ | \ + // | U\ P \| \ P P + // | U \P +C' \ P P + // | U U |\ \P P + // | U U\ | \ P P + // | U U \| \ P\ P + // | U U +C" \ P \ P + // | U U \ \P \ P + // | U U \ U \ D P E + // | U U \ U\ +------------P--------+ + // | U U \ U \ | P + // | U U \U \ | P + // | U U U \|D' P E' + // +---------------------------+---+---------------------+ + // 3 4 2 1 + // + // This diagram presents computation of the disqualification limit which differs by four cases. + // The debt portion illustrated with the use of the letter 'P' stands for the actual limit. + // That is the minimum amount we consider effective to keep us away from a ban for delinquent + // debtors. Beyond that mark, if the debt is bigger, it completes the column with 'U's. This + // part can be forgiven for the time being, until more funds is supplied for the consuming + // wallet. + // + // Points A, B, D, E make up a simple outline of possible payment thresholds. These are + // fundamental statements: The x-axis distance between B and D is "threshold_interval_sec". + // From B vertically down to the x-axis, it amounts to "debt_threshold_gwei". D is as far + // from D' as the size of the "permanent_debt_allowed_gwei" parameter. A few other line + // segments in the diagram are also derived from this last mentioned measurement, like B - B' + // and B' - B". + // + // 1. This debt is ordered entire strictly as well as any other one situated between D and E. + // (Note that the E isn't a real point, the axis goes endless this direction). + // 2. Since we are earlier in the time with debt, a different rule is applied. The limit is + // formed as the part above the threshold, plus an equivalent of the D - D' distance. + // It's notable that we are evaluating a debt older than the timestamp which would appear + // on the x-axis if we prolonged the C - C" line towards it. + // 3. Now we are before that timestamp, however the surplussing debt portion X isn't + // significant enough yet. Therefore the same rule as at No. 2 is applied also here. + // 4. This time we hold the condition for the age not reaching the decisive timestamp and + // the debt becomes sizable, measured as Y, which indicates that it might be linked to + // a Node that we've used extensively (or even that we're using right now). We then prefer + // to increase the margin added to the above-threshold amount, and so we double it. + // If true to the reality, the diagram would have to run much further upwards. That's + // because the condition to consider a debt's size significant says that the part under + // the threshold must be twice (or more) smaller than that above it (Y). + // } #[cfg(test)] @@ -429,7 +496,7 @@ mod tests { .qualified_as .bare_account .wallet - .clone(); + .address(); let mut account_4 = make_meaningless_weighed_account(012); account_4.analyzed_account.disqualification_limit_minor = 1_000_000; account_4.weight = 1001; @@ -471,11 +538,11 @@ mod tests { }) .collect_vec(); assert_eq!(all_wallets.len(), 4); - let wallets_as_wallet_3 = all_wallets + let wallets_same_as_wallet_3 = all_wallets .iter() - .filter(|wallet| wallet == &&&wallet_3) + .filter(|wallet| wallet.address() == wallet_3) .collect_vec(); - assert_eq!(wallets_as_wallet_3.len(), 1); + assert_eq!(wallets_same_as_wallet_3.len(), 1); } fn make_unconfirmed_adjustments(weights: Vec) -> Vec { diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index fc0445308..45d7d1015 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -30,9 +30,9 @@ macro_rules! diagnostics { ) }; // Displays an account by wallet address, brief description and formatted literal with arguments - ($wallet_ref: expr, $description: expr, $($formatted_values: tt)*) => { + ($wallet: expr, $description: expr, $($formatted_values: tt)*) => { diagnostics( - Some(||$wallet_ref.to_string()), + Some(||format!("{:?}", $wallet)), $description, Some(|| format!($($formatted_values)*)) ) @@ -97,8 +97,8 @@ pub mod ordinary_diagnostic_functions { use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ AdjustedAccountBeforeFinalization, UnconfirmedAdjustment, WeighedPayable, }; - use crate::sub_lib::wallet::Wallet; use thousands::Separable; + use web3::types::Address; pub fn thriving_competitor_found_diagnostics( account_info: &UnconfirmedAdjustment, @@ -180,7 +180,7 @@ pub mod ordinary_diagnostic_functions { pub fn try_finding_an_account_to_disqualify_diagnostics( disqualification_suspected_accounts: &[DisqualificationSuspectedAccount], - wallet: &Wallet, + wallet: Address, ) { diagnostics!( "PICKED DISQUALIFIED ACCOUNT", @@ -191,7 +191,7 @@ pub mod ordinary_diagnostic_functions { } pub fn calculated_criterion_and_weight_diagnostics( - wallet_ref: &Wallet, + wallet: Address, calculator: &dyn CriterionCalculator, criterion: u128, added_in_the_sum: u128, @@ -199,7 +199,7 @@ pub mod ordinary_diagnostic_functions { const FIRST_COLUMN_WIDTH: usize = 30; diagnostics!( - wallet_ref, + wallet, "PARTIAL CRITERION CALCULATED", "For {:, + original_account_balances_mapped: HashMap, adjusted_accounts: &[PayableAccount], ) -> String { let excluded_wallets_and_balances = @@ -58,7 +57,7 @@ fn included_accounts_title() -> String { } fn format_summary_for_included_accounts( - original_account_balances_mapped: &HashMap, + original_account_balances_mapped: &HashMap, adjusted_accounts: &[PayableAccount], ) -> String { adjusted_accounts @@ -69,7 +68,7 @@ fn format_summary_for_included_accounts( }) .map(|account| { let original_balance = original_account_balances_mapped - .get(&account.wallet) + .get(&account.wallet.address()) .expectv(""); (account, *original_balance) }) @@ -98,32 +97,32 @@ fn excluded_accounts_title() -> String { ) } -fn preprocess_excluded_accounts<'a>( - original_account_balances_mapped: &'a HashMap, - adjusted_accounts: &'a [PayableAccount], -) -> Vec<(&'a Wallet, u128)> { - let adjusted_accounts_wallets: Vec<&Wallet> = adjusted_accounts +fn preprocess_excluded_accounts( + original_account_balances_mapped: &HashMap, + adjusted_accounts: &[PayableAccount], +) -> Vec<(Address, u128)> { + let adjusted_accounts_wallets: Vec
= adjusted_accounts .iter() - .map(|account| &account.wallet) + .map(|account| account.wallet.address()) .collect(); original_account_balances_mapped .iter() .fold(vec![], |mut acc, (wallet, original_balance)| { if !adjusted_accounts_wallets.contains(&wallet) { - acc.push((wallet, *original_balance)); + acc.push((*wallet, *original_balance)); } acc }) } -fn format_summary_for_excluded_accounts(excluded: &[(&Wallet, u128)]) -> String { +fn format_summary_for_excluded_accounts(excluded: &[(Address, u128)]) -> String { excluded .iter() .sorted_by(|(_, balance_account_a), (_, balance_account_b)| { Ord::cmp(&balance_account_b, &balance_account_a) }) .map(|(wallet, original_balance)| { - format!("{} {}", wallet, original_balance.separate_with_commas()) + format!("{:?} {}", wallet, original_balance.separate_with_commas()) }) .join("\n") } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 23f3d90af..597aaaf61 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -2,8 +2,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::AnalyzedPayableAccount; -use crate::sub_lib::wallet::Wallet; -use web3::types::U256; +use web3::types::{Address, U256}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct WeighedPayable { @@ -19,8 +18,12 @@ impl WeighedPayable { } } - pub fn wallet(&self) -> &Wallet { - &self.analyzed_account.qualified_as.bare_account.wallet + pub fn wallet(&self) -> Address { + self.analyzed_account + .qualified_as + .bare_account + .wallet + .address() } pub fn initial_balance_minor(&self) -> u128 { @@ -69,7 +72,7 @@ impl UnconfirmedAdjustment { } } - pub fn wallet(&self) -> &Wallet { + pub fn wallet(&self) -> Address { self.weighed_account.wallet() } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d5ab4a699..b5d430ea8 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -43,7 +43,6 @@ use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::Prepare use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::diagnostics; use crate::sub_lib::blockchain_bridge::OutboundPaymentsInstructions; -use crate::sub_lib::wallet::Wallet; use itertools::Either; use masq_lib::logger::Logger; use std::collections::HashMap; @@ -51,9 +50,10 @@ use std::fmt::{Display, Formatter}; use std::time::SystemTime; use thousands::Separable; use variant_count::VariantCount; -use web3::types::U256; +use web3::types::{Address, U256}; use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; + // PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions // of an acute insolvency. You can easily expand the range of evaluated parameters to determine // an optimized allocation of scarce assets by writing your own CriterionCalculator. The calculator @@ -164,8 +164,7 @@ impl PaymentAdjusterReal { &self, required_adjustment: Adjustment, initial_service_fee_balance_minor: u128, - //TODO use 'of qualified payables' instead of 'in' - max_debt_above_threshold_in_qualified_payables_minor_minor: u128, + max_debt_above_threshold_in_qualified_payables_minor: u128, now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { @@ -178,7 +177,7 @@ impl PaymentAdjusterReal { self.inner.initialize_guts( transaction_fee_limitation_opt, initial_service_fee_balance_minor, - max_debt_above_threshold_in_qualified_payables_minor_minor, + max_debt_above_threshold_in_qualified_payables_minor, now, ) } @@ -359,7 +358,7 @@ impl PaymentAdjusterReal { let summed_up = weight + new_criterion; calculated_criterion_and_weight_diagnostics( - &payable.qualified_as.bare_account.wallet, + payable.qualified_as.bare_account.wallet.address(), criterion_calculator.as_ref(), new_criterion, summed_up, @@ -396,23 +395,23 @@ impl PaymentAdjusterReal { fn sketch_debug_log_opt( &self, qualified_payables: &[AnalyzedPayableAccount], - ) -> Option> { + ) -> Option> { self.logger.debug_enabled().then(|| { qualified_payables .iter() .map(|payable| { ( - payable.qualified_as.bare_account.wallet.clone(), + payable.qualified_as.bare_account.wallet.address(), payable.qualified_as.bare_account.balance_wei, ) }) - .collect::>() + .collect() }) } fn complete_debug_log_if_enabled( &self, - sketched_debug_info_opt: Option>, + sketched_debug_info_opt: Option>, fully_processed_accounts: &[PayableAccount], ) { self.logger.debug(|| { @@ -446,15 +445,15 @@ impl AdjustmentAnalysisReport { #[derive(Debug, PartialEq, Eq, VariantCount)] pub enum PaymentAdjusterError { - EarlyNotEnoughFeeForSingleTransaction { + AbsolutelyInsufficientBalance { number_of_accounts: usize, transaction_fee_opt: Option, service_fee_opt: Option, }, - LateNotEnoughFeeForSingleTransaction { + AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts: usize, number_of_accounts: usize, - original_service_fee_required_total_minor: u128, + original_total_service_fee_required_minor: u128, cw_service_fee_balance_minor: u128, }, RecursionDrainedAllAccounts, @@ -475,8 +474,10 @@ pub struct ServiceFeeImmoderateInsufficiency { impl PaymentAdjusterError { pub fn insolvency_detected(&self) -> bool { match self { - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => true, - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => true, + PaymentAdjusterError::AbsolutelyInsufficientBalance { .. } => true, + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { + .. + } => true, PaymentAdjusterError::RecursionDrainedAllAccounts => true, // We haven't needed to worry in this matter yet, this is rather a future alarm that // will draw attention after somebody adds a possibility for an error not necessarily @@ -491,7 +492,7 @@ impl PaymentAdjusterError { impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts, transaction_fee_opt, service_fee_opt, @@ -531,10 +532,10 @@ impl Display for PaymentAdjusterError { (None, None) => unreachable!("This error contains no specifications") } }, - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts, number_of_accounts, - original_service_fee_required_total_minor, + original_total_service_fee_required_minor, cw_service_fee_balance_minor, } => write!(f, "The original set with {} accounts was adjusted down to {} due to \ transaction fee. The new set was tested on service fee later again and did not \ @@ -542,7 +543,7 @@ impl Display for PaymentAdjusterError { contains {} wei.", original_number_of_accounts, number_of_accounts, - original_service_fee_required_total_minor.separate_with_commas(), + original_total_service_fee_required_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() ), PaymentAdjusterError::RecursionDrainedAllAccounts => write!( @@ -565,7 +566,7 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, sum_as, }; - use crate::accountant::payment_adjuster::service_fee_adjuster::test_helpers::illustrate_why_we_need_to_prevent_exceeding_the_original_value; + use crate::accountant::payment_adjuster::service_fee_adjuster::illustrative_util::illustrate_why_we_need_to_prevent_exceeding_the_original_value; use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, @@ -589,7 +590,6 @@ mod tests { AnalyzedPayableAccount, CreditorThresholds, QualifiedPayableAccount, ResponseSkeleton, }; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; - use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use itertools::{Either, Itertools}; @@ -601,7 +601,7 @@ mod tests { use std::time::{Duration, SystemTime}; use std::{usize, vec}; use thousands::Separable; - use web3::types::U256; + use web3::types::{Address, U256}; #[test] #[should_panic( @@ -801,16 +801,14 @@ mod tests { }; assert_eq!( result, - Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor: cw_transaction_fee_balance_minor.into(), - }), - service_fee_opt: None - } - ) + Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + number_of_accounts, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor: cw_transaction_fee_balance_minor.into(), + }), + service_fee_opt: None + }) ); } @@ -849,16 +847,14 @@ mod tests { assert_eq!( result, - Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts: 3, - transaction_fee_opt: None, - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: multiply_by_billion(920), - cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance - }) - } - ) + Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + number_of_accounts: 3, + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: multiply_by_billion(920), + cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance + }) + }) ); } @@ -888,19 +884,17 @@ mod tests { TX_FEE_MARGIN_IN_PERCENT.add_percent_to(55_000 * multiply_by_billion(123)); assert_eq!( result, - Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { - number_of_accounts, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor: U256::zero(), - }), - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: multiply_by_billion(500), - cw_service_fee_balance_minor: 0 - }) - } - ) + Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + number_of_accounts, + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor: U256::zero(), + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: multiply_by_billion(500), + cw_service_fee_balance_minor: 0 + }) + }) ); } @@ -908,7 +902,7 @@ mod tests { fn payment_adjuster_error_implements_display() { let inputs = vec![ ( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 4, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ per_transaction_requirement_minor: multiply_by_billion(70_000), @@ -921,7 +915,7 @@ mod tests { the wallet contains: 90,000 wei", ), ( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 5, transaction_fee_opt: None, service_fee_opt: Some(ServiceFeeImmoderateInsufficiency{ @@ -934,7 +928,7 @@ mod tests { contains: 333,000,000 wei", ), ( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 5, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ per_transaction_requirement_minor: 5_000_000_000, @@ -951,10 +945,10 @@ mod tests { while in wallet: 100,000,000 wei", ), ( - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts: 6, number_of_accounts: 3, - original_service_fee_required_total_minor: 1234567891011, + original_total_service_fee_required_minor: 1234567891011, cw_service_fee_balance_minor: 333333, }, "The original set with 6 accounts was adjusted down to 3 due to transaction fee. \ @@ -980,7 +974,7 @@ mod tests { specifications" )] fn error_message_for_input_referring_to_no_issues_cannot_be_made() { - let _ = PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + let _ = PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 0, transaction_fee_opt: None, service_fee_opt: None, @@ -992,7 +986,7 @@ mod tests { fn we_can_say_if_error_occurred_after_insolvency_was_detected() { let inputs = vec![ PaymentAdjusterError::RecursionDrainedAllAccounts, - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 0, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { per_transaction_requirement_minor: 0, @@ -1000,7 +994,7 @@ mod tests { }), service_fee_opt: None, }, - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 0, transaction_fee_opt: None, service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { @@ -1008,7 +1002,7 @@ mod tests { cw_service_fee_balance_minor: 0, }), }, - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 0, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { per_transaction_requirement_minor: 0, @@ -1019,10 +1013,10 @@ mod tests { cw_service_fee_balance_minor: 0, }), }, - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts: 0, number_of_accounts: 0, - original_service_fee_required_total_minor: 0, + original_total_service_fee_required_minor: 0, cw_service_fee_balance_minor: 0, }, ]; @@ -1077,7 +1071,7 @@ mod tests { illustrate_why_we_need_to_prevent_exceeding_the_original_value( cw_service_fee_balance_minor, weighed_payables.clone(), - wallet_2, + wallet_2.address(), balance_2, ); let payable_account_1 = &weighed_payables[0] @@ -2094,10 +2088,10 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts: 3, number_of_accounts: 2, - original_service_fee_required_total_minor: balance_account_1 + original_total_service_fee_required_minor: balance_account_1 + balance_account_2 + balance_account_3, cw_service_fee_balance_minor @@ -2354,7 +2348,7 @@ mod tests { } struct ExpectedWeightWithWallet { - wallet: Wallet, + wallet: Address, weight: u128, } @@ -2523,7 +2517,7 @@ mod tests { .payable .bare_account .wallet - .clone(); + .address(); let weight = particular_calculator_scenario.expected_weight; let expected_weight = ExpectedWeightWithWallet { wallet, weight }; (particular_calculator_scenario.payable, expected_weight) @@ -2559,10 +2553,10 @@ mod tests { fn make_comparison_hashmap( weighed_accounts: Vec, - ) -> HashMap { + ) -> HashMap { let feeding_iterator = weighed_accounts .into_iter() - .map(|account| (account.wallet().clone(), account)); + .map(|account| (account.wallet(), account)); HashMap::from_iter(feeding_iterator) } diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 8f17d4bee..737e71bbb 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -38,7 +38,7 @@ use std::fs::File; use std::io::Write; use std::time::SystemTime; use thousands::Separable; -use web3::types::U256; +use web3::types::{Address, U256}; #[test] // TODO If an option for "occasional tests" is added, this is a good adept @@ -409,7 +409,7 @@ fn make_payables_according_to_thresholds_setup( } AppliedThresholds::RandomizedForEachAccount { individual_thresholds, - } => make_payables_with_individual_thresholds(gn, &individual_thresholds, now), + } => make_payables_with_individual_thresholds(gn, wallets, individual_thresholds, now), }; (payables, nominated_thresholds) @@ -434,8 +434,8 @@ fn choose_thresholds(gn: &mut ThreadRng, prepared_wallets: &[Wallet]) -> Applied } else { let individual_thresholds = prepared_wallets .iter() - .map(|wallet| (wallet.clone(), return_single_randomized_thresholds(gn))) - .collect::>(); + .map(|wallet| (wallet.address(), return_single_randomized_thresholds(gn))) + .collect::>(); AppliedThresholds::RandomizedForEachAccount { individual_thresholds, } @@ -457,12 +457,20 @@ fn make_payables_with_common_thresholds( fn make_payables_with_individual_thresholds( gn: &mut ThreadRng, - wallets_and_thresholds: &HashMap, + wallets: Vec, + wallet_addresses_and_thresholds: &HashMap, now: SystemTime, ) -> Vec { - wallets_and_thresholds + let mut wallets_by_address = wallets + .into_iter() + .map(|wallet| (wallet.address(), wallet)) + .collect::>(); + wallet_addresses_and_thresholds .iter() - .map(|(wallet, thresholds)| make_payable_account(wallet.clone(), thresholds, now, gn)) + .map(|(wallet, thresholds)| { + let wallet = wallets_by_address.remove(wallet).expect("missing wallet"); + make_payable_account(wallet, thresholds, now, gn) + }) .collect() } @@ -556,8 +564,8 @@ fn merge_information_about_particular_account( ) -> Vec<(AccountInfo, Option)> { let mut accounts_hashmap = accounts_after_adjustment .into_iter() - .map(|account| (account.wallet.clone(), account)) - .collect::>(); + .map(|account| (account.wallet.address(), account)) + .collect::>(); accounts_infos .into_iter() @@ -631,7 +639,7 @@ fn preserve_account_infos( accounts .iter() .map(|account| AccountInfo { - wallet: account.qualified_as.bare_account.wallet.clone(), + wallet: account.qualified_as.bare_account.wallet.address(), initially_requested_service_fee_minor: account.qualified_as.bare_account.balance_wei, debt_age_s: now .duration_since(account.qualified_as.bare_account.last_paid_timestamp) @@ -807,10 +815,10 @@ fn do_final_processing_of_single_scenario( } Err(negative) => { match negative.adjuster_error { - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { .. } => { + PaymentAdjusterError::AbsolutelyInsufficientBalance { .. } => { panic!("Such errors should be already filtered out") } - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { .. } => { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { .. } => { output_collector.late_immoderately_insufficient_service_fee_balance += 1 } PaymentAdjusterError::RecursionDrainedAllAccounts => { @@ -887,7 +895,7 @@ fn render_negative_scenario(file: &mut File, negative_result: FailedAdjustment) } trait AccountWithWallet { - fn wallet(&self) -> &Wallet; + fn wallet(&self) -> Address; } fn render_accounts( @@ -913,10 +921,7 @@ fn render_accounts( .map(|account| { ( account, - fetch_individual_thresholds_for_account_if_appropriate( - individual_thresholds_opt, - account, - ), + fetch_individual_thresholds_for_account_opt(individual_thresholds_opt, account), ) }) .for_each(|(account, individual_thresholds_opt)| { @@ -926,8 +931,8 @@ fn render_accounts( file.write(b"\n").unwrap(); } -fn fetch_individual_thresholds_for_account_if_appropriate<'a, Account>( - individual_thresholds_opt: Option<&'a HashMap>, +fn fetch_individual_thresholds_for_account_opt<'a, Account>( + individual_thresholds_opt: Option<&'a HashMap>, account: &'a Account, ) -> Option<&'a PaymentThresholds> where @@ -1226,8 +1231,8 @@ struct InterpretableAccountAdjustmentResult { } impl AccountWithWallet for InterpretableAccountAdjustmentResult { - fn wallet(&self) -> &Wallet { - &self.info.wallet + fn wallet(&self) -> Address { + self.info.wallet } } @@ -1257,14 +1262,14 @@ impl InterpretableAccountAdjustmentResult { } struct AccountInfo { - wallet: Wallet, + wallet: Address, initially_requested_service_fee_minor: u128, debt_age_s: u64, } impl AccountWithWallet for AccountInfo { - fn wallet(&self) -> &Wallet { - &self.wallet + fn wallet(&self) -> Address { + self.wallet } } @@ -1274,6 +1279,6 @@ enum AppliedThresholds { common_thresholds: PaymentThresholds, }, RandomizedForEachAccount { - individual_thresholds: HashMap, + individual_thresholds: HashMap, }, } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 3c0d26797..62ddde7ac 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -110,7 +110,7 @@ impl PreparatoryAnalyzer { let construct_error = |tx_fee_check_err_opt: Option, service_fee_check_err_opt: Option| { - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts, transaction_fee_opt: tx_fee_check_err_opt, service_fee_opt: service_fee_check_err_opt, @@ -321,18 +321,18 @@ impl ServiceFeeSingleTXErrorFactory Self { let original_number_of_accounts = unadjusted_accounts.len(); - let original_service_fee_required_total_minor = sum_as(unadjusted_accounts, |account| { + let original_total_service_fee_required_minor = sum_as(unadjusted_accounts, |account| { account.initial_balance_minor() }); Self { original_number_of_accounts, - original_service_fee_required_total_minor, + original_total_service_fee_required_minor, } } } @@ -346,11 +346,11 @@ impl ServiceFeeSingleTXErrorFactory cw_service_fee_balance_minor: u128, ) -> PaymentAdjusterError { let number_of_accounts = current_set_of_accounts.len(); - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts: self.original_number_of_accounts, number_of_accounts, - original_service_fee_required_total_minor: self - .original_service_fee_required_total_minor, + original_total_service_fee_required_minor: self + .original_total_service_fee_required_minor, cw_service_fee_balance_minor, } } @@ -594,10 +594,10 @@ mod tests { let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); let ensure_accounts_right_type = |accounts| accounts; let prepare_expected_error = |number_of_accounts, _, cw_service_fee_balance_minor| { - PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { original_number_of_accounts, number_of_accounts, - original_service_fee_required_total_minor: initial_sum, + original_total_service_fee_required_minor: initial_sum, cw_service_fee_balance_minor, } }; @@ -684,7 +684,7 @@ mod tests { result, LateServiceFeeSingleTxErrorFactory { original_number_of_accounts: 2, - original_service_fee_required_total_minor: balance_1 + balance_2 + original_total_service_fee_required_minor: balance_1 + balance_2 } ) } diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index a5e5d2984..185315bb7 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -101,7 +101,7 @@ impl ServiceFeeAdjusterReal { let remaining = unconfirmed_adjustments .into_iter() - .filter(|account_info| account_info.wallet() != &disqualified_account_wallet) + .filter(|account_info| account_info.wallet() != disqualified_account_wallet) .collect(); let remaining_reverted = convert_collection(remaining); @@ -316,16 +316,16 @@ mod tests { } #[cfg(test)] -pub mod test_helpers { +pub mod illustrative_util { use crate::accountant::payment_adjuster::miscellaneous::data_structures::WeighedPayable; use crate::accountant::payment_adjuster::service_fee_adjuster::compute_unconfirmed_adjustments; - use crate::sub_lib::wallet::Wallet; use thousands::Separable; + use web3::types::Address; pub fn illustrate_why_we_need_to_prevent_exceeding_the_original_value( cw_service_fee_balance_minor: u128, weighed_accounts: Vec, - wallet_of_expected_outweighed: Wallet, + wallet_of_expected_outweighed: Address, original_balance_of_outweighed_account: u128, ) { let unconfirmed_adjustments = @@ -333,14 +333,15 @@ pub mod test_helpers { // The results are sorted from the biggest weights down assert_eq!( unconfirmed_adjustments[1].wallet(), - &wallet_of_expected_outweighed + wallet_of_expected_outweighed ); // To prevent unjust reallocation we used to secure a rule an account could never demand // more than 100% of its size. - // Later it was changed to a different policy, so called "outweighed" account gains - // automatically a balance equal to its disqualification limit. Still, later on it's very - // likely to be given a bit more from the remains languishing in the consuming wallet. + // Later it was changed to a different policy, the so called "outweighed" account is given + // automatically a balance equal to its disqualification limit. Still, later on, it's quite + // likely to acquire slightly more by a distribution of the last bits of funds away from + // within the consuming wallet. let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), From 5b432d01d4107a491272dd926e6de40999dec42e Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 3 Jan 2025 21:21:16 +0100 Subject: [PATCH 239/250] GH-711-review-one: after ci/all.sh --- masq_lib/src/percentage.rs | 2 +- node/src/accountant/mod.rs | 15 +--- .../balance_calculator.rs | 4 +- node/src/accountant/payment_adjuster/inner.rs | 76 ++++++----------- .../logging_and_diagnostics/log_functions.rs | 6 +- node/src/accountant/payment_adjuster/mod.rs | 85 +++++++------------ .../payment_adjuster/non_unit_tests/mod.rs | 2 +- .../payment_adjuster/service_fee_adjuster.rs | 8 +- .../accountant/payment_adjuster/test_utils.rs | 11 +-- node/src/accountant/scanners/mod.rs | 3 +- node/src/accountant/test_utils.rs | 13 +-- 11 files changed, 75 insertions(+), 150 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 6a3201b3b..1e89bb045 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -616,7 +616,7 @@ mod tests { #[test] fn loose_percentage_multiple_of_percent_hits_limit() { - let percents = ((u8::MAX as u32 + 1) * 100); + let percents = (u8::MAX as u32 + 1) * 100; let subject = LoosePercentage::new(percents); let result: Result = subject.of(1); diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index a785c15a7..c9551598a 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1639,11 +1639,9 @@ mod tests { subject_addr.try_send(payable_payments_setup_msg).unwrap(); - let before = SystemTime::now(); assert_eq!(system.run(), 0); - let after = SystemTime::now(); let mut adjust_payments_params = adjust_payments_params_arc.lock().unwrap(); - let (actual_prepared_adjustment, captured_now) = adjust_payments_params.remove(0); + let actual_prepared_adjustment = adjust_payments_params.remove(0); assert_eq!( actual_prepared_adjustment.adjustment_analysis.adjustment, Adjustment::ByServiceFee @@ -1656,13 +1654,6 @@ mod tests { actual_prepared_adjustment.agent.arbitrary_id_stamp(), agent_id_stamp_first_phase ); - assert!( - before <= captured_now && captured_now <= after, - "captured timestamp should have been between {:?} and {:?} but was {:?}", - before, - after, - captured_now - ); assert!(adjust_payments_params.is_empty()); let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); let actual_payments_instructions = @@ -1748,7 +1739,7 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 1, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), @@ -1813,7 +1804,7 @@ mod tests { "payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( - PaymentAdjusterError::EarlyNotEnoughFeeForSingleTransaction { + PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 20, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { per_transaction_requirement_minor: 40_000_000_000, diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index f1dcc05c2..77f086bd4 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -32,7 +32,6 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::helper_functions::find_largest_exceeding_balance; use crate::accountant::payment_adjuster::test_utils::local_utils::multiply_by_billion; use crate::accountant::test_utils::make_meaningless_analyzed_account; - use std::time::SystemTime; #[test] fn calculator_knows_its_name() { @@ -45,7 +44,6 @@ mod tests { #[test] fn balance_criterion_calculator_works() { - let now = SystemTime::now(); let analyzed_accounts = [50, 100, 2_222] .into_iter() .enumerate() @@ -61,7 +59,7 @@ mod tests { .collect::>(); let largest_exceeding_balance = find_largest_exceeding_balance(&analyzed_accounts); let payment_adjuster_inner = PaymentAdjusterInner::default(); - payment_adjuster_inner.initialize_guts(None, 123456789, largest_exceeding_balance, now); + payment_adjuster_inner.initialize_guts(None, 123456789, largest_exceeding_balance); let subject = BalanceCriterionCalculator::default(); let computed_criteria = analyzed_accounts diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 18c2d6868..99bafd080 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -1,7 +1,6 @@ // Copyright (c) 2023, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use std::cell::RefCell; -use std::time::SystemTime; pub struct PaymentAdjusterInner { initialized_guts_opt: RefCell>, @@ -15,46 +14,37 @@ impl Default for PaymentAdjusterInner { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct GutsOfPaymentAdjusterInner { - now: SystemTime, transaction_count_limit_opt: Option, max_debt_above_threshold_in_qualified_payables_minor: u128, original_cw_service_fee_balance_minor: u128, - unallocated_cw_service_fee_balance_minor: u128, + remaining_cw_service_fee_balance_minor: u128, } impl GutsOfPaymentAdjusterInner { pub fn new( - now: SystemTime, transaction_count_limit_opt: Option, cw_service_fee_balance_minor: u128, max_debt_above_threshold_in_qualified_payables_minor: u128, ) -> Self { Self { - now, transaction_count_limit_opt, max_debt_above_threshold_in_qualified_payables_minor, original_cw_service_fee_balance_minor: cw_service_fee_balance_minor, - unallocated_cw_service_fee_balance_minor: cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor: cw_service_fee_balance_minor, } } } impl PaymentAdjusterInner { - pub fn now(&self) -> SystemTime { - self.get_value("now", |guts_ref| guts_ref.now) - } - pub fn initialize_guts( &self, tx_count_limit_opt: Option, cw_service_fee_balance: u128, max_debt_above_threshold_in_qualified_payables_minor: u128, - now: SystemTime, ) { let initialized_guts = GutsOfPaymentAdjusterInner::new( - now, tx_count_limit_opt, cw_service_fee_balance, max_debt_above_threshold_in_qualified_payables_minor, @@ -82,26 +72,24 @@ impl PaymentAdjusterInner { guts_ref.original_cw_service_fee_balance_minor }) } - pub fn unallocated_cw_service_fee_balance_minor(&self) -> u128 { - self.get_value("unallocated_cw_service_fee_balance_minor", |guts_ref| { - guts_ref.unallocated_cw_service_fee_balance_minor + pub fn remaining_cw_service_fee_balance_minor(&self) -> u128 { + self.get_value("remaining_cw_service_fee_balance_minor", |guts_ref| { + guts_ref.remaining_cw_service_fee_balance_minor }) } - pub fn subtract_from_unallocated_cw_service_fee_balance_minor(&self, subtrahend: u128) { + pub fn subtract_from_remaining_cw_service_fee_balance_minor(&self, subtrahend: u128) { let updated_thought_cw_balance = self.get_value( - "subtract_from_unallocated_cw_service_fee_balance_minor", + "subtract_from_remaining_cw_service_fee_balance_minor", |guts_ref| { guts_ref - .unallocated_cw_service_fee_balance_minor + .remaining_cw_service_fee_balance_minor .checked_sub(subtrahend) .expect("should never go beyond zero") }, ); self.set_value( - "subtract_from_unallocated_cw_service_fee_balance_minor", - |guts_mut| { - guts_mut.unallocated_cw_service_fee_balance_minor = updated_thought_cw_balance - }, + "subtract_from_remaining_cw_service_fee_balance_minor", + |guts_mut| guts_mut.remaining_cw_service_fee_balance_minor = updated_thought_cw_balance, ) } @@ -149,7 +137,6 @@ mod tests { GutsOfPaymentAdjusterInner, PaymentAdjusterInner, }; use std::panic::{catch_unwind, AssertUnwindSafe}; - use std::time::SystemTime; #[test] fn defaulted_payment_adjuster_inner() { @@ -162,7 +149,6 @@ mod tests { #[test] fn initialization_and_getters_of_payment_adjuster_inner_work() { let subject = PaymentAdjusterInner::default(); - let now = SystemTime::now(); let tx_count_limit_opt = Some(3); let cw_service_fee_balance = 123_456_789; let max_debt_above_threshold_in_qualified_payables_minor = 44_555_666; @@ -171,18 +157,15 @@ mod tests { tx_count_limit_opt, cw_service_fee_balance, max_debt_above_threshold_in_qualified_payables_minor, - now, ); - let read_now = subject.now(); let read_max_debt_above_threshold_in_qualified_payables_minor = subject.max_debt_above_threshold_in_qualified_payables_minor(); let read_tx_count_limit_opt = subject.transaction_count_limit_opt(); let read_original_cw_service_fee_balance_minor = subject.original_cw_service_fee_balance_minor(); - let read_unallocated_cw_service_fee_balance_minor = - subject.unallocated_cw_service_fee_balance_minor(); + let read_remaining_cw_service_fee_balance_minor = + subject.remaining_cw_service_fee_balance_minor(); - assert_eq!(read_now, now); assert_eq!( read_max_debt_above_threshold_in_qualified_payables_minor, max_debt_above_threshold_in_qualified_payables_minor @@ -193,29 +176,24 @@ mod tests { cw_service_fee_balance ); assert_eq!( - read_unallocated_cw_service_fee_balance_minor, + read_remaining_cw_service_fee_balance_minor, cw_service_fee_balance ); } #[test] - fn reducing_unallocated_cw_service_fee_balance_works() { + fn reducing_remaining_cw_service_fee_balance_works() { let initial_cw_service_fee_balance_minor = 123_123_678_678; let subject = PaymentAdjusterInner::default(); - subject.initialize_guts( - None, - initial_cw_service_fee_balance_minor, - 12345, - SystemTime::now(), - ); + subject.initialize_guts(None, initial_cw_service_fee_balance_minor, 12345); let amount_to_subtract = 555_666_777; - subject.subtract_from_unallocated_cw_service_fee_balance_minor(amount_to_subtract); + subject.subtract_from_remaining_cw_service_fee_balance_minor(amount_to_subtract); - let unallocated_cw_service_fee_balance_minor = - subject.unallocated_cw_service_fee_balance_minor(); + let remaining_cw_service_fee_balance_minor = + subject.remaining_cw_service_fee_balance_minor(); assert_eq!( - unallocated_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, initial_cw_service_fee_balance_minor - amount_to_subtract ) } @@ -226,11 +204,10 @@ mod tests { subject .initialized_guts_opt .replace(Some(GutsOfPaymentAdjusterInner { - now: SystemTime::now(), transaction_count_limit_opt: None, max_debt_above_threshold_in_qualified_payables_minor: 0, original_cw_service_fee_balance_minor: 0, - unallocated_cw_service_fee_balance_minor: 0, + remaining_cw_service_fee_balance_minor: 0, })); subject.invalidate_guts(); @@ -242,9 +219,6 @@ mod tests { #[test] fn reasonable_panics_about_lacking_initialization_for_respective_methods() { let uninitialized_subject = PaymentAdjusterInner::default(); - test_properly_implemented_panic(&uninitialized_subject, "now", |subject| { - subject.now(); - }); test_properly_implemented_panic( &uninitialized_subject, "max_debt_above_threshold_in_qualified_payables_minor", @@ -268,16 +242,16 @@ mod tests { ); test_properly_implemented_panic( &uninitialized_subject, - "unallocated_cw_service_fee_balance_minor", + "remaining_cw_service_fee_balance_minor", |subject| { - subject.unallocated_cw_service_fee_balance_minor(); + subject.remaining_cw_service_fee_balance_minor(); }, ); test_properly_implemented_panic( &uninitialized_subject, - "subtract_from_unallocated_cw_service_fee_balance_minor", + "subtract_from_remaining_cw_service_fee_balance_minor", |subject| { - subject.subtract_from_unallocated_cw_service_fee_balance_minor(123456); + subject.subtract_from_remaining_cw_service_fee_balance_minor(123456); }, ) } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index c401db6fb..d8dbb7740 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -108,7 +108,7 @@ fn preprocess_excluded_accounts( original_account_balances_mapped .iter() .fold(vec![], |mut acc, (wallet, original_balance)| { - if !adjusted_accounts_wallets.contains(&wallet) { + if !adjusted_accounts_wallets.contains(wallet) { acc.push((*wallet, *original_balance)); } acc @@ -150,8 +150,8 @@ pub fn info_log_for_disqualified_account( ) { info!( logger, - "Ready payment to {} was eliminated to spare MASQ for those higher prioritized. {} wei owed \ - at the moment.", + "Ready payment to {:?} was eliminated to spare MASQ for those higher prioritized. {} wei \ + owed at the moment.", account.wallet, account.initial_account_balance_minor.separate_with_commas(), ) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index b5d430ea8..77307b8aa 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -47,7 +47,6 @@ use itertools::Either; use masq_lib::logger::Logger; use std::collections::HashMap; use std::fmt::{Display, Formatter}; -use std::time::SystemTime; use thousands::Separable; use variant_count::VariantCount; use web3::types::{Address, U256}; @@ -80,7 +79,6 @@ pub trait PaymentAdjuster { fn adjust_payments( &self, setup: PreparedAdjustment, - now: SystemTime, ) -> Result; } @@ -109,7 +107,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { fn adjust_payments( &self, setup: PreparedAdjustment, - now: SystemTime, ) -> Result { let analyzed_payables = setup.adjustment_analysis.accounts; let response_skeleton_opt = setup.response_skeleton_opt; @@ -123,7 +120,6 @@ impl PaymentAdjuster for PaymentAdjusterReal { required_adjustment, initial_service_fee_balance_minor, max_debt_above_threshold_in_qualified_payables_minor, - now, ); let sketched_debug_log_opt = self.sketch_debug_log_opt(&analyzed_payables); @@ -165,7 +161,6 @@ impl PaymentAdjusterReal { required_adjustment: Adjustment, initial_service_fee_balance_minor: u128, max_debt_above_threshold_in_qualified_payables_minor: u128, - now: SystemTime, ) { let transaction_fee_limitation_opt = match required_adjustment { Adjustment::BeginByTransactionFee { @@ -178,7 +173,6 @@ impl PaymentAdjusterReal { transaction_fee_limitation_opt, initial_service_fee_balance_minor, max_debt_above_threshold_in_qualified_payables_minor, - now, ) } @@ -273,14 +267,13 @@ impl PaymentAdjusterReal { ); let disqualification_arbiter = &self.disqualification_arbiter; - let unallocated_cw_service_fee_balance = - self.inner.unallocated_cw_service_fee_balance_minor(); + let remaining_cw_service_fee_balance = self.inner.remaining_cw_service_fee_balance_minor(); let logger = &self.logger; let current_iteration_result = self.service_fee_adjuster.perform_adjustment_by_service_fee( weighed_accounts, disqualification_arbiter, - unallocated_cw_service_fee_balance, + remaining_cw_service_fee_balance, logger, ); @@ -292,7 +285,7 @@ impl PaymentAdjusterReal { } if !decided_accounts.is_empty() { - self.adjust_remaining_unallocated_cw_balance_down(&decided_accounts) + self.adjust_remaining_remaining_cw_balance_down(&decided_accounts) } let merged = @@ -320,12 +313,11 @@ impl PaymentAdjusterReal { &self, remaining_undecided_accounts: &[WeighedPayable], ) -> bool { - let unallocated_cw_service_fee_balance = - self.inner.unallocated_cw_service_fee_balance_minor(); + let remaining_cw_service_fee_balance = self.inner.remaining_cw_service_fee_balance_minor(); let minimum_sum_required: u128 = sum_as(remaining_undecided_accounts, |weighed_account| { weighed_account.disqualification_limit() }); - minimum_sum_required <= unallocated_cw_service_fee_balance + minimum_sum_required <= remaining_cw_service_fee_balance } fn merge_accounts( @@ -372,7 +364,7 @@ impl PaymentAdjusterReal { .collect() } - fn adjust_remaining_unallocated_cw_balance_down( + fn adjust_remaining_remaining_cw_balance_down( &self, decided_accounts: &[AdjustedAccountBeforeFinalization], ) { @@ -380,14 +372,14 @@ impl PaymentAdjusterReal { account.proposed_adjusted_balance_minor }); self.inner - .subtract_from_unallocated_cw_service_fee_balance_minor(subtrahend_total); + .subtract_from_remaining_cw_service_fee_balance_minor(subtrahend_total); diagnostics!( "LOWERED CW BALANCE", "Unallocated balance lowered by {} to {}", subtrahend_total.separate_with_commas(), self.inner - .unallocated_cw_service_fee_balance_minor() + .remaining_cw_service_fee_balance_minor() .separate_with_commas() ) } @@ -606,12 +598,12 @@ mod tests { #[test] #[should_panic( expected = "PaymentAdjusterInner is uninitialized. It was identified during \ - the execution of 'unallocated_cw_service_fee_balance_minor()'" + the execution of 'remaining_cw_service_fee_balance_minor()'" )] fn payment_adjuster_new_is_created_with_inner_null() { let subject = PaymentAdjusterReal::new(); - let _ = subject.inner.unallocated_cw_service_fee_balance_minor(); + let _ = subject.inner.remaining_cw_service_fee_balance_minor(); } #[test] @@ -1189,7 +1181,7 @@ mod tests { adjustment_analysis, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup); let err = match result { Err(e) => e, @@ -1276,7 +1268,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); let expected_affordable_accounts = { vec![account_3, account_2] }; assert_eq!(result.affordable_accounts, expected_affordable_accounts); @@ -1327,7 +1319,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup); // The error isn't important. Received just because we set an almost empty wallet let err = match result { @@ -1369,15 +1361,14 @@ mod tests { fn test_is_cw_balance_enough_to_remaining_accounts( initial_disqualification_limit_for_each_account: u128, - untaken_cw_service_fee_balance_minor: u128, + remaining_cw_service_fee_balance_minor: u128, expected_result: bool, ) { let subject = PaymentAdjusterReal::new(); subject.initialize_inner( Adjustment::ByServiceFee, - untaken_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, 1234567, - SystemTime::now(), ); let mut payable_1 = make_weighed_payable(111, 2 * initial_disqualification_limit_for_each_account); @@ -1395,40 +1386,40 @@ mod tests { } #[test] - fn untaken_balance_is_equal_to_sum_of_disqualification_limits_in_remaining_accounts() { + fn remaining_balance_is_equal_to_sum_of_disqualification_limits_in_remaining_accounts() { let disqualification_limit_for_each_account = multiply_by_billion(5); - let untaken_cw_service_fee_balance_minor = + let remaining_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account; test_is_cw_balance_enough_to_remaining_accounts( disqualification_limit_for_each_account, - untaken_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, true, ) } #[test] - fn untaken_balance_is_more_than_sum_of_disqualification_limits_in_remaining_accounts() { + fn remaining_balance_is_more_than_sum_of_disqualification_limits_in_remaining_accounts() { let disqualification_limit_for_each_account = multiply_by_billion(5); - let untaken_cw_service_fee_balance_minor = + let remaining_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account + 1; test_is_cw_balance_enough_to_remaining_accounts( disqualification_limit_for_each_account, - untaken_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, true, ) } #[test] - fn untaken_balance_is_less_than_sum_of_disqualification_limits_in_remaining_accounts() { + fn remaining_balance_is_less_than_sum_of_disqualification_limits_in_remaining_accounts() { let disqualification_limit_for_each_account = multiply_by_billion(5); - let untaken_cw_service_fee_balance_minor = + let remaining_cw_service_fee_balance_minor = disqualification_limit_for_each_account + disqualification_limit_for_each_account - 1; test_is_cw_balance_enough_to_remaining_accounts( disqualification_limit_for_each_account, - untaken_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, false, ) } @@ -1442,7 +1433,6 @@ mod tests { init_test_logging(); let calculate_params_arc = Arc::new(Mutex::new(vec![])); let test_name = "accounts_count_does_not_change_during_adjustment"; - let now = SystemTime::now(); let balance_account_1 = 5_100_100_100_200_200_200; let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", @@ -1500,7 +1490,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); actual_disqualification_limits.validate_against_expected( 4_100_100_100_200_200_200, @@ -1560,7 +1550,6 @@ mod tests { init_test_logging(); let test_name = "only_transaction_fee_causes_limitations_and_the_service_fee_balance_suffices"; - let now = SystemTime::now(); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", balance_minor: multiply_by_quintillion_concise(0.111), @@ -1613,7 +1602,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); // The account 1 takes the first place for its weight being the biggest let expected_affordable_accounts = { @@ -1653,7 +1642,6 @@ mod tests { // 1) adjustment by transaction fee (always means accounts elimination), // 2) adjustment by service fee (can but not have to cause an account drop-off) init_test_logging(); - let now = SystemTime::now(); let balance_account_1 = multiply_by_quintillion_concise(0.111); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", @@ -1712,7 +1700,7 @@ mod tests { response_skeleton_opt, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); actual_disqualification_limits.validate_against_expected( multiply_by_quintillion_concise(0.071), @@ -1739,7 +1727,6 @@ mod tests { fn only_service_fee_balance_limits_the_payments_count() { init_test_logging(); let test_name = "only_service_fee_balance_limits_the_payments_count"; - let now = SystemTime::now(); // Account to be adjusted to keep as much as it is left in the cw balance let balance_account_1 = multiply_by_billion(333_000_000); let sketched_account_1 = SketchedPayableAccount { @@ -1799,7 +1786,7 @@ mod tests { response_skeleton_opt, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); actual_disqualification_limits.validate_against_expected( multiply_by_billion(183_000_000), @@ -1839,7 +1826,6 @@ mod tests { fn service_fee_as_well_as_transaction_fee_limits_the_payments_count() { init_test_logging(); let test_name = "service_fee_as_well_as_transaction_fee_limits_the_payments_count"; - let now = SystemTime::now(); let balance_account_1 = multiply_by_quintillion(100); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", @@ -1898,7 +1884,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now).unwrap(); + let result = subject.adjust_payments(adjustment_setup).unwrap(); actual_disqualification_limits.validate_against_expected( multiply_by_quintillion(50), @@ -2031,7 +2017,6 @@ mod tests { init_test_logging(); let test_name = "late_error_after_tx_fee_adjusted_but_rechecked_service_fee_found_fatally_insufficient"; - let now = SystemTime::now(); let balance_account_1 = multiply_by_quintillion(500); let sketched_account_1 = SketchedPayableAccount { wallet_addr_seed: "abc", @@ -2075,7 +2060,7 @@ mod tests { response_skeleton_opt: None, }; - let result = subject.adjust_payments(adjustment_setup, now); + let result = subject.adjust_payments(adjustment_setup); actual_disqualification_limits.validate_against_expected( multiply_by_quintillion(300), @@ -2277,7 +2262,7 @@ mod tests { let exceeding_balance = qualified_payable.bare_account.balance_wei - qualified_payable.payment_threshold_intercept_minor; let context = PaymentAdjusterInner::default(); - context.initialize_guts(None, cw_service_fee_balance_minor, exceeding_balance, now); + context.initialize_guts(None, cw_service_fee_balance_minor, exceeding_balance); payment_adjuster .calculators @@ -2380,7 +2365,6 @@ mod tests { to modify only those parameters that are processed within your new calculator " ); test_accounts_from_input_matrix( - now, input_matrix, cw_service_fee_balance_minor, template_computed_weight, @@ -2394,7 +2378,6 @@ mod tests { let template_accounts = initialize_template_accounts(now); let template_weight = compute_common_weight_for_templates( template_accounts.clone(), - now, cw_service_fee_balance_minor, ); (template_accounts, template_weight) @@ -2420,12 +2403,10 @@ mod tests { fn compute_common_weight_for_templates( template_accounts: [QualifiedPayableAccount; 2], - now: SystemTime, cw_service_fee_balance_minor: u128, ) -> TemplateComputedWeight { let template_results = exercise_production_code_to_get_weighed_accounts( template_accounts.to_vec(), - now, cw_service_fee_balance_minor, ); let templates_common_weight = template_results @@ -2446,7 +2427,6 @@ mod tests { fn exercise_production_code_to_get_weighed_accounts( qualified_payables: Vec, - now: SystemTime, cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = @@ -2454,7 +2434,6 @@ mod tests { let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() - .now(now) .cw_service_fee_balance_minor(cw_service_fee_balance_minor) .max_debt_above_threshold_in_qualified_payables_minor( max_debt_above_threshold_in_qualified_payables_minor, @@ -2505,7 +2484,6 @@ mod tests { } fn test_accounts_from_input_matrix( - now: SystemTime, input_matrix: Vec<[CalculatorTestScenario; 2]>, cw_service_fee_balance_minor: u128, template_computed_weight: TemplateComputedWeight, @@ -2539,7 +2517,6 @@ mod tests { let actual_weighed_accounts = exercise_production_code_to_get_weighed_accounts( qualified_payments, - now, cw_service_fee_balance_minor, ); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 737e71bbb..32346cd7f 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -181,7 +181,7 @@ fn loading_test_with_randomized_params() { let cw_service_fee_balance_minor = prepared_adjustment.agent.service_fee_balance_minor(); - let payment_adjuster_result = subject.adjust_payments(prepared_adjustment, now); + let payment_adjuster_result = subject.adjust_payments(prepared_adjustment); administrate_single_scenario_result( payment_adjuster_result, diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index 185315bb7..f9e86a7b5 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -19,7 +19,7 @@ pub trait ServiceFeeAdjuster { &self, weighed_accounts: Vec, disqualification_arbiter: &DisqualificationArbiter, - unallocated_cw_service_fee_balance_minor: u128, + remaining_cw_service_fee_balance_minor: u128, logger: &Logger, ) -> AdjustmentIterationResult; } @@ -148,16 +148,16 @@ impl ServiceFeeAdjusterReal { fn compute_unconfirmed_adjustments( weighed_accounts: Vec, - unallocated_cw_service_fee_balance_minor: u128, + remaining_cw_service_fee_balance_minor: u128, ) -> Vec { let weights_total = sum_as(&weighed_accounts, |weighed_account| weighed_account.weight); let multiplication_coefficient = compute_mul_coefficient_preventing_fractional_numbers( - unallocated_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, ); let proportional_cw_fragment = compute_proportional_cw_fragment( - unallocated_cw_service_fee_balance_minor, + remaining_cw_service_fee_balance_minor, weights_total, multiplication_coefficient, ); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 8b1e6100c..e6c1e3eab 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -38,7 +38,6 @@ pub(super) mod local_utils { #[derive(Default)] pub struct PaymentAdjusterBuilder { start_with_inner_null: bool, - now_opt: Option, cw_service_fee_balance_minor_opt: Option, mock_replacing_calculators_opt: Option, max_debt_above_threshold_in_qualified_payables_minor_opt: Option, @@ -57,7 +56,6 @@ pub(super) mod local_utils { self.cw_service_fee_balance_minor_opt.unwrap_or(0), self.max_debt_above_threshold_in_qualified_payables_minor_opt .unwrap_or(0), - self.now_opt.unwrap_or(SystemTime::now()), ); } if let Some(calculator) = self.mock_replacing_calculators_opt { @@ -93,11 +91,6 @@ pub(super) mod local_utils { self } - pub fn now(mut self, now: SystemTime) -> Self { - self.now_opt = Some(now); - self - } - pub fn logger(mut self, logger: Logger) -> Self { self.logger_opt = Some(logger); self @@ -247,13 +240,13 @@ pub(super) mod local_utils { &self, weighed_accounts: Vec, _disqualification_arbiter: &DisqualificationArbiter, - unallocated_cw_service_fee_balance_minor: u128, + remaining_cw_service_fee_balance_minor: u128, _logger: &Logger, ) -> AdjustmentIterationResult { self.perform_adjustment_by_service_fee_params .lock() .unwrap() - .push((weighed_accounts, unallocated_cw_service_fee_balance_minor)); + .push((weighed_accounts, remaining_cw_service_fee_balance_minor)); self.perform_adjustment_by_service_fee_results .borrow_mut() .remove(0) diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 1124299bd..70d7345b8 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -313,8 +313,7 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { setup: PreparedAdjustment, logger: &Logger, ) -> Option { - let now = SystemTime::now(); - match self.payment_adjuster.adjust_payments(setup, now) { + match self.payment_adjuster.adjust_payments(setup) { Ok(instructions) => Some(instructions), Err(e) => { warning!( diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index fad8ec9f9..f4a286091 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1470,7 +1470,7 @@ pub fn trick_rusqlite_with_read_only_conn( pub struct PaymentAdjusterMock { consider_adjustment_params: Arc, ArbitraryIdStamp)>>>, consider_adjustment_results: RefCell>, - adjust_payments_params: Arc>>, + adjust_payments_params: Arc>>, adjust_payments_results: RefCell>>, } @@ -1491,12 +1491,8 @@ impl PaymentAdjuster for PaymentAdjusterMock { fn adjust_payments( &self, setup: PreparedAdjustment, - now: SystemTime, ) -> Result { - self.adjust_payments_params - .lock() - .unwrap() - .push((setup, now)); + self.adjust_payments_params.lock().unwrap().push(setup); self.adjust_payments_results.borrow_mut().remove(0) } } @@ -1515,10 +1511,7 @@ impl PaymentAdjusterMock { self } - pub fn adjust_payments_params( - mut self, - params: &Arc>>, - ) -> Self { + pub fn adjust_payments_params(mut self, params: &Arc>>) -> Self { self.adjust_payments_params = params.clone(); self } From 6c8b10396a888d07b2749ddf96fe34206cded43f Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 20 Mar 2025 21:20:22 +0100 Subject: [PATCH 240/250] GH-711: knocking off commented findings; interim commit --- masq_lib/src/percentage.rs | 18 +++++++++--------- .../tests/verify_bill_payment_utils/utils.rs | 4 ++-- node/src/accountant/mod.rs | 18 +++++++++--------- .../balance_calculator.rs | 4 ++-- .../logging_and_diagnostics/diagnostics.rs | 4 ++-- .../account_stages_conversions.rs | 6 +++--- node/src/accountant/payment_adjuster/mod.rs | 12 ++++++------ .../payment_adjuster/non_unit_tests/mod.rs | 4 ++-- .../accountant/payment_adjuster/test_utils.rs | 3 ++- .../payable_scanner/test_utils.rs | 6 +++--- node/src/accountant/scanners/mod.rs | 4 ++-- node/src/accountant/test_utils.rs | 4 ++-- node/src/test_utils/database_utils.rs | 4 ++-- node/src/test_utils/mod.rs | 10 +++++----- 14 files changed, 51 insertions(+), 50 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index 1e89bb045..f100ae95a 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -31,6 +31,7 @@ pub struct LoosePercentage { pub trait PercentageInteger: TryFrom + //+ TryFrom + CheckedMul + CheckedAdd + CheckedSub @@ -247,25 +248,24 @@ impl PurePercentage { >::Error: Debug, { let hundred = N::try_from(100).expect("Each type has 100"); - let modulo = num % hundred; + let remainder = num % hundred; let percent = N::try_from(self.degree as i8).expect("Each type has 100"); - - let without_treated_remainder = (num / hundred) * percent; - let final_remainder_treatment = Self::treat_remainder(modulo, percent); - without_treated_remainder + final_remainder_treatment + let percents_of_hundred_multiples = (num / hundred) * percent; + let treated_remainder = Self::treat_remainder(remainder, percent); + percents_of_hundred_multiples + treated_remainder } - fn treat_remainder(modulo: N, percent: N) -> N + fn treat_remainder(remainder: N, percent: N) -> N where N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - let extended_remainder_prepared_for_rounding = i16::try_from(modulo) - .unwrap_or_else(|_| panic!("u16 from -100..=100 failed at modulo {:?}", modulo)) + let extended_remainder_before_rounding = i16::try_from(remainder) + .unwrap_or_else(|_| panic!("u16 from -100..=100 failed at modulo {:?}", remainder)) * i16::try_from(percent).expect("i16 from within 0..=100 failed at multiplier"); - let rounded = Self::div_by_100_and_round(extended_remainder_prepared_for_rounding); + let rounded = Self::div_by_100_and_round(extended_remainder_before_rounding); N::try_from(rounded as i8).expect("Each type has 0 up to 100") } } diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index 18eee8ae7..6f381aaae 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -31,7 +31,7 @@ use node_lib::sub_lib::accountant::PaymentThresholds; use node_lib::sub_lib::blockchain_interface_web3::transaction_data_web3; use node_lib::sub_lib::wallet::Wallet; use node_lib::test_utils; -use node_lib::test_utils::standard_dir_for_test_input_data; +use node_lib::test_utils::test_input_data_standard_dir; use rustc_hex::{FromHex, ToHex}; use std::cell::RefCell; use std::fs::File; @@ -445,7 +445,7 @@ fn make_db_init_config(chain: Chain) -> DbInitializationConfig { fn load_contract_in_bytes() -> Vec { let file_path = - standard_dir_for_test_input_data().join("smart_contract_for_on_blockchain_test"); + test_input_data_standard_dir().join("smart_contract_for_on_blockchain_test"); let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index c9551598a..88a6d87e1 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1078,7 +1078,7 @@ mod tests { }; use crate::accountant::db_access_objects::receivable_dao::ReceivableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; - use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; + use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, @@ -1585,7 +1585,7 @@ mod tests { let prepare_unadjusted_and_adjusted_payable = |n: u64| { let unadjusted_account = make_meaningless_qualified_payable(n); let adjusted_account = PayableAccount { - balance_wei: gwei_to_wei(n / 3), + balance_wei: gwei_to_wei::(n) / 3, ..unadjusted_account.bare_account.clone() }; (unadjusted_account, adjusted_account) @@ -1621,7 +1621,7 @@ mod tests { response_skeleton_opt: Some(response_skeleton), }; let analyzed_accounts = - convert_qualified_into_analyzed_payables_in_test(unadjusted_qualified_accounts.clone()); + convert_qualified_p_into_analyzed_p(unadjusted_qualified_accounts.clone()); let adjustment_analysis = AdjustmentAnalysisReport::new(Adjustment::ByServiceFee, analyzed_accounts.clone()); let payment_adjuster = PaymentAdjusterMock::default() @@ -1762,8 +1762,8 @@ mod tests { log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - The cause appears to be in competence of the user." + "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ + Resolution of the issue appears to be the user's responsibility." )); } @@ -1792,8 +1792,8 @@ mod tests { log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for \ - payments. The cause appears to be in competence of the user" + "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ + Resolution of the issue appears to be the user's responsibility." )); } @@ -1832,8 +1832,8 @@ mod tests { // No NodeUiMessage was sent because there is no `response_skeleton`. It is evident by // the fact that the test didn't blow up even though UIGateway is unbound TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner is blocked from preparing instructions for payments. \ - The cause appears to be in competence of the user" + "ERROR: {test_name}: Payable scanner is unable to generate payment \ + instructions. Resolution of the issue appears to be the user's responsibility." )); } diff --git a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs index 77f086bd4..45226e199 100644 --- a/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs +++ b/node/src/accountant/payment_adjuster/criterion_calculators/balance_calculator.rs @@ -62,7 +62,7 @@ mod tests { payment_adjuster_inner.initialize_guts(None, 123456789, largest_exceeding_balance); let subject = BalanceCriterionCalculator::default(); - let computed_criteria = analyzed_accounts + let calculated_values = analyzed_accounts .iter() .map(|analyzed_account| { subject.calculate(&analyzed_account.qualified_as, &payment_adjuster_inner) @@ -70,7 +70,7 @@ mod tests { .collect::>(); let expected_values = vec![4_384_000_000_000, 4_336_000_000_000, 2_216_000_000_000]; - computed_criteria + calculated_values .into_iter() .zip(expected_values.into_iter()) .for_each(|(actual_criterion, expected_criterion)| { diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 45d7d1015..7936e167e 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -30,9 +30,9 @@ macro_rules! diagnostics { ) }; // Displays an account by wallet address, brief description and formatted literal with arguments - ($wallet: expr, $description: expr, $($formatted_values: tt)*) => { + ($wallet_address: expr, $description: expr, $($formatted_values: tt)*) => { diagnostics( - Some(||format!("{:?}", $wallet)), + Some(||format!("{:?}", $wallet_address)), $description, Some(|| format!($($formatted_values)*)) ) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs index 2007975d1..dbd17bffb 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs @@ -66,11 +66,11 @@ impl From for AdjustedAccountBeforeFinalization { // an amount that equals to their disqualification limit (and can be later provided with even more) impl From for AdjustedAccountBeforeFinalization { fn from(weighed_account: WeighedPayable) -> Self { - let limited_adjusted_balance = weighed_account.disqualification_limit(); - minimal_acceptable_balance_assigned_diagnostics(&weighed_account, limited_adjusted_balance); + let adjusted_balance = weighed_account.disqualification_limit(); + minimal_acceptable_balance_assigned_diagnostics(&weighed_account, adjusted_balance); let weight = weighed_account.weight; let original_account = weighed_account.analyzed_account.qualified_as.bare_account; - AdjustedAccountBeforeFinalization::new(original_account, weight, limited_adjusted_balance) + AdjustedAccountBeforeFinalization::new(original_account, weight, adjusted_balance) } } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 77307b8aa..836376127 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -559,7 +559,7 @@ mod tests { find_largest_exceeding_balance, sum_as, }; use crate::accountant::payment_adjuster::service_fee_adjuster::illustrative_util::illustrate_why_we_need_to_prevent_exceeding_the_original_value; - use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; + use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_mammoth_payables, make_meaningless_analyzed_account_by_wallet, multiply_by_billion, multiply_by_billion_concise, multiply_by_quintillion, multiply_by_quintillion_concise, @@ -698,7 +698,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); let analyzed_payables = - convert_qualified_into_analyzed_payables_in_test(qualified_payables); + convert_qualified_p_into_analyzed_p(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -742,7 +742,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); let analyzed_payables = - convert_qualified_into_analyzed_payables_in_test(qualified_payables); + convert_qualified_p_into_analyzed_p(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -819,7 +819,7 @@ mod tests { let (qualified_payables, boxed_agent) = make_input_for_initial_check_tests(service_fee_balances_config_opt, None); let analyzed_accounts = - convert_qualified_into_analyzed_payables_in_test(qualified_payables.clone()); + convert_qualified_p_into_analyzed_p(qualified_payables.clone()); let minimal_disqualification_limit = analyzed_accounts .iter() .map(|account| account.disqualification_limit_minor) @@ -1989,7 +1989,7 @@ mod tests { }) .collect(); let analyzed_accounts = - convert_qualified_into_analyzed_payables_in_test(qualified_payables); + convert_qualified_p_into_analyzed_p(qualified_payables); let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); (analyzed_accounts, disqualification_limits) @@ -2430,7 +2430,7 @@ mod tests { cw_service_fee_balance_minor: u128, ) -> Vec { let analyzed_payables = - convert_qualified_into_analyzed_payables_in_test(qualified_payables); + convert_qualified_p_into_analyzed_p(qualified_payables); let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 32346cd7f..4f8cbd20a 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -6,7 +6,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::sum_as; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::BalanceProvidingAccount; -use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; +use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::test_utils::local_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, @@ -219,7 +219,7 @@ fn try_making_single_valid_scenario( let (cw_service_fee_balance, qualified_payables, applied_thresholds) = try_generating_qualified_payables_and_cw_balance(gn, accounts_count, now)?; - let analyzed_accounts = convert_qualified_into_analyzed_payables_in_test(qualified_payables); + let analyzed_accounts = convert_qualified_p_into_analyzed_p(qualified_payables); let agent = make_agent(cw_service_fee_balance); let adjustment = make_adjustment(gn, analyzed_accounts.len()); let prepared_adjustment = PreparedAdjustment::new( diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index e6c1e3eab..435cb8cd3 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -325,7 +325,8 @@ pub mod exposed_utils { use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationArbiter; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; - pub fn convert_qualified_into_analyzed_payables_in_test( + // Refrain from using this fn in the prod code + pub fn convert_qualified_p_into_analyzed_p( qualified_account: Vec, ) -> Vec { qualified_account diff --git a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs index ff1f6d3d7..bf6acf656 100644 --- a/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs +++ b/node/src/accountant/scanners/mid_scan_msg_handling/payable_scanner/test_utils.rs @@ -16,7 +16,7 @@ pub struct BlockchainAgentMock { transaction_fee_balance_minor_results: RefCell>, service_fee_balance_minor_results: RefCell>, gas_price_results: RefCell>, - gas_price_margin: RefCell>, + gas_price_margin_results: RefCell>, consuming_wallet_result_opt: Option, pending_transaction_id_results: RefCell>, arbitrary_id_stamp_opt: Option, @@ -46,7 +46,7 @@ impl BlockchainAgent for BlockchainAgentMock { } fn gas_price_margin(&self) -> PurePercentage { - self.gas_price_margin.borrow_mut().remove(0) + self.gas_price_margin_results.borrow_mut().remove(0) } fn consuming_wallet(&self) -> &Wallet { @@ -92,7 +92,7 @@ impl BlockchainAgentMock { } pub fn gas_price_margin_result(self, result: PurePercentage) -> Self { - self.gas_price_margin.borrow_mut().push(result); + self.gas_price_margin_results.borrow_mut().push(result); self } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 70d7345b8..e5f6cd15c 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -330,8 +330,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { fn cancel_scan(&mut self, logger: &Logger) { error!( logger, - "Payable scanner is blocked from preparing instructions for payments. The cause appears \ - to be in competence of the user." + "Payable scanner is unable to generate payment instructions. Resolution of the issue \ + appears to be the user's responsibility." ); self.mark_as_ended(logger) } diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index f4a286091..23e649564 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -13,7 +13,7 @@ use crate::accountant::db_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; -use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_into_analyzed_payables_in_test; +use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::{ AdjustmentAnalysisResult, PaymentAdjuster, PaymentAdjusterError, }; @@ -1755,7 +1755,7 @@ pub fn make_analyzed_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - convert_qualified_into_analyzed_payables_in_test(make_qualified_payables( + convert_qualified_p_into_analyzed_p(make_qualified_payables( payables, payment_thresholds, now, diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index 7bf9d4124..493360302 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -7,7 +7,7 @@ use crate::database::db_initializer::ExternalData; use crate::database::rusqlite_wrappers::ConnectionWrapper; use crate::database::db_migrations::db_migrator::DbMigrator; -use crate::test_utils::standard_dir_for_test_input_data; +use crate::test_utils::test_input_data_standard_dir; use masq_lib::logger::Logger; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::utils::{to_string, NeighborhoodModeLight}; @@ -26,7 +26,7 @@ pub fn bring_db_0_back_to_life_and_return_connection(db_path: &Path) -> Connecti _ => (), }; let conn = Connection::open(&db_path).unwrap(); - let file_path = standard_dir_for_test_input_data().join("database_version_0_sqls.txt"); + let file_path = test_input_data_standard_dir().join("database_version_0_sqls.txt"); let mut file = File::open(file_path).unwrap(); let mut buffer = String::new(); file.read_to_string(&mut buffer).unwrap(); diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index a3b279eca..713049a65 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -527,11 +527,11 @@ pub struct TestRawTransaction { pub data: Vec, } -pub fn standard_dir_for_test_input_data() -> PathBuf { - let mut working_dir = current_dir().unwrap(); - if !working_dir.ends_with("/node/") { - working_dir = working_dir.parent().unwrap().join("node"); - } +pub fn test_input_data_standard_dir() -> PathBuf { + let working_dir = current_dir().unwrap(); + if !working_dir.ends_with("node") { + panic!("Project structure with missing \"node\" directory: {:?}.", working_dir); + }; working_dir .join("src") .join("test_utils") From 1239a158eb44b20b409715b97b472c40e3a5c1be Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 23 Mar 2025 00:39:16 +0100 Subject: [PATCH 241/250] GH-711: fixing percentage.rs; yet more to go with --- masq_lib/src/percentage.rs | 449 ++++++++++-------- .../tests/verify_bill_payment_utils/utils.rs | 3 +- node/src/accountant/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 15 +- node/src/accountant/test_utils.rs | 6 +- node/src/test_utils/mod.rs | 5 +- 6 files changed, 256 insertions(+), 224 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index f100ae95a..c69cc7a42 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -6,32 +6,9 @@ use num::{CheckedDiv, CheckedMul, Integer}; use std::any::type_name; use std::fmt::Debug; use std::ops::Rem; -// Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage -// operations over a wide variety of integer types. It is also to look after the least significant -// digit on the resulted number in order to avoid the effect of a loss on precision that genuinely -// comes with division on integers if a remainder is left over. The percents are always represented -// by an unsigned integer. On the contrary, the numbers that it is applied on can take on both -// positive and negative values. - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PurePercentage { - degree: u8, -} - -// This is a wider type that allows to specify cumulative percents of more than only 100. -// The expected use of this would look like requesting percents meaning possibly multiples of 100%, -// roughly, of a certain base number. Similarly to the PurePercentage type, also signed numbers -// would be accepted. - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct LoosePercentage { - multiples_of_100_percent: u32, - degrees_from_remainder: PurePercentage, -} pub trait PercentageInteger: TryFrom - //+ TryFrom + CheckedMul + CheckedAdd + CheckedSub @@ -52,51 +29,18 @@ macro_rules! impl_percentage_integer { impl_percentage_integer!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); -impl LoosePercentage { - pub fn new(percents: u32) -> Self { - let multiples_of_100_percent = percents / 100; - let remainder = (percents % 100) as u8; - let degrees_from_remainder = - PurePercentage::try_from(remainder).expect("should never happen"); - Self { - multiples_of_100_percent, - degrees_from_remainder, - } - } - - // If this overflows you probably want to precede the operation by converting your base number - // to a larger integer type - pub fn of(&self, num: N) -> Result - where - N: PercentageInteger, - >::Error: Debug, - N: TryFrom, - >::Error: Debug, - i16: TryFrom, - >::Error: Debug, - { - let multiples = match N::try_from(self.multiples_of_100_percent) { - Ok(num) => num, - Err(_) => return Err(BaseTypeOverflow {}), - }; - - let by_wholes = match num.checked_mul(&multiples) { - Some(num) => num, - None => return Err(BaseTypeOverflow {}), - }; - - let by_remainder = self.degrees_from_remainder.of(num); +// Designed to store values from 0 to 100 and offer a set of handy methods for PurePercentage +// operations over a wide variety of integer types. It is also to look after the least significant +// digit on the resulted number in order to avoid the effect of a loss on precision that genuinely +// comes with division on integers if a remainder is left over. The percents are always represented +// by an unsigned integer. On the contrary, the numbers that it is applied on can take on both +// positive and negative values. - match by_wholes.checked_add(&by_remainder) { - Some(res) => Ok(res), - None => Err(BaseTypeOverflow {}), - } - } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PurePercentage { + degree: u8, } -#[derive(Debug, PartialEq, Eq)] -pub struct BaseTypeOverflow {} - impl TryFrom for PurePercentage { type Error = String; @@ -111,15 +55,28 @@ impl TryFrom for PurePercentage { } } -impl PurePercentage { - pub fn of(&self, num: N) -> N - where - N: PercentageInteger, - >::Error: Debug, - i16: TryFrom, - >::Error: Debug, - { - if let Some(zero) = self.return_zero(num) { +trait PurePercentageInternalMethods +where + Self: Sized, +{ + fn _of(&self, num: N) -> N; + fn __check_zero_and_maybe_return_it(&self, num: N) -> Option; + fn __abs(num: N, is_signed: bool) -> N; + fn __transform_to_a_one_by_rounding(remainder: N) -> N; + fn _add_percent_to(&self, num: N) -> N; + fn _subtract_percent_from(&self, num: N) -> N; + fn __handle_upper_overflow(&self, num: N) -> N; +} + +impl PurePercentageInternalMethods for PurePercentage +where + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, +{ + fn _of(&self, num: N) -> N { + if let Some(zero) = self.__check_zero_and_maybe_return_it(num) { return zero; } @@ -128,17 +85,15 @@ impl PurePercentage { .checked_mul(&num) { Some(num) => num, - None => return self.handle_upper_overflow(num), + None => return self.__handle_upper_overflow(num), }; - Self::div_by_100_and_round(product_before_final_div) + let (base, remainder) = base_and_rem_from_div_100(product_before_final_div); + + base + Self::__transform_to_a_one_by_rounding(remainder) } - fn return_zero(&self, num: N) -> Option - where - N: PercentageInteger, - >::Error: Debug, - { + fn __check_zero_and_maybe_return_it(&self, num: N) -> Option { let zero = N::try_from(0).expect("Each type has 0"); if num == zero || N::try_from(self.degree as i8).expect("Each type has 100") == zero { Some(zero) @@ -147,76 +102,32 @@ impl PurePercentage { } } - fn div_by_100_and_round(num: N) -> N - where - N: PercentageInteger, - >::Error: Debug, - { - let divisor = N::try_from(100).expect("Each type has 100"); - let desired_rounding = Self::should_be_rounded_to(num, divisor); - let significant_digits_only = num.checked_div(&divisor).expect("Division failed"); - - macro_rules! adjust_num { - ($significant_digits: expr, $method_add_or_sub: ident, $msg_in_expect: literal) => { - $significant_digits - .$method_add_or_sub(&N::try_from(1).expect("Each type has 1")) - .expect($msg_in_expect) - }; - } - - match desired_rounding { - RoundingTo::BiggerPositive => { - adjust_num!(significant_digits_only, checked_add, "Addition failed") - } - RoundingTo::BiggerNegative => { - adjust_num!(significant_digits_only, checked_sub, "Subtraction failed") - } - RoundingTo::SmallerNegative | RoundingTo::SmallerPositive => significant_digits_only, - } - } - - fn should_be_rounded_to(num: N, divisor: N) -> RoundingTo - where - N: PercentageInteger, - >::Error: Debug, - { - let least_significant_digits: N = num % divisor; - let is_signed = num < N::try_from(0).expect("Each type has 0"); - let divider = N::try_from(50).expect("Each type has 50"); - let abs_of_significant_digits = - Self::abs_of_least_significant_digits(least_significant_digits, is_signed); - let is_minor = abs_of_significant_digits < divider; - match (is_signed, is_minor) { - (false, true) => RoundingTo::SmallerPositive, - (false, false) => RoundingTo::BiggerPositive, - (true, true) => RoundingTo::SmallerNegative, - (true, false) => RoundingTo::BiggerNegative, - } - } - - fn abs_of_least_significant_digits(least_significant_digits: N, is_signed: bool) -> N - where - N: TryFrom + CheckedMul, - >::Error: Debug, - { - if is_signed { + fn __abs(num: N, is_negative: bool) -> N { + if is_negative { N::try_from(-1) .expect("Negative 1 must be possible for a confirmed signed integer") - .checked_mul(&least_significant_digits) - .expect("Must be possible in these low values") + .checked_mul(&num) + .expect("Must be possible for these low values") } else { - least_significant_digits + num } } - pub fn add_percent_to(&self, num: N) -> N - where - N: PercentageInteger, - >::Error: Debug, - i16: TryFrom, - >::Error: Debug, - { - let to_add = self.of(num); + fn __transform_to_a_one_by_rounding(remainder: N) -> N { + let is_negative = remainder < N::try_from(0).expect("Each type has 0"); + let is_minor = + Self::__abs(remainder, is_negative) < N::try_from(50).expect("Each type has 50"); + let addition = match (is_negative, is_minor) { + (false, true) => 0, + (false, false) => 1, + (true, true) => 0, + (true, false) => -1, + }; + N::try_from(addition).expect("Each number has either +1 or -1") + } + + fn _add_percent_to(&self, num: N) -> N { + let to_add = self._of(num); num.checked_add(&to_add).unwrap_or_else(|| { panic!( "Overflowed during addition of {} percent, that is {:?}, to {:?} of type {}.", @@ -228,60 +139,176 @@ impl PurePercentage { }) } - pub fn subtract_percent_from(&self, num: N) -> N + fn _subtract_percent_from(&self, num: N) -> N { + let to_subtract = self._of(num); + num.checked_sub(&to_subtract) + .expect("Mathematically impossible") + } + + fn __handle_upper_overflow(&self, num: N) -> N { + let (base, remainder) = base_and_rem_from_div_100(num); + let percents = N::try_from(self.degree as i8).expect("Each type has 100"); + let percents_of_base = base * percents; + let (percents_of_remainder, nearly_lost_tail) = + base_and_rem_for_ensured_i16(remainder, percents); + let final_rounding_element = Self::__transform_to_a_one_by_rounding(nearly_lost_tail); + + percents_of_base + percents_of_remainder + final_rounding_element + } +} + +impl PurePercentage { + pub fn of(&self, num: N) -> N where - N: PercentageInteger + CheckedSub, + N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - let to_subtract = self.of(num); - num.checked_sub(&to_subtract) - .expect("should never happen by its principle") + self._of(num) } - fn handle_upper_overflow(&self, num: N) -> N + pub fn add_percent_to(&self, num: N) -> N where N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - let hundred = N::try_from(100).expect("Each type has 100"); - let remainder = num % hundred; - let percent = N::try_from(self.degree as i8).expect("Each type has 100"); - let percents_of_hundred_multiples = (num / hundred) * percent; - let treated_remainder = Self::treat_remainder(remainder, percent); - percents_of_hundred_multiples + treated_remainder + self._add_percent_to(num) } - fn treat_remainder(remainder: N, percent: N) -> N + pub fn subtract_percent_from(&self, num: N) -> N where N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - let extended_remainder_before_rounding = i16::try_from(remainder) - .unwrap_or_else(|_| panic!("u16 from -100..=100 failed at modulo {:?}", remainder)) - * i16::try_from(percent).expect("i16 from within 0..=100 failed at multiplier"); - let rounded = Self::div_by_100_and_round(extended_remainder_before_rounding); - N::try_from(rounded as i8).expect("Each type has 0 up to 100") + self._subtract_percent_from(num) + } +} + +fn base_and_rem_for_ensured_i16(a: N, b: N) -> (N, N) +where + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, +{ + let num = i16::try_from(a).expect("Remainder: Should be up to 100") + * i16::try_from(b).expect("Percents: Should be up to 100"); + + let (base, remainder) = base_and_rem_from_div_100(num); + + ( + N::try_from(base as i8).expect("Each type has up to 100"), + N::try_from(remainder as i8).expect("Each type has up to 100"), + ) +} + +fn base_and_rem_from_div_100(num: N) -> (N, N) +where + N: PercentageInteger, + >::Error: Debug, +{ + let hundred = N::try_from(100i8).expect("Each type has 100"); + let modulo = num % hundred; + (num / hundred, modulo) +} + +// This is a wider type that allows to specify cumulative percents of more than only 100. +// The expected use of this would look like requesting percents meaning possibly multiples of 100%, +// roughly, of a certain base number. Similarly to the PurePercentage type, also signed numbers +// would be accepted. + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct LoosePercentage { + multiplier_of_100_percent: u32, + degrees_from_remainder: PurePercentage, +} + +impl LoosePercentage { + pub fn new(percents: u32) -> Self { + let multiples_of_100_percent = percents / 100; + let remainder = (percents % 100) as u8; + let degrees_from_remainder = + PurePercentage::try_from(remainder).expect("should never happen"); + Self { + multiplier_of_100_percent: multiples_of_100_percent, + degrees_from_remainder, + } + } + + // If this returns an overflow error, you may want to precede this by converting the base number + // to a larger integer + pub fn of(&self, num: N) -> Result + where + N: PercentageInteger + TryFrom, + >::Error: Debug, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, + { + let multiplier = match N::try_from(self.multiplier_of_100_percent) { + Ok(n) => n, + Err(e) => { + return Err(BaseTypeOverflow { + msg: format!( + "Couldn't init multiplier {} to type {} due to {:?}.", + self.multiplier_of_100_percent, + type_name::(), + e + ), + }) + } + }; + + let wholes = match num.checked_mul(&multiplier) { + Some(n) => n, + None => { + return Err(BaseTypeOverflow { + msg: format!( + "Multiplication failed between {:?} and {:?} for type {}.", + num, + multiplier, + type_name::() + ), + }) + } + }; + + let remainder = self.degrees_from_remainder.of(num); + + match wholes.checked_add(&remainder) { + Some(res) => Ok(res), + None => Err(BaseTypeOverflow { + msg: format!( + "Final addition failed on {:?} and {:?} for type {}.", + wholes, + remainder, + type_name::() + ), + }), + } } + + // Note that functions like 'add_percents_to' or 'subtract_percents_from' don't need to be + // implemented here, even though they are at the 'PurePercentage'. You can substitute them + // simply by querying 100 + or 100 - } #[derive(Debug, PartialEq, Eq)] -enum RoundingTo { - BiggerPositive, - BiggerNegative, - SmallerPositive, - SmallerNegative, +pub struct BaseTypeOverflow { + msg: String, } #[cfg(test)] mod tests { use crate::percentage::{ - BaseTypeOverflow, LoosePercentage, PercentageInteger, PurePercentage, RoundingTo, + BaseTypeOverflow, LoosePercentage, PercentageInteger, PurePercentage, + PurePercentageInternalMethods, }; use std::fmt::Debug; @@ -308,10 +335,18 @@ mod tests { i16: TryFrom, >::Error: Debug, { - assert_eq!(subject.of(num), expected); - let half = num / N::try_from(2).unwrap(); + let percents_of_num = subject.of(num); + assert_eq!( + percents_of_num, expected, + "Expected {:?}, but was {:?}", + expected, percents_of_num + ); + let simply_calculated_half = num / N::try_from(2).unwrap(); let one = N::try_from(1).unwrap(); - assert!((half - one) <= half && half <= (half + one)) + assert!( + (simply_calculated_half - one) <= simply_calculated_half + && simply_calculated_half <= (simply_calculated_half + one) + ) } #[test] @@ -421,47 +456,35 @@ mod tests { #[test] fn should_be_rounded_to_works_for_last_but_one_digit() { [ - (49, RoundingTo::SmallerPositive, RoundingTo::SmallerNegative), - (50, RoundingTo::BiggerPositive, RoundingTo::BiggerNegative), - (51, RoundingTo::BiggerPositive, RoundingTo::BiggerNegative), - (5, RoundingTo::SmallerPositive, RoundingTo::SmallerNegative), - ( - 100, - RoundingTo::SmallerPositive, - RoundingTo::SmallerNegative, - ), - ( - 787879, - RoundingTo::BiggerPositive, - RoundingTo::BiggerNegative, - ), - ( - 898784545, - RoundingTo::SmallerPositive, - RoundingTo::SmallerNegative, - ), + (49, 0), + (50, 1), + (51, 1), + (5, 0), + (99,1), + (0,0) ] .into_iter() .for_each( - |(num, expected_result_for_unsigned_base, expected_result_for_signed_base)| { - let result = PurePercentage::should_be_rounded_to(num, 100); + |(num, expected_abs_result)| { + let result = PurePercentage::__transform_to_a_one_by_rounding(num); assert_eq!( - result, - expected_result_for_unsigned_base, - "Unsigned number {} was identified for rounding as {:?} but it should've been {:?}", - num, - result, - expected_result_for_unsigned_base + result, + expected_abs_result, + "Unsigned number {} was identified for rounding as {:?} but it should've been {:?}", + num, + result, + expected_abs_result ); let signed = num as i64 * -1; - let result = PurePercentage::should_be_rounded_to(signed, 100); + let result = PurePercentage::__transform_to_a_one_by_rounding(signed); + let expected_neg_result = expected_abs_result * -1; assert_eq!( result, - expected_result_for_signed_base, + expected_neg_result, "Signed number {} was identified for rounding as {:?} but it should've been {:?}", signed, result, - expected_result_for_signed_base + expected_neg_result ) }, ) @@ -621,7 +644,13 @@ mod tests { let result: Result = subject.of(1); - assert_eq!(result, Err(BaseTypeOverflow {})) + assert_eq!( + result, + Err(BaseTypeOverflow { + msg: "Couldn't init multiplier 256 to type u8 due to TryFromIntError(())." + .to_string() + }) + ) } #[test] @@ -631,7 +660,12 @@ mod tests { let result: Result = subject.of(u8::MAX); - assert_eq!(result, Err(BaseTypeOverflow {})) + assert_eq!( + result, + Err(BaseTypeOverflow { + msg: "Multiplication failed between 255 and 2 for type u8.".to_string() + }) + ) } #[test] @@ -641,6 +675,11 @@ mod tests { let result: Result = subject.of(u8::MAX); - assert_eq!(result, Err(BaseTypeOverflow {})) + assert_eq!( + result, + Err(BaseTypeOverflow { + msg: "Final addition failed on 255 and 3 for type u8.".to_string() + }) + ) } } diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index 6f381aaae..cb1876c1e 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -444,8 +444,7 @@ fn make_db_init_config(chain: Chain) -> DbInitializationConfig { } fn load_contract_in_bytes() -> Vec { - let file_path = - test_input_data_standard_dir().join("smart_contract_for_on_blockchain_test"); + let file_path = test_input_data_standard_dir().join("smart_contract_for_on_blockchain_test"); let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 88a6d87e1..7a15b4807 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1585,7 +1585,7 @@ mod tests { let prepare_unadjusted_and_adjusted_payable = |n: u64| { let unadjusted_account = make_meaningless_qualified_payable(n); let adjusted_account = PayableAccount { - balance_wei: gwei_to_wei::(n) / 3, + balance_wei: gwei_to_wei::(n) / 3, ..unadjusted_account.bare_account.clone() }; (unadjusted_account, adjusted_account) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 836376127..0c583f17b 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -697,8 +697,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); - let analyzed_payables = - convert_qualified_p_into_analyzed_p(qualified_payables); + let analyzed_payables = convert_qualified_p_into_analyzed_p(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -741,8 +740,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables.clone(), &*agent); - let analyzed_payables = - convert_qualified_p_into_analyzed_p(qualified_payables); + let analyzed_payables = convert_qualified_p_into_analyzed_p(qualified_payables); assert_eq!( result, Ok(Either::Right(AdjustmentAnalysisReport::new( @@ -818,8 +816,7 @@ mod tests { }); let (qualified_payables, boxed_agent) = make_input_for_initial_check_tests(service_fee_balances_config_opt, None); - let analyzed_accounts = - convert_qualified_p_into_analyzed_p(qualified_payables.clone()); + let analyzed_accounts = convert_qualified_p_into_analyzed_p(qualified_payables.clone()); let minimal_disqualification_limit = analyzed_accounts .iter() .map(|account| account.disqualification_limit_minor) @@ -1988,8 +1985,7 @@ mod tests { ) }) .collect(); - let analyzed_accounts = - convert_qualified_p_into_analyzed_p(qualified_payables); + let analyzed_accounts = convert_qualified_p_into_analyzed_p(qualified_payables); let analyzed_accounts: [AnalyzedPayableAccount; 3] = analyzed_accounts.try_into().unwrap(); let disqualification_limits: QuantifiedDisqualificationLimits = (&analyzed_accounts).into(); (analyzed_accounts, disqualification_limits) @@ -2429,8 +2425,7 @@ mod tests { qualified_payables: Vec, cw_service_fee_balance_minor: u128, ) -> Vec { - let analyzed_payables = - convert_qualified_p_into_analyzed_p(qualified_payables); + let analyzed_payables = convert_qualified_p_into_analyzed_p(qualified_payables); let max_debt_above_threshold_in_qualified_payables_minor = find_largest_exceeding_balance(&analyzed_payables); let mut subject = PaymentAdjusterBuilder::default() diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 23e649564..85f679c13 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1755,11 +1755,7 @@ pub fn make_analyzed_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - convert_qualified_p_into_analyzed_p(make_qualified_payables( - payables, - payment_thresholds, - now, - )) + convert_qualified_p_into_analyzed_p(make_qualified_payables(payables, payment_thresholds, now)) } pub fn try_to_make_guaranteed_qualified_payables( diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 713049a65..d3219d800 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -530,7 +530,10 @@ pub struct TestRawTransaction { pub fn test_input_data_standard_dir() -> PathBuf { let working_dir = current_dir().unwrap(); if !working_dir.ends_with("node") { - panic!("Project structure with missing \"node\" directory: {:?}.", working_dir); + panic!( + "Project structure with missing \"node\" directory: {:?}.", + working_dir + ); }; working_dir .join("src") From b7c7900000a51f8ca77dd16a449e581e1804987b Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 24 Mar 2025 00:50:27 +0100 Subject: [PATCH 242/250] GH-711: percentage.rs finished --- masq_lib/src/percentage.rs | 224 +++++++++++------- .../tests/verify_bill_payment.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 10 +- .../preparatory_analyser/mod.rs | 2 +- 4 files changed, 147 insertions(+), 91 deletions(-) diff --git a/masq_lib/src/percentage.rs b/masq_lib/src/percentage.rs index c69cc7a42..f290fb74c 100644 --- a/masq_lib/src/percentage.rs +++ b/masq_lib/src/percentage.rs @@ -48,7 +48,7 @@ impl TryFrom for PurePercentage { match degree { 0..=100 => Ok(Self { degree }), x => Err(format!( - "Accepts only range from 0 to 100 but {} was supplied", + "Accepts only range from 0 to 100, but {} was supplied", x )), } @@ -62,9 +62,9 @@ where fn _of(&self, num: N) -> N; fn __check_zero_and_maybe_return_it(&self, num: N) -> Option; fn __abs(num: N, is_signed: bool) -> N; - fn __transform_to_a_one_by_rounding(remainder: N) -> N; - fn _add_percent_to(&self, num: N) -> N; - fn _subtract_percent_from(&self, num: N) -> N; + fn __derive_rounding_increment(remainder: N) -> N; + fn _increase_by_percent_for(&self, num: N) -> N; + fn _decrease_by_percent_for(&self, num: N) -> N; fn __handle_upper_overflow(&self, num: N) -> N; } @@ -81,7 +81,7 @@ where } let product_before_final_div = match N::try_from(self.degree as i8) - .expect("Each type has 100") + .expect("Each integer has 100") .checked_mul(&num) { Some(num) => num, @@ -90,12 +90,12 @@ where let (base, remainder) = base_and_rem_from_div_100(product_before_final_div); - base + Self::__transform_to_a_one_by_rounding(remainder) + base + Self::__derive_rounding_increment(remainder) } fn __check_zero_and_maybe_return_it(&self, num: N) -> Option { - let zero = N::try_from(0).expect("Each type has 0"); - if num == zero || N::try_from(self.degree as i8).expect("Each type has 100") == zero { + let zero = N::try_from(0).expect("Each integer has 0"); + if num == zero || N::try_from(self.degree as i8).expect("Each integer has 100") == zero { Some(zero) } else { None @@ -113,24 +113,26 @@ where } } - fn __transform_to_a_one_by_rounding(remainder: N) -> N { - let is_negative = remainder < N::try_from(0).expect("Each type has 0"); + // This function helps to correct the last digit of the resulting integer to be as close + // as possible to the hypothetical fractional number, if we could go beyond the decimal point. + fn __derive_rounding_increment(remainder: N) -> N { + let is_negative = remainder < N::try_from(0).expect("Each integer has 0"); let is_minor = - Self::__abs(remainder, is_negative) < N::try_from(50).expect("Each type has 50"); + Self::__abs(remainder, is_negative) < N::try_from(50).expect("Each integer has 50"); let addition = match (is_negative, is_minor) { (false, true) => 0, (false, false) => 1, (true, true) => 0, (true, false) => -1, }; - N::try_from(addition).expect("Each number has either +1 or -1") + N::try_from(addition).expect("Each integer has 1, or -1 if signed") } - fn _add_percent_to(&self, num: N) -> N { + fn _increase_by_percent_for(&self, num: N) -> N { let to_add = self._of(num); num.checked_add(&to_add).unwrap_or_else(|| { panic!( - "Overflowed during addition of {} percent, that is {:?}, to {:?} of type {}.", + "Overflowed during addition of {} percent, that is an extra {:?} for {:?} of type {}.", self.degree, to_add, num, @@ -139,7 +141,7 @@ where }) } - fn _subtract_percent_from(&self, num: N) -> N { + fn _decrease_by_percent_for(&self, num: N) -> N { let to_subtract = self._of(num); num.checked_sub(&to_subtract) .expect("Mathematically impossible") @@ -147,11 +149,11 @@ where fn __handle_upper_overflow(&self, num: N) -> N { let (base, remainder) = base_and_rem_from_div_100(num); - let percents = N::try_from(self.degree as i8).expect("Each type has 100"); + let percents = N::try_from(self.degree as i8).expect("Each integer has 100"); let percents_of_base = base * percents; let (percents_of_remainder, nearly_lost_tail) = base_and_rem_for_ensured_i16(remainder, percents); - let final_rounding_element = Self::__transform_to_a_one_by_rounding(nearly_lost_tail); + let final_rounding_element = Self::__derive_rounding_increment(nearly_lost_tail); percents_of_base + percents_of_remainder + final_rounding_element } @@ -168,24 +170,24 @@ impl PurePercentage { self._of(num) } - pub fn add_percent_to(&self, num: N) -> N + pub fn increase_by_percent_for(&self, num: N) -> N where N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - self._add_percent_to(num) + self._increase_by_percent_for(num) } - pub fn subtract_percent_from(&self, num: N) -> N + pub fn decrease_by_percent_for(&self, num: N) -> N where N: PercentageInteger, >::Error: Debug, i16: TryFrom, >::Error: Debug, { - self._subtract_percent_from(num) + self._decrease_by_percent_for(num) } } @@ -196,14 +198,18 @@ where i16: TryFrom, >::Error: Debug, { - let num = i16::try_from(a).expect("Remainder: Should be up to 100") - * i16::try_from(b).expect("Percents: Should be up to 100"); + let num = i16::try_from(a) + .expect("Remainder: Each integer can go up to 100, or down to -100 if signed") + * i16::try_from(b) + .expect("Percents: Each integer can go up to 100, or down to -100 if signed"); let (base, remainder) = base_and_rem_from_div_100(num); ( - N::try_from(base as i8).expect("Each type has up to 100"), - N::try_from(remainder as i8).expect("Each type has up to 100"), + N::try_from(base as i8) + .expect("Base: Each integer can go up to 100, or down to -100 if signed"), + N::try_from(remainder as i8) + .expect("Remainder: Each integer can go up to 100, or down to -100 if signed"), ) } @@ -212,7 +218,7 @@ where N: PercentageInteger, >::Error: Debug, { - let hundred = N::try_from(100i8).expect("Each type has 100"); + let hundred = N::try_from(100i8).expect("Each integer has 100"); let modulo = num % hundred; (num / hundred, modulo) } @@ -233,15 +239,15 @@ impl LoosePercentage { let multiples_of_100_percent = percents / 100; let remainder = (percents % 100) as u8; let degrees_from_remainder = - PurePercentage::try_from(remainder).expect("should never happen"); + PurePercentage::try_from(remainder).expect("Should never happen."); Self { multiplier_of_100_percent: multiples_of_100_percent, degrees_from_remainder, } } - // If this returns an overflow error, you may want to precede this by converting the base number - // to a larger integer + // If this returns an overflow error, you may want to precede this by converting the base + // number to a larger integer pub fn of(&self, num: N) -> Result where N: PercentageInteger + TryFrom, @@ -316,19 +322,77 @@ mod tests { fn percentage_is_implemented_for_all_rust_integers() { let subject = PurePercentage::try_from(50).unwrap(); - assert_integer_compatibility(&subject, u8::MAX, 128); - assert_integer_compatibility(&subject, u16::MAX, 32768); - assert_integer_compatibility(&subject, u32::MAX, 2147483648); - assert_integer_compatibility(&subject, u64::MAX, 9223372036854775808); - assert_integer_compatibility(&subject, u128::MAX, 170141183460469231731687303715884105728); - assert_integer_compatibility(&subject, i8::MIN, -64); - assert_integer_compatibility(&subject, i16::MIN, -16384); - assert_integer_compatibility(&subject, i32::MIN, -1073741824); - assert_integer_compatibility(&subject, i64::MIN, -4611686018427387904); - assert_integer_compatibility(&subject, i128::MIN, -85070591730234615865843651857942052864); + assert_positive_integer_compatibility(&subject, u8::MAX, 128); + assert_positive_integer_compatibility(&subject, u16::MAX, 32768); + assert_positive_integer_compatibility(&subject, u32::MAX, 2147483648); + assert_positive_integer_compatibility(&subject, u64::MAX, 9223372036854775808); + assert_positive_integer_compatibility( + &subject, + u128::MAX, + 170141183460469231731687303715884105728, + ); + assert_negative_integer_compatibility(&subject, i8::MIN, -64); + assert_negative_integer_compatibility(&subject, i16::MIN, -16384); + assert_negative_integer_compatibility(&subject, i32::MIN, -1073741824); + assert_negative_integer_compatibility(&subject, i64::MIN, -4611686018427387904); + assert_negative_integer_compatibility( + &subject, + i128::MIN, + -85070591730234615865843651857942052864, + ); + } + + fn assert_positive_integer_compatibility( + subject: &PurePercentage, + num: N, + expected_literal_num: N, + ) where + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, + { + assert_against_literal_value(subject, num, expected_literal_num); + + let trivially_calculated_half = num / N::try_from(2).unwrap(); + // Widening the bounds to compensate the extra rounding + let one = N::try_from(1).unwrap(); + assert!( + trivially_calculated_half <= expected_literal_num + && expected_literal_num <= (trivially_calculated_half + one), + "We expected {:?} to be {:?} or {:?}", + expected_literal_num, + trivially_calculated_half, + trivially_calculated_half + one + ) + } + + fn assert_negative_integer_compatibility( + subject: &PurePercentage, + num: N, + expected_literal_num: N, + ) where + N: PercentageInteger, + >::Error: Debug, + i16: TryFrom, + >::Error: Debug, + { + assert_against_literal_value(subject, num, expected_literal_num); + + let trivially_calculated_half = num / N::try_from(2).unwrap(); + // Widening the bounds to compensate the extra rounding + let one = N::try_from(1).unwrap(); + assert!( + trivially_calculated_half >= expected_literal_num + && expected_literal_num >= trivially_calculated_half - one, + "We expected {:?} to be {:?} or {:?}", + expected_literal_num, + trivially_calculated_half, + trivially_calculated_half - one + ) } - fn assert_integer_compatibility(subject: &PurePercentage, num: N, expected: N) + fn assert_against_literal_value(subject: &PurePercentage, num: N, expected_literal_num: N) where N: PercentageInteger, >::Error: Debug, @@ -336,17 +400,12 @@ mod tests { >::Error: Debug, { let percents_of_num = subject.of(num); + assert_eq!( - percents_of_num, expected, + percents_of_num, expected_literal_num, "Expected {:?}, but was {:?}", - expected, percents_of_num + expected_literal_num, percents_of_num ); - let simply_calculated_half = num / N::try_from(2).unwrap(); - let one = N::try_from(1).unwrap(); - assert!( - (simply_calculated_half - one) <= simply_calculated_half - && simply_calculated_half <= (simply_calculated_half + one) - ) } #[test] @@ -357,34 +416,31 @@ mod tests { #[test] fn pure_percentage_end_to_end_test_for_unsigned() { + let base_value = 100; + let act = |percent, base| PurePercentage::try_from(percent).unwrap().of(base); let expected_values = (0..=100).collect::>(); - test_end_to_end(100, expected_values, |percent, base| { - PurePercentage::try_from(percent).unwrap().of(base) - }) + test_end_to_end(act, base_value, expected_values) } #[test] fn pure_percentage_end_to_end_test_for_signed() { + let base_value = -100; + let act = |percent, base| PurePercentage::try_from(percent).unwrap().of(base); let expected_values = (-100..=0).rev().collect::>(); - test_end_to_end(-100, expected_values, |percent, base| { - PurePercentage::try_from(percent).unwrap().of(base) - }) + test_end_to_end(act, base_value, expected_values) } - fn test_end_to_end( - base: i8, - expected_values: Vec, - create_percentage_and_apply_it_on_number: F, - ) where + fn test_end_to_end(act: F, base: i8, expected_values: Vec) + where F: Fn(u8, i8) -> i8, { let range = 0_u8..=100; let round_returned_range = range .into_iter() - .map(|percent| create_percentage_and_apply_it_on_number(percent, base)) + .map(|percent| act(percent, base)) .collect::>(); assert_eq!(round_returned_range, expected_values) @@ -398,7 +454,7 @@ mod tests { assert_eq!( res, Err(format!( - "Accepts only range from 0 to 100 but {} was supplied", + "Accepts only range from 0 to 100, but {} was supplied", num )) ) @@ -447,7 +503,7 @@ mod tests { .of(case.examined_base_number); assert_eq!( result, case.expected_result, - "For {} percent and number {} the expected result was {} but we got {}", + "For {} percent and number {} the expected result was {}, but we got {}", case.requested_percent, case.examined_base_number, case.expected_result, result ) }) @@ -466,22 +522,22 @@ mod tests { .into_iter() .for_each( |(num, expected_abs_result)| { - let result = PurePercentage::__transform_to_a_one_by_rounding(num); + let result = PurePercentage::__derive_rounding_increment(num); assert_eq!( result, expected_abs_result, - "Unsigned number {} was identified for rounding as {:?} but it should've been {:?}", + "Unsigned number {} was identified for rounding as {:?}, but it should've been {:?}", num, result, expected_abs_result ); let signed = num as i64 * -1; - let result = PurePercentage::__transform_to_a_one_by_rounding(signed); + let result = PurePercentage::__derive_rounding_increment(signed); let expected_neg_result = expected_abs_result * -1; assert_eq!( result, expected_neg_result, - "Signed number {} was identified for rounding as {:?} but it should've been {:?}", + "Signed number {} was identified for rounding as {:?}, but it should've been {:?}", signed, result, expected_neg_result @@ -491,11 +547,11 @@ mod tests { } #[test] - fn add_percent_to_works() { + fn increase_by_percent_for_works() { let subject = PurePercentage::try_from(13).unwrap(); - let unsigned = subject.add_percent_to(100); - let signed = subject.add_percent_to(-100); + let unsigned = subject.increase_by_percent_for(100); + let signed = subject.increase_by_percent_for(-100); assert_eq!(unsigned, 113); assert_eq!(signed, -113) @@ -503,19 +559,19 @@ mod tests { #[test] #[should_panic(expected = "Overflowed during addition of 1 percent, that is \ - 184467440737095516, to 18446744073709551615 of type u64.")] - fn add_percent_to_hits_overflow() { + an extra 184467440737095516 for 18446744073709551615 of type u64.")] + fn increase_by_percent_for_hits_overflow() { let _ = PurePercentage::try_from(1) .unwrap() - .add_percent_to(u64::MAX); + .increase_by_percent_for(u64::MAX); } #[test] - fn subtract_percent_from_works() { + fn decrease_by_percent_for_works() { let subject = PurePercentage::try_from(55).unwrap(); - let unsigned = subject.subtract_percent_from(100); - let signed = subject.subtract_percent_from(-100); + let unsigned = subject.decrease_by_percent_for(100); + let signed = subject.decrease_by_percent_for(-100); assert_eq!(unsigned, 45); assert_eq!(signed, -45) @@ -547,7 +603,7 @@ mod tests { assert_eq!(case_one, 187541898082713775); assert_eq!(case_two, 77); assert_eq!(case_three, 76) - //Note: Interestingly, this isn't a threat on the negative numbers, even the extremes. + // Note: Interestingly, this isn't a threat on the negative numbers, even the extremes. } #[test] @@ -558,20 +614,20 @@ mod tests { #[test] fn loose_percentage_end_to_end_test_for_standard_values_unsigned() { + let base_value = 100; + let act = |percent, base| LoosePercentage::new(percent as u32).of(base).unwrap(); let expected_values = (0..=100).collect::>(); - test_end_to_end(100, expected_values, |percent, base| { - LoosePercentage::new(percent as u32).of(base).unwrap() - }) + test_end_to_end(act, base_value, expected_values) } #[test] fn loose_percentage_end_to_end_test_for_standard_values_signed() { + let base_value = -100; + let act = |percent, base| LoosePercentage::new(percent as u32).of(base).unwrap(); let expected_values = (-100..=0).rev().collect::>(); - test_end_to_end(-100, expected_values, |percent, base| { - LoosePercentage::new(percent as u32).of(base).unwrap() - }) + test_end_to_end(act, base_value, expected_values) } const TEST_SET: [Case; 5] = [ @@ -654,7 +710,7 @@ mod tests { } #[test] - fn loose_percentage_multiplying_input_number_hits_limit() { + fn loose_percentage_hits_limit_at_multiplication() { let percents = 200; let subject = LoosePercentage::new(percents); @@ -669,7 +725,7 @@ mod tests { } #[test] - fn loose_percentage_adding_portion_from_remainder_hits_limit() { + fn loose_percentage_hits_limit_at_addition_from_remainder() { let percents = 101; let subject = LoosePercentage::new(percents); diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index a250b8ed3..2ec721f4f 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -183,7 +183,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { .unwrap() }; let transaction_fee_margin = PurePercentage::try_from(15).unwrap(); - transaction_fee_margin.add_percent_to(gas_limit_dev_chain * gas_price_major) + transaction_fee_margin.increase_by_percent_for(gas_limit_dev_chain * gas_price_major) }; const AFFORDABLE_PAYMENTS_COUNT: u128 = 2; let tx_fee_needed_to_pay_for_one_payment_minor: u128 = diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 0c583f17b..8bcbdbfa3 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -636,7 +636,7 @@ mod tests { ); let transaction_fee_balance_exactly_required_minor: u128 = { let base_value = (100 * 6 * 53_000) as u128; - let with_margin = TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_value); + let with_margin = TX_FEE_MARGIN_IN_PERCENT.increase_by_percent_for(base_value); multiply_by_billion(with_margin) }; // Transaction fee balance > payments @@ -690,7 +690,7 @@ mod tests { number_of_accounts, tx_computation_units: 55_000, cw_transaction_fee_balance_minor: TX_FEE_MARGIN_IN_PERCENT - .add_percent_to(multiply_by_billion(100 * 3 * 55_000)) + .increase_by_percent_for(multiply_by_billion(100 * 3 * 55_000)) - 1, }), ); @@ -767,7 +767,7 @@ mod tests { let number_of_accounts = 3; let tx_fee_exactly_required_for_single_tx = { let base_minor = multiply_by_billion(55_000 * 100); - TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_minor) + TX_FEE_MARGIN_IN_PERCENT.increase_by_percent_for(base_minor) }; let cw_transaction_fee_balance_minor = tx_fee_exactly_required_for_single_tx - 1; let (qualified_payables, agent) = make_input_for_initial_check_tests( @@ -787,7 +787,7 @@ mod tests { let per_transaction_requirement_minor = { let base_minor = multiply_by_billion(55_000 * 100); - TX_FEE_MARGIN_IN_PERCENT.add_percent_to(base_minor) + TX_FEE_MARGIN_IN_PERCENT.increase_by_percent_for(base_minor) }; assert_eq!( result, @@ -870,7 +870,7 @@ mod tests { let result = subject.consider_adjustment(qualified_payables, &*agent); let per_transaction_requirement_minor = - TX_FEE_MARGIN_IN_PERCENT.add_percent_to(55_000 * multiply_by_billion(123)); + TX_FEE_MARGIN_IN_PERCENT.increase_by_percent_for(55_000 * multiply_by_billion(123)); assert_eq!( result, Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 62ddde7ac..6640d01ea 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -170,7 +170,7 @@ impl PreparatoryAnalyzer { logger: &Logger, ) -> Result, TransactionFeeImmoderateInsufficiency> { let per_txn_requirement_minor_with_margin = - gas_price_margin.add_percent_to(per_transaction_requirement_minor); + gas_price_margin.increase_by_percent_for(per_transaction_requirement_minor); let verified_tx_counts = Self::transaction_counts_verification( cw_transaction_fee_balance_minor, From 4fc1a1ac91e660c865abd3e12145168a6582f95d Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 27 Mar 2025 17:24:32 +0100 Subject: [PATCH 243/250] GH-711: some progress in verify_bill_payment --- .../tests/verify_bill_payment.rs | 35 +- .../tests/verify_bill_payment_utils/utils.rs | 543 +++++++++--------- 2 files changed, 300 insertions(+), 278 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index 2ec721f4f..cb528e30c 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -2,7 +2,7 @@ use crate::verify_bill_payment_utils::utils::{ test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, - NodeProfile, Ports, TestInputsBuilder, WholesomeConfig, + NodeProfile, Ports, TestInputBuilder, WholesomeConfig, }; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; @@ -23,7 +23,7 @@ use std::u128; mod verify_bill_payment_utils; #[test] -fn full_payments_were_processed_for_sufficient_balances() { +fn payments_processed_fully_as_balances_were_sufficient() { // Note: besides the main objectives of this test, it relies on (and so it proves) the premise // that each Node, after it achieves an effective connectivity as making a route is enabled, // activates the accountancy module whereas the first cycle of scanners is unleashed. That's @@ -46,7 +46,7 @@ fn full_payments_were_processed_for_sufficient_balances() { let owed_to_serving_node_2_minor = debt_threshold_wei + 456_789; let owed_to_serving_node_3_minor = debt_threshold_wei + 789_012; let consuming_node_initial_service_fee_balance_minor = debt_threshold_wei * 4; - let test_inputs = TestInputsBuilder::default() + let test_input = TestInputBuilder::default() .consuming_node_initial_service_fee_balance_minor( consuming_node_initial_service_fee_balance_minor, ) @@ -75,7 +75,7 @@ fn full_payments_were_processed_for_sufficient_balances() { }; test_body( - test_inputs, + test_input, assertions_values, stimulate_consuming_node_to_pay_for_test_with_sufficient_funds, activating_serving_nodes_for_test_with_sufficient_funds, @@ -87,7 +87,8 @@ fn stimulate_consuming_node_to_pay_for_test_with_sufficient_funds( real_consuming_node: &MASQRealNode, _wholesome_config: &WholesomeConfig, ) { - for _ in 0..6 { + // 1 + 4 Nodes should be enough to compose a route, right? + for _ in 0..4 { cluster.start_real_node( NodeStartupConfigBuilder::standard() .chain(Chain::Dev) @@ -145,7 +146,8 @@ fn set_old_debts( .into_iter() .map(|balance_minor| Debt::new(balance_minor, quite_long_ago)) .collect_vec(); - DebtsSpecs::new(debts[0], debts[1], debts[2]) + let debt_array = debts.try_into().unwrap(); + DebtsSpecs::new(debt_array) } #[test] @@ -161,14 +163,15 @@ fn payments_were_adjusted_due_to_insufficient_balances() { // Assuming all Nodes rely on the same set of payment thresholds let owed_to_serv_node_1_minor = to_wei(payment_thresholds.debt_threshold_gwei + 5_000_000); let owed_to_serv_node_2_minor = to_wei(payment_thresholds.debt_threshold_gwei + 20_000_000); - // Account of Node 3 will be a victim of tx fee insufficiency and will fall away, as its debt - // is the heaviest, implying the smallest weight evaluated and the last priority compared to - // those two others. + // Account of Node 3 will be a victim of tx fee insufficiency and will drop out, as its debt + // is the heaviest, implying the smallest weight evaluated and the smallest priority compared to + // the two others. let owed_to_serv_node_3_minor = to_wei(payment_thresholds.debt_threshold_gwei + 60_000_000); let enough_balance_for_serving_node_1_and_2 = owed_to_serv_node_1_minor + owed_to_serv_node_2_minor; + let missing_portion_to_the_full_amount = to_wei(2_345_678); let consuming_node_initial_service_fee_balance_minor = - enough_balance_for_serving_node_1_and_2 - to_wei(2_345_678); + enough_balance_for_serving_node_1_and_2 - missing_portion_to_the_full_amount; let gas_price_major = 60; let tx_fee_needed_to_pay_for_one_payment_major = { // We'll need littler funds, but we can stand mild inaccuracy from assuming the use of @@ -185,12 +188,12 @@ fn payments_were_adjusted_due_to_insufficient_balances() { let transaction_fee_margin = PurePercentage::try_from(15).unwrap(); transaction_fee_margin.increase_by_percent_for(gas_limit_dev_chain * gas_price_major) }; - const AFFORDABLE_PAYMENTS_COUNT: u128 = 2; + let affordable_payments_count_by_tx_fee = 2; let tx_fee_needed_to_pay_for_one_payment_minor: u128 = to_wei(tx_fee_needed_to_pay_for_one_payment_major); let consuming_node_transaction_fee_balance_minor = - AFFORDABLE_PAYMENTS_COUNT * tx_fee_needed_to_pay_for_one_payment_minor; - let test_inputs = TestInputsBuilder::default() + affordable_payments_count_by_tx_fee * tx_fee_needed_to_pay_for_one_payment_minor; + let test_input = TestInputBuilder::default() .ui_ports(Ports::new( find_free_port(), find_free_port(), @@ -202,7 +205,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { .consuming_node_initial_service_fee_balance_minor( consuming_node_initial_service_fee_balance_minor, ) - .debts_config(DebtsSpecs::new( + .debts_config(DebtsSpecs::new([ // This account will be the most significant and will deserve the full balance Debt::new( owed_to_serv_node_1_minor, @@ -219,7 +222,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { owed_to_serv_node_3_minor, payment_thresholds.maturity_threshold_sec + 30_000, ), - )) + ])) .payment_thresholds_all_nodes(payment_thresholds) .consuming_node_gas_price_major(gas_price_major) .build(); @@ -240,7 +243,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { }; test_body( - test_inputs, + test_input, assertions_values, stimulate_consuming_node_to_pay_for_test_with_insufficient_funds, activating_serving_nodes_for_test_with_insufficient_funds, diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index cb1876c1e..58fcb8134 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -1,6 +1,7 @@ // Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use bip39::{Language, Mnemonic, Seed}; +use ethereum_types::H256; use futures::Future; use itertools::Itertools; use lazy_static::lazy_static; @@ -41,46 +42,39 @@ use std::thread; use std::time::{Duration, Instant, SystemTime}; use tiny_hderive::bip32::ExtendedPrivKey; use web3::transports::Http; -use web3::types::{Address, Bytes, SignedTransaction, TransactionParameters, TransactionRequest}; +use web3::types::{ + Address, Bytes, SignedTransaction, TransactionParameters, TransactionReceipt, + TransactionRequest, +}; use web3::Web3; pub type StimulateConsumingNodePayments = fn(&mut MASQNodeCluster, &MASQRealNode, &WholesomeConfig); -pub type StartServingNodesAndLetThemPerformReceivablesCheck = +pub type StartServingNodesAndLetThemActivateTheirAccountancy = fn(&mut MASQNodeCluster, &WholesomeConfig) -> [MASQRealNode; 3]; pub fn test_body( - test_inputs: TestInputs, + test_inputs: TestInput, assertions_values: AssertionsValues, - stimulate_consuming_node_to_pay: StimulateConsumingNodePayments, - start_serving_nodes_and_activate_their_accountancy: StartServingNodesAndLetThemPerformReceivablesCheck, + stimulate_consuming_node_payments: StimulateConsumingNodePayments, + start_serving_nodes_and_let_them_activate_their_accountancy: StartServingNodesAndLetThemActivateTheirAccountancy, ) { // It's important to prevent the blockchain server handle being dropped too early let (mut cluster, global_values, _blockchain_server) = establish_test_frame(test_inputs); - let consuming_node = - global_values.prepare_consuming_node(&mut cluster, &global_values.blockchain_interfaces); + let consuming_node = global_values.prepare_consuming_node(&mut cluster); let serving_nodes_array = global_values.prepare_serving_nodes(&mut cluster); global_values.set_up_consuming_node_db(&serving_nodes_array, &consuming_node); global_values.set_up_serving_nodes_databases(&serving_nodes_array, &consuming_node); let wholesome_config = WholesomeConfig::new(global_values, consuming_node, serving_nodes_array); wholesome_config.assert_expected_wallet_addresses(); + let cn_common = &wholesome_config.consuming_node.common; let real_consuming_node = cluster.start_named_real_node( - &wholesome_config - .consuming_node - .common - .prepared_node - .node_docker_name, - wholesome_config.consuming_node.common.prepared_node.index, - wholesome_config - .consuming_node - .common - .startup_config_opt - .borrow_mut() - .take() - .unwrap(), + &cn_common.prepared_node.node_docker_name, + cn_common.prepared_node.index, + cn_common.startup_config_opt.borrow_mut().take().unwrap(), ); - stimulate_consuming_node_to_pay(&mut cluster, &real_consuming_node, &wholesome_config); + stimulate_consuming_node_payments(&mut cluster, &real_consuming_node, &wholesome_config); let timeout_start = Instant::now(); while !wholesome_config @@ -94,7 +88,7 @@ pub fn test_body( } wholesome_config.assert_payments_via_direct_blockchain_scanning(&assertions_values); - let _ = start_serving_nodes_and_activate_their_accountancy( + let _ = start_serving_nodes_and_let_them_activate_their_accountancy( &mut cluster, // So that individual Configs can be pulled out and used &wholesome_config, @@ -107,7 +101,7 @@ const MNEMONIC_PHRASE: &str = "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ lamp absent write kind term toddler sphere ripple idle dragon curious hold"; -pub struct TestInputs { +pub struct TestInput { // The contract owner wallet is populated with 100 ETH as defined in the set of commands with // which we start up the Ganache server. // @@ -119,16 +113,16 @@ pub struct TestInputs { } #[derive(Default)] -pub struct TestInputsBuilder { +pub struct TestInputBuilder { ui_ports_opt: Option, consuming_node_initial_tx_fee_balance_minor_opt: Option, consuming_node_initial_service_fee_balance_minor_opt: Option, debts_config_opt: Option, payment_thresholds_all_nodes_opt: Option, - consuming_node_gas_price_opt: Option, + consuming_node_gas_price_major_opt: Option, } -impl TestInputsBuilder { +impl TestInputBuilder { pub fn ui_ports(mut self, ports: Ports) -> Self { self.ui_ports_opt = Some(ports); self @@ -155,11 +149,11 @@ impl TestInputsBuilder { } pub fn consuming_node_gas_price_major(mut self, gas_price: u64) -> Self { - self.consuming_node_gas_price_opt = Some(gas_price); + self.consuming_node_gas_price_major_opt = Some(gas_price); self } - pub fn build(self) -> TestInputs { + pub fn build(self) -> TestInput { let mut debts = self .debts_config_opt .expect("You forgot providing a mandatory input: debts config") @@ -170,7 +164,7 @@ impl TestInputsBuilder { let mut serving_nodes_ui_ports_opt = serving_nodes_ui_ports_opt.to_vec(); let consuming_node = ConsumingNodeProfile { ui_port_opt: consuming_node_ui_port_opt, - gas_price_opt: self.consuming_node_gas_price_opt, + gas_price_opt: self.consuming_node_gas_price_major_opt, initial_tx_fee_balance_minor_opt: self.consuming_node_initial_tx_fee_balance_minor_opt, initial_service_fee_balance_minor: self .consuming_node_initial_service_fee_balance_minor_opt @@ -197,7 +191,7 @@ impl TestInputsBuilder { serving_nodes: core::array::from_fn(|_| serving_nodes.remove(0)), }; - TestInputs { + TestInput { payment_thresholds_all_nodes: self .payment_thresholds_all_nodes_opt .expect("Mandatory input not provided: payment thresholds"), @@ -208,10 +202,8 @@ impl TestInputsBuilder { fn resolve_ports(ui_ports_opt: Option) -> (Option, [Option; 3]) { match ui_ports_opt { Some(ui_ports) => { - let mut ui_ports_as_opt = - ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); - let serving_nodes_array: [Option; 3] = - core::array::from_fn(|_| ui_ports_as_opt.remove(0)); + let ui_ports_as_opt = ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); + let serving_nodes_array: [Option; 3] = ui_ports_as_opt.try_into().unwrap(); (Some(ui_ports.consuming_node), serving_nodes_array) } None => Default::default(), @@ -259,21 +251,236 @@ impl FinalServiceFeeBalancesByServingNodes { pub struct BlockchainParams { chain: Chain, server_url: String, - contract_owner_addr: Address, contract_owner_wallet: Wallet, seed: Seed, } -struct BlockchainInterfaces { - standard_blockchain_interface: Box, - web3: Web3, +struct ExtendedBlockchainInterface { + node_standard_interface: Box, + raw_interface: RawBlockchainInterface, +} + +impl ExtendedBlockchainInterface { + fn new( + node_internal_interface: Box, + raw_interface: RawBlockchainInterface, + ) -> Self { + Self { + node_standard_interface: node_internal_interface, + raw_interface, + } + } + + fn deploy_smart_contract(&self, wallet: &Wallet, chain: Chain) -> Address { + let contract = load_contract_in_bytes(); + let tx = TransactionParameters { + nonce: Some(ethereum_types::U256::zero()), + to: None, + gas: *GAS_LIMIT, + gas_price: Some(*GAS_PRICE), + value: ethereum_types::U256::zero(), + data: Bytes(contract), + chain_id: Some(chain.rec().num_chain_id), + }; + let signed_tx = self.raw_interface.await_sign_transaction(tx, wallet); + match self.raw_interface.await_send_raw_transaction(signed_tx) { + Ok(tx_hash) => match self.raw_interface.await_transaction_receipt(tx_hash) { + Ok(Some(tx_receipt)) => tx_receipt.contract_address.unwrap(), + Ok(None) => panic!("Contract deployment failed Ok(None)"), + Err(e) => panic!("Contract deployment failed {:?}", e), + }, + Err(e) => panic!("Contract deployment failed {:?}", e), + } + } + + fn transfer_transaction_fee_amount_to_address( + &self, + from_wallet: &Wallet, + to_wallet: &Wallet, + amount_minor: u128, + transaction_nonce: u64, + ) { + let tx = TransactionRequest { + from: from_wallet.address(), + to: Some(to_wallet.address()), + gas: Some(*GAS_LIMIT), + gas_price: Some(*GAS_PRICE), + value: Some(ethereum_types::U256::from(amount_minor)), + data: None, + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + condition: None, + }; + match self + .raw_interface + .await_unlock_account(from_wallet.address(), "", None) + { + Ok(was_successful) => { + if was_successful { + eprintln!("Account {} unlocked for a single transaction", from_wallet) + } else { + panic!( + "Couldn't unlock account {} for the purpose of signing the next transaction", + from_wallet + ) + } + } + Err(e) => panic!( + "Attempt to unlock account {:?} failed at {:?}", + from_wallet.address(), + e + ), + } + match self.raw_interface.await_send_transaction(tx) { + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", + tx_hash, amount_minor, from_wallet, to_wallet + ), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } + } + + fn transfer_service_fee_amount_to_address( + &self, + contract_addr: Address, + from_wallet: &Wallet, + to_wallet: &Wallet, + amount_minor: u128, + transaction_nonce: u64, + chain: Chain, + ) { + let data = transaction_data_web3(to_wallet, amount_minor); + let tx = TransactionParameters { + nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), + to: Some(contract_addr), + gas: *GAS_LIMIT, + gas_price: Some(*GAS_PRICE), + value: ethereum_types::U256::zero(), + data: Bytes(data.to_vec()), + chain_id: Some(chain.rec().num_chain_id), + }; + let signed_tx = self.raw_interface.await_sign_transaction(tx, from_wallet); + match &self.raw_interface.await_send_raw_transaction(signed_tx) { + Ok(tx_hash) => eprintln!( + "Transaction {:?} of {} wei of MASQ was sent from wallet {} to {}", + tx_hash, amount_minor, from_wallet, to_wallet + ), + Err(e) => panic!("Transaction for token transfer failed {:?}", e), + } + } + + fn single_balance_assertion( + &self, + wallet: &Wallet, + expected_balance: u128, + balance_specification: &str, + balance_fetcher: fn(&dyn LowBlockchainInt, &Wallet) -> ResultForBalance, + ) { + let actual_balance = { + let lower_blockchain_int = self.node_standard_interface.lower_interface(); + balance_fetcher(lower_blockchain_int, &wallet).unwrap_or_else(|_| { + panic!( + "Failed to retrieve {} for {}", + balance_specification, wallet + ) + }) + }; + assert_eq!( + actual_balance, + web3::types::U256::from(expected_balance), + "Actual {} {} doesn't much with expected {} for {}", + balance_specification, + actual_balance, + expected_balance, + wallet + ); + } + + fn assert_balances( + &self, + wallet: &Wallet, + expected_tx_fee_balance: u128, + expected_service_fee_balance: u128, + ) { + self.single_balance_assertion( + wallet, + expected_tx_fee_balance, + "ETH balance", + |blockchain_interface, wallet| blockchain_interface.get_transaction_fee_balance(wallet), + ); + + self.single_balance_assertion( + wallet, + expected_service_fee_balance, + "MASQ balance", + |blockchain_interface, wallet| blockchain_interface.get_service_fee_balance(wallet), + ); + } +} + +struct RawBlockchainInterface { + web3_transport: Web3, +} + +impl RawBlockchainInterface { + fn new(web3_transport: Web3) -> Self { + Self { web3_transport } + } + fn await_unlock_account( + &self, + address: Address, + password: &str, + duration: Option, + ) -> Result { + self.web3_transport + .personal() + .unlock_account(address, password, duration) + .wait() + } + fn await_send_transaction(&self, tx: TransactionRequest) -> Result { + self.web3_transport.eth().send_transaction(tx).wait() + } + + fn await_sign_transaction( + &self, + tx: TransactionParameters, + signing_wallet: &Wallet, + ) -> SignedTransaction { + let secret = &signing_wallet + .prepare_secp256k1_secret() + .expect("wallet without secret"); + self.web3_transport + .accounts() + .sign_transaction(tx, secret) + .wait() + .expect("transaction preparation failed") + } + + fn await_send_raw_transaction( + &self, + tx: SignedTransaction, + ) -> Result { + self.web3_transport + .eth() + .send_raw_transaction(tx.raw_transaction) + .wait() + } + + fn await_transaction_receipt( + &self, + tx_hash: H256, + ) -> Result, web3::error::Error> { + self.web3_transport + .eth() + .transaction_receipt(tx_hash) + .wait() + } } pub struct GlobalValues { - pub test_inputs: TestInputs, + pub test_inputs: TestInput, pub blockchain_params: BlockchainParams, pub now_in_common: SystemTime, - blockchain_interfaces: BlockchainInterfaces, + blockchain_interfaces: ExtendedBlockchainInterface, } pub struct WholesomeConfig { @@ -287,8 +494,7 @@ pub struct DebtsSpecs { } impl DebtsSpecs { - pub fn new(node_1: Debt, node_2: Debt, node_3: Debt) -> Self { - let debts = [node_1, node_2, node_3]; + pub fn new(debts: [Debt; 3]) -> Self { Self { debts } } } @@ -376,7 +582,7 @@ impl NodeProfile for ServingNodeProfile { } pub fn establish_test_frame( - test_inputs: TestInputs, + test_inputs: TestInput, ) -> (MASQNodeCluster, GlobalValues, BlockchainServer) { let now = SystemTime::now(); let cluster = match MASQNodeCluster::start() { @@ -387,27 +593,27 @@ pub fn establish_test_frame( blockchain_server.start(); blockchain_server.wait_until_ready(); let server_url = blockchain_server.url().to_string(); + let chain = cluster.chain(); let (event_loop_handle, http) = Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); let web3 = Web3::new(http.clone()); + let raw_blockchain_interface = RawBlockchainInterface::new(web3); + let node_standard_interface = + Box::new(BlockchainInterfaceWeb3::new(http, event_loop_handle, chain)); + let blockchain_interfaces = + ExtendedBlockchainInterface::new(node_standard_interface, raw_blockchain_interface); let seed = make_seed(); let (contract_owner_wallet, _) = make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); - let chain = cluster.chain(); - let contract_owner_addr = deploy_smart_contract(&contract_owner_wallet, &web3, chain); - let blockchain_interface = - Box::new(BlockchainInterfaceWeb3::new(http, event_loop_handle, chain)); + let contract_owner_addr = + blockchain_interfaces.deploy_smart_contract(&contract_owner_wallet, chain); let blockchain_params = BlockchainParams { chain, server_url, - contract_owner_addr, contract_owner_wallet, seed, }; - let blockchain_interfaces = BlockchainInterfaces { - standard_blockchain_interface: blockchain_interface, - web3, - }; + let global_values = GlobalValues { test_inputs, blockchain_params, @@ -463,176 +669,6 @@ lazy_static! { 1_000_000_u64.try_into().expect("Gas limit, internal error"); } -fn deploy_smart_contract(wallet: &Wallet, web3: &Web3, chain: Chain) -> Address { - let contract = load_contract_in_bytes(); - let tx = TransactionParameters { - nonce: Some(ethereum_types::U256::zero()), - to: None, - gas: *GAS_LIMIT, - gas_price: Some(*GAS_PRICE), - value: ethereum_types::U256::zero(), - data: Bytes(contract), - chain_id: Some(chain.rec().num_chain_id), - }; - let signed_tx = primitive_sign_transaction(web3, tx, wallet); - match web3 - .eth() - .send_raw_transaction(signed_tx.raw_transaction) - .wait() - { - Ok(tx_hash) => match web3.eth().transaction_receipt(tx_hash).wait() { - Ok(Some(tx_receipt)) => tx_receipt.contract_address.unwrap(), - Ok(None) => panic!("Contract deployment failed Ok(None)"), - Err(e) => panic!("Contract deployment failed {:?}", e), - }, - Err(e) => panic!("Contract deployment failed {:?}", e), - } -} - -fn transfer_service_fee_amount_to_address( - contract_addr: Address, - from_wallet: &Wallet, - to_wallet: &Wallet, - amount_minor: u128, - transaction_nonce: u64, - web3: &Web3, - chain: Chain, -) { - let data = transaction_data_web3(to_wallet, amount_minor); - let tx = TransactionParameters { - nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), - to: Some(contract_addr), - gas: *GAS_LIMIT, - gas_price: Some(*GAS_PRICE), - value: ethereum_types::U256::zero(), - data: Bytes(data.to_vec()), - chain_id: Some(chain.rec().num_chain_id), - }; - let signed_tx = primitive_sign_transaction(web3, tx, from_wallet); - match web3 - .eth() - .send_raw_transaction(signed_tx.raw_transaction) - .wait() - { - Ok(tx_hash) => eprintln!( - "Transaction {:?} of {} wei of MASQ was sent from wallet {} to {}", - tx_hash, amount_minor, from_wallet, to_wallet - ), - Err(e) => panic!("Transaction for token transfer failed {:?}", e), - } -} - -fn primitive_sign_transaction( - web3: &Web3, - tx: TransactionParameters, - signing_wallet: &Wallet, -) -> SignedTransaction { - let secret = &signing_wallet - .prepare_secp256k1_secret() - .expect("wallet without secret"); - web3.accounts() - .sign_transaction(tx, secret) - .wait() - .expect("transaction preparation failed") -} - -fn transfer_transaction_fee_amount_to_address( - from_wallet: &Wallet, - to_wallet: &Wallet, - amount_minor: u128, - transaction_nonce: u64, - web3: &Web3, -) { - let tx = TransactionRequest { - from: from_wallet.address(), - to: Some(to_wallet.address()), - gas: Some(*GAS_LIMIT), - gas_price: Some(*GAS_PRICE), - value: Some(ethereum_types::U256::from(amount_minor)), - data: None, - nonce: Some(ethereum_types::U256::try_from(transaction_nonce).expect("Internal error")), - condition: None, - }; - match web3 - .personal() - .unlock_account(from_wallet.address(), "", None) - .wait() - { - Ok(was_successful) => { - if was_successful { - eprintln!("Account {} unlocked for a single transaction", from_wallet) - } else { - panic!( - "Couldn't unlock account {} for the purpose of signing the next transaction", - from_wallet - ) - } - } - Err(e) => panic!( - "Attempt to unlock account {:?} failed at {:?}", - from_wallet.address(), - e - ), - } - match web3.eth().send_transaction(tx).wait() { - Ok(tx_hash) => eprintln!( - "Transaction {:?} of {} wei of ETH was sent from wallet {:?} to {:?}", - tx_hash, amount_minor, from_wallet, to_wallet - ), - Err(e) => panic!("Transaction for token transfer failed {:?}", e), - } -} - -fn assert_balances( - wallet: &Wallet, - blockchain_interface: &dyn BlockchainInterface, - expected_eth_balance: u128, - expected_token_balance: u128, -) { - single_balance_assertion( - blockchain_interface, - wallet, - expected_eth_balance, - "ETH balance", - |blockchain_interface, wallet| blockchain_interface.get_transaction_fee_balance(wallet), - ); - - single_balance_assertion( - blockchain_interface, - wallet, - expected_token_balance, - "MASQ balance", - |blockchain_interface, wallet| blockchain_interface.get_service_fee_balance(wallet), - ); -} - -fn single_balance_assertion( - blockchain_interface: &dyn BlockchainInterface, - wallet: &Wallet, - expected_balance: u128, - balance_specification: &str, - balance_fetcher: fn(&dyn LowBlockchainInt, &Wallet) -> ResultForBalance, -) { - let actual_balance = { - let lower_blockchain_int = blockchain_interface.lower_interface(); - balance_fetcher(lower_blockchain_int, &wallet).unwrap_or_else(|_| { - panic!( - "Failed to retrieve {} for {}", - balance_specification, wallet - ) - }) - }; - assert_eq!( - actual_balance, - web3::types::U256::from(expected_balance), - "Actual {} {} doesn't much with expected {} for {}", - balance_specification, - actual_balance, - expected_balance, - wallet - ); -} - fn make_node_wallet_and_private_key(seed: &Seed, derivation_path: &str) -> (Wallet, String) { let extended_private_key = ExtendedPrivKey::derive(&seed.as_ref(), derivation_path).unwrap(); let str_private_key: String = extended_private_key.secret().to_hex(); @@ -667,11 +703,7 @@ impl GlobalValues { (config_builder.build(), node_wallet) } - fn prepare_consuming_node( - &self, - cluster: &mut MASQNodeCluster, - blockchain_interfaces: &BlockchainInterfaces, - ) -> ConsumingNode { + fn prepare_consuming_node(&self, cluster: &mut MASQNodeCluster) -> ConsumingNode { let consuming_node_profile = self.test_inputs.node_profiles.consuming_node.clone(); let initial_service_fee_balance_minor = consuming_node_profile.initial_service_fee_balance_minor; @@ -680,26 +712,25 @@ impl GlobalValues { let (consuming_node_config, consuming_node_wallet) = self.get_node_config_and_wallet(&consuming_node_profile); let initial_transaction_fee_balance = initial_tx_fee_balance_opt.unwrap_or(ONE_ETH_IN_WEI); - transfer_transaction_fee_amount_to_address( - &self.blockchain_params.contract_owner_wallet, - &consuming_node_wallet, - initial_transaction_fee_balance, - 1, - &blockchain_interfaces.web3, - ); - transfer_service_fee_amount_to_address( - self.blockchain_params.contract_owner_addr, - &self.blockchain_params.contract_owner_wallet, - &consuming_node_wallet, - initial_service_fee_balance_minor, - 2, - &blockchain_interfaces.web3, - self.blockchain_params.chain, - ); + self.blockchain_interfaces + .transfer_transaction_fee_amount_to_address( + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + initial_transaction_fee_balance, + 1, + ); + self.blockchain_interfaces + .transfer_service_fee_amount_to_address( + self.blockchain_params.contract_owner_wallet.address(), + &self.blockchain_params.contract_owner_wallet, + &consuming_node_wallet, + initial_service_fee_balance_minor, + 2, + self.blockchain_params.chain, + ); - assert_balances( + self.blockchain_interfaces.assert_balances( &consuming_node_wallet, - blockchain_interfaces.standard_blockchain_interface.as_ref(), initial_transaction_fee_balance, initial_service_fee_balance_minor, ); @@ -772,14 +803,8 @@ impl GlobalValues { .receivable_dao .more_money_receivable(timestamp, &consuming_node.consuming_wallet, balance) .unwrap(); - assert_balances( - &serving_node.earning_wallet, - self.blockchain_interfaces - .standard_blockchain_interface - .as_ref(), - 0, - 0, - ); + self.blockchain_interfaces + .assert_balances(&serving_node.earning_wallet, 0, 0); Self::set_start_block_to_zero(&serving_node.common.prepared_node.db_path) }) } @@ -843,14 +868,9 @@ impl WholesomeConfig { } fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { - let blockchain_interface = self - .global_values - .blockchain_interfaces - .standard_blockchain_interface - .as_ref(); - assert_balances( + let blockchain_interfaces = &self.global_values.blockchain_interfaces; + blockchain_interfaces.assert_balances( &self.consuming_node.consuming_wallet, - blockchain_interface, assertions_values.final_consuming_node_transaction_fee_balance_minor, assertions_values.final_consuming_node_service_fee_balance_minor, ); @@ -860,9 +880,8 @@ impl WholesomeConfig { .into_iter() .zip(self.serving_nodes.iter()) .for_each(|(expected_remaining_owed_value, serving_node)| { - assert_balances( + blockchain_interfaces.assert_balances( &serving_node.earning_wallet, - blockchain_interface, 0, expected_remaining_owed_value, ); From 8dd256d0c1e6299e22d574c39339266e4a042d69 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 7 Apr 2025 23:56:19 +0200 Subject: [PATCH 244/250] GH-711: a few more fixes in verify_bill_payments.rs --- .../tests/verify_bill_payment.rs | 6 +- .../tests/verify_bill_payment_utils/utils.rs | 188 +++++++++--------- ...st => verify_bill_payments_smart_contract} | 0 3 files changed, 102 insertions(+), 92 deletions(-) rename node/src/test_utils/test_input_data/{smart_contract_for_on_blockchain_test => verify_bill_payments_smart_contract} (100%) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index cb528e30c..b121d4148 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -2,7 +2,7 @@ use crate::verify_bill_payment_utils::utils::{ test_body, to_wei, AssertionsValues, Debt, DebtsSpecs, FinalServiceFeeBalancesByServingNodes, - NodeProfile, Ports, TestInputBuilder, WholesomeConfig, + NodeProfile, TestInputBuilder, UiPorts, WholesomeConfig, }; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; @@ -194,7 +194,7 @@ fn payments_were_adjusted_due_to_insufficient_balances() { let consuming_node_transaction_fee_balance_minor = affordable_payments_count_by_tx_fee * tx_fee_needed_to_pay_for_one_payment_minor; let test_input = TestInputBuilder::default() - .ui_ports(Ports::new( + .ui_ports(UiPorts::new( find_free_port(), find_free_port(), find_free_port(), @@ -289,7 +289,7 @@ fn activating_serving_nodes_for_test_with_insufficient_funds( node_config, ); let ui_port = serving_node_attributes - .serving_node_profile + .node_profile .ui_port() .expect("ui port missing"); diff --git a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs index 58fcb8134..cdace9d32 100644 --- a/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs +++ b/multinode_integration_tests/tests/verify_bill_payment_utils/utils.rs @@ -21,9 +21,7 @@ use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; use node_lib::blockchain::blockchain_interface::blockchain_interface_web3::{ BlockchainInterfaceWeb3, REQUESTS_IN_PARALLEL, }; -use node_lib::blockchain::blockchain_interface::lower_level_interface::{ - LowBlockchainInt, ResultForBalance, -}; +use node_lib::blockchain::blockchain_interface::lower_level_interface::LowBlockchainInt; use node_lib::blockchain::blockchain_interface::BlockchainInterface; use node_lib::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, ExternalData, @@ -97,10 +95,6 @@ pub fn test_body( wholesome_config.assert_serving_nodes_addressed_received_payments(&assertions_values) } -const MNEMONIC_PHRASE: &str = - "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ - lamp absent write kind term toddler sphere ripple idle dragon curious hold"; - pub struct TestInput { // The contract owner wallet is populated with 100 ETH as defined in the set of commands with // which we start up the Ganache server. @@ -114,7 +108,7 @@ pub struct TestInput { #[derive(Default)] pub struct TestInputBuilder { - ui_ports_opt: Option, + ui_ports_opt: Option, consuming_node_initial_tx_fee_balance_minor_opt: Option, consuming_node_initial_service_fee_balance_minor_opt: Option, debts_config_opt: Option, @@ -123,7 +117,7 @@ pub struct TestInputBuilder { } impl TestInputBuilder { - pub fn ui_ports(mut self, ports: Ports) -> Self { + pub fn ui_ports(mut self, ports: UiPorts) -> Self { self.ui_ports_opt = Some(ports); self } @@ -199,7 +193,7 @@ impl TestInputBuilder { } } - fn resolve_ports(ui_ports_opt: Option) -> (Option, [Option; 3]) { + fn resolve_ports(ui_ports_opt: Option) -> (Option, [Option; 3]) { match ui_ports_opt { Some(ui_ports) => { let ui_ports_as_opt = ui_ports.serving_nodes.into_iter().map(Some).collect_vec(); @@ -255,18 +249,51 @@ pub struct BlockchainParams { seed: Seed, } +impl BlockchainParams { + fn new( + chain: Chain, + server_url: String, + blockchain_interface: &ExtendedBlockchainInterface, + ) -> Self { + let seed = make_seed(); + let (contract_owner_wallet, _) = + make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); + let contract_owner_addr = + blockchain_interface.deploy_smart_contract(&contract_owner_wallet, chain); + + assert_eq!( + contract_owner_addr, + chain.rec().contract, + "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ + Resulted contact addr {:?} doesn't much what's expected: {:?}", + contract_owner_addr, + chain.rec().contract + ); + + BlockchainParams { + chain, + server_url, + contract_owner_wallet, + seed, + } + } +} + struct ExtendedBlockchainInterface { node_standard_interface: Box, raw_interface: RawBlockchainInterface, } impl ExtendedBlockchainInterface { - fn new( - node_internal_interface: Box, - raw_interface: RawBlockchainInterface, - ) -> Self { + fn new(chain: Chain, server_url: &str) -> Self { + let (event_loop_handle, http) = + Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); + let web3 = Web3::new(http.clone()); + let raw_interface = RawBlockchainInterface::new(web3); + let node_standard_interface = + Box::new(BlockchainInterfaceWeb3::new(http, event_loop_handle, chain)); Self { - node_standard_interface: node_internal_interface, + node_standard_interface, raw_interface, } } @@ -372,23 +399,20 @@ impl ExtendedBlockchainInterface { &self, wallet: &Wallet, expected_balance: u128, - balance_specification: &str, - balance_fetcher: fn(&dyn LowBlockchainInt, &Wallet) -> ResultForBalance, + asserted_balance: AssertedBalance, ) { - let actual_balance = { - let lower_blockchain_int = self.node_standard_interface.lower_interface(); - balance_fetcher(lower_blockchain_int, &wallet).unwrap_or_else(|_| { - panic!( - "Failed to retrieve {} for {}", - balance_specification, wallet - ) - }) + let balance_fetcher = match asserted_balance { + AssertedBalance::TransactionFee => LowBlockchainInt::get_transaction_fee_balance, + AssertedBalance::ServiceFee => LowBlockchainInt::get_service_fee_balance, }; + let lower_blockchain_int = self.node_standard_interface.lower_interface(); + let actual_balance = balance_fetcher(lower_blockchain_int, &wallet) + .unwrap_or_else(|_| panic!("Failed to retrieve {:?} for {}", asserted_balance, wallet)); assert_eq!( actual_balance, web3::types::U256::from(expected_balance), - "Actual {} {} doesn't much with expected {} for {}", - balance_specification, + "Actual {:?} {} doesn't much with expected {} for {}", + asserted_balance, actual_balance, expected_balance, wallet @@ -404,19 +428,30 @@ impl ExtendedBlockchainInterface { self.single_balance_assertion( wallet, expected_tx_fee_balance, - "ETH balance", - |blockchain_interface, wallet| blockchain_interface.get_transaction_fee_balance(wallet), + AssertedBalance::TransactionFee, ); self.single_balance_assertion( wallet, expected_service_fee_balance, - "MASQ balance", - |blockchain_interface, wallet| blockchain_interface.get_service_fee_balance(wallet), + AssertedBalance::ServiceFee, ); } } +#[derive(Debug, Clone, Copy)] +enum AssertedBalance { + TransactionFee, + ServiceFee, +} + +lazy_static! { + static ref GAS_PRICE: ethereum_types::U256 = + 50_u64.try_into().expect("Gas price, internal error"); + static ref GAS_LIMIT: ethereum_types::U256 = + 1_000_000_u64.try_into().expect("Gas limit, internal error"); +} + struct RawBlockchainInterface { web3_transport: Web3, } @@ -480,7 +515,7 @@ pub struct GlobalValues { pub test_inputs: TestInput, pub blockchain_params: BlockchainParams, pub now_in_common: SystemTime, - blockchain_interfaces: ExtendedBlockchainInterface, + blockchain_interface: ExtendedBlockchainInterface, } pub struct WholesomeConfig { @@ -589,49 +624,31 @@ pub fn establish_test_frame( Ok(cluster) => cluster, Err(e) => panic!("{}", e), }; - let blockchain_server = BlockchainServer::new("ganache-cli"); - blockchain_server.start(); - blockchain_server.wait_until_ready(); - let server_url = blockchain_server.url().to_string(); + let (blockchain_server, server_url) = start_blockchain_server(); let chain = cluster.chain(); - let (event_loop_handle, http) = - Http::with_max_parallel(&server_url, REQUESTS_IN_PARALLEL).unwrap(); - let web3 = Web3::new(http.clone()); - let raw_blockchain_interface = RawBlockchainInterface::new(web3); - let node_standard_interface = - Box::new(BlockchainInterfaceWeb3::new(http, event_loop_handle, chain)); - let blockchain_interfaces = - ExtendedBlockchainInterface::new(node_standard_interface, raw_blockchain_interface); - let seed = make_seed(); - let (contract_owner_wallet, _) = - make_node_wallet_and_private_key(&seed, &derivation_path(0, 0)); - let contract_owner_addr = - blockchain_interfaces.deploy_smart_contract(&contract_owner_wallet, chain); - let blockchain_params = BlockchainParams { - chain, - server_url, - contract_owner_wallet, - seed, - }; - + let blockchain_interface = ExtendedBlockchainInterface::new(chain, &server_url); + let blockchain_params = BlockchainParams::new(chain, server_url, &blockchain_interface); let global_values = GlobalValues { test_inputs, blockchain_params, - blockchain_interfaces, + blockchain_interface, now_in_common: now, }; - assert_eq!( - contract_owner_addr, - chain.rec().contract, - "Either the contract has been modified or Ganache is not accurately mimicking Ethereum. \ - Resulted contact addr {:?} doesn't much what's expected: {:?}", - contract_owner_addr, - chain.rec().contract - ); - (cluster, global_values, blockchain_server) } +fn start_blockchain_server() -> (BlockchainServer, String) { + let blockchain_server = BlockchainServer::new("ganache-cli"); + blockchain_server.start(); + blockchain_server.wait_until_ready(); + let url = blockchain_server.url().to_string(); + (blockchain_server, url) +} + +const MNEMONIC_PHRASE: &str = + "timber cage wide hawk phone shaft pattern movie army dizzy hen tackle \ + lamp absent write kind term toddler sphere ripple idle dragon curious hold"; + fn make_seed() -> Seed { let mnemonic = Mnemonic::from_phrase(MNEMONIC_PHRASE, Language::English).unwrap(); Seed::new(&mnemonic, "") @@ -650,8 +667,8 @@ fn make_db_init_config(chain: Chain) -> DbInitializationConfig { } fn load_contract_in_bytes() -> Vec { - let file_path = test_input_data_standard_dir().join("smart_contract_for_on_blockchain_test"); - let mut file = File::open(file_path).expect("couldn't acquire a handle to the data file"); + let file_path = test_input_data_standard_dir().join("verify_bill_payments_smart_contract"); + let mut file = File::open(file_path).expect("couldn't acquire a file handle"); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let data = data @@ -659,14 +676,7 @@ fn load_contract_in_bytes() -> Vec { .filter(|char| !char.is_whitespace()) .collect::(); data.from_hex::>() - .expect("bad contract: contains non-hexadecimal characters") -} - -lazy_static! { - static ref GAS_PRICE: ethereum_types::U256 = - 50_u64.try_into().expect("Gas price, internal error"); - static ref GAS_LIMIT: ethereum_types::U256 = - 1_000_000_u64.try_into().expect("Gas limit, internal error"); + .expect("contract contains non-hexadecimal characters") } fn make_node_wallet_and_private_key(seed: &Seed, derivation_path: &str) -> (Wallet, String) { @@ -712,14 +722,14 @@ impl GlobalValues { let (consuming_node_config, consuming_node_wallet) = self.get_node_config_and_wallet(&consuming_node_profile); let initial_transaction_fee_balance = initial_tx_fee_balance_opt.unwrap_or(ONE_ETH_IN_WEI); - self.blockchain_interfaces + self.blockchain_interface .transfer_transaction_fee_amount_to_address( &self.blockchain_params.contract_owner_wallet, &consuming_node_wallet, initial_transaction_fee_balance, 1, ); - self.blockchain_interfaces + self.blockchain_interface .transfer_service_fee_amount_to_address( self.blockchain_params.contract_owner_wallet.address(), &self.blockchain_params.contract_owner_wallet, @@ -729,7 +739,7 @@ impl GlobalValues { self.blockchain_params.chain, ); - self.blockchain_interfaces.assert_balances( + self.blockchain_interface.assert_balances( &consuming_node_wallet, initial_transaction_fee_balance, initial_service_fee_balance_minor, @@ -803,7 +813,7 @@ impl GlobalValues { .receivable_dao .more_money_receivable(timestamp, &consuming_node.consuming_wallet, balance) .unwrap(); - self.blockchain_interfaces + self.blockchain_interface .assert_balances(&serving_node.earning_wallet, 0, 0); Self::set_start_block_to_zero(&serving_node.common.prepared_node.db_path) }) @@ -860,7 +870,7 @@ impl WholesomeConfig { &serving_node_actual, expected_wallet_addr, "{:?} wallet {} mismatched with expected {}", - serving_node.serving_node_profile.serving_node_by_name, + serving_node.node_profile.serving_node_by_name, serving_node_actual, expected_wallet_addr ); @@ -868,7 +878,7 @@ impl WholesomeConfig { } fn assert_payments_via_direct_blockchain_scanning(&self, assertions_values: &AssertionsValues) { - let blockchain_interfaces = &self.global_values.blockchain_interfaces; + let blockchain_interfaces = &self.global_values.blockchain_interface; blockchain_interfaces.assert_balances( &self.consuming_node.consuming_wallet, assertions_values.final_consuming_node_transaction_fee_balance_minor, @@ -900,7 +910,7 @@ impl WholesomeConfig { .iter() .zip(actually_received_payments.into_iter()) .for_each(|(serving_node, received_payment)| { - let original_debt = serving_node.serving_node_profile.debt_specs().balance_minor; + let original_debt = serving_node.node_profile.debt_specs().balance_minor; let expected_final_balance = original_debt - received_payment; Self::wait_for_exact_balance_in_receivables( &serving_node.receivable_dao, @@ -927,12 +937,12 @@ impl WholesomeConfig { pub const ONE_ETH_IN_WEI: u128 = 10_u128.pow(18); -pub struct Ports { +pub struct UiPorts { consuming_node: u16, serving_nodes: [u16; 3], } -impl Ports { +impl UiPorts { pub fn new( consuming_node: u16, serving_node_1: u16, @@ -971,7 +981,7 @@ pub struct ConsumingNode { #[derive(Debug)] pub struct ServingNode { - pub serving_node_profile: ServingNodeProfile, + pub node_profile: ServingNodeProfile, pub common: NodeAttributesCommon, pub earning_wallet: Wallet, pub receivable_dao: ReceivableDaoReal, @@ -979,7 +989,7 @@ pub struct ServingNode { impl ServingNode { fn debt_balance_and_timestamp(&self, now: SystemTime) -> (u128, SystemTime) { - let debt_specs = self.serving_node_profile.debt_specs(); + let debt_specs = self.node_profile.debt_specs(); (debt_specs.balance_minor, debt_specs.proper_timestamp(now)) } } @@ -1004,7 +1014,7 @@ impl ConsumingNode { impl ServingNode { fn new( - serving_node_profile: ServingNodeProfile, + node_profile: ServingNodeProfile, prepared_node: PreparedNodeInfo, config: NodeStartupConfig, earning_wallet: Wallet, @@ -1012,7 +1022,7 @@ impl ServingNode { ) -> Self { let common = NodeAttributesCommon::new(prepared_node, config); Self { - serving_node_profile, + node_profile, common, earning_wallet, receivable_dao, diff --git a/node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test b/node/src/test_utils/test_input_data/verify_bill_payments_smart_contract similarity index 100% rename from node/src/test_utils/test_input_data/smart_contract_for_on_blockchain_test rename to node/src/test_utils/test_input_data/verify_bill_payments_smart_contract From d3067181f1f223a3bf1d2f5243da6016641ef522 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 9 Apr 2025 00:14:52 +0200 Subject: [PATCH 245/250] GH-711: a few more shovels --- .../disqualification_arbiter.rs | 29 +++------- node/src/accountant/payment_adjuster/inner.rs | 6 +- .../logging_and_diagnostics/diagnostics.rs | 2 +- .../logging_and_diagnostics/log_functions.rs | 32 +++++------ .../miscellaneous/helper_functions.rs | 2 +- .../preparatory_analyser/mod.rs | 57 ++++++++++--------- .../payment_adjuster/service_fee_adjuster.rs | 52 +++++++++-------- 7 files changed, 87 insertions(+), 93 deletions(-) diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 119b9c0ba..62240472a 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -177,13 +177,13 @@ impl DisqualificationGaugeReal { let considered_forgiven = threshold_intercept_minor - permanent_debt_allowed_minor; let minimal_acceptable_payment = exceeding_threshold + permanent_debt_allowed_minor; - let condition_of_debt_fast_growth = minimal_acceptable_payment + let is_debt_growing_fast = minimal_acceptable_payment >= Self::FIRST_QUALIFICATION_CONDITION_COEFFICIENT * considered_forgiven; - let condition_of_position_on_rather_the_left_half_of_the_slope = considered_forgiven + let situated_on_the_left_half_of_the_slope = considered_forgiven >= Self::SECOND_QUALIFICATION_CONDITION_COEFFICIENT * permanent_debt_allowed_minor; - condition_of_debt_fast_growth && condition_of_position_on_rather_the_left_half_of_the_slope + is_debt_growing_fast && situated_on_the_left_half_of_the_slope } fn determine_adequate_minimal_payment( @@ -207,6 +207,9 @@ impl DisqualificationGaugeReal { // This schema shows the conditions used to determine the disqualification limit // (or minimal acceptable payment) // + // Y axis - debt size + // + // | // | A + // | | P -----------+ // | | P | @@ -237,7 +240,7 @@ impl DisqualificationGaugeReal { // | U U \ U \ | P // | U U \U \ | P // | U U U \|D' P E' - // +---------------------------+---+---------------------+ + // +---------------------------+---+---------------------+ X axis - time // 3 4 2 1 // // This diagram presents computation of the disqualification limit which differs by four cases. @@ -525,24 +528,6 @@ mod tests { ); assert_eq!(result, wallet_3); - // Hardening of the test with more formal checks - let all_wallets = unconfirmed_adjustments - .iter() - .map(|unconfirmed_adjustment| { - &unconfirmed_adjustment - .weighed_account - .analyzed_account - .qualified_as - .bare_account - .wallet - }) - .collect_vec(); - assert_eq!(all_wallets.len(), 4); - let wallets_same_as_wallet_3 = all_wallets - .iter() - .filter(|wallet| wallet.address() == wallet_3) - .collect_vec(); - assert_eq!(wallets_same_as_wallet_3.len(), 1); } fn make_unconfirmed_adjustments(weights: Vec) -> Vec { diff --git a/node/src/accountant/payment_adjuster/inner.rs b/node/src/accountant/payment_adjuster/inner.rs index 99bafd080..24daf4910 100644 --- a/node/src/accountant/payment_adjuster/inner.rs +++ b/node/src/accountant/payment_adjuster/inner.rs @@ -78,7 +78,7 @@ impl PaymentAdjusterInner { }) } pub fn subtract_from_remaining_cw_service_fee_balance_minor(&self, subtrahend: u128) { - let updated_thought_cw_balance = self.get_value( + let updated_cw_balance = self.get_value( "subtract_from_remaining_cw_service_fee_balance_minor", |guts_ref| { guts_ref @@ -89,7 +89,7 @@ impl PaymentAdjusterInner { ); self.set_value( "subtract_from_remaining_cw_service_fee_balance_minor", - |guts_mut| guts_mut.remaining_cw_service_fee_balance_minor = updated_thought_cw_balance, + |guts_mut| guts_mut.remaining_cw_service_fee_balance_minor = updated_cw_balance, ) } @@ -182,7 +182,7 @@ mod tests { } #[test] - fn reducing_remaining_cw_service_fee_balance_works() { + fn subtracting_remaining_cw_service_fee_balance_works() { let initial_cw_service_fee_balance_minor = 123_123_678_678; let subject = PaymentAdjusterInner::default(); subject.initialize_guts(None, initial_cw_service_fee_balance_minor, 12345); diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs index 7936e167e..f6dabec53 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs @@ -100,7 +100,7 @@ pub mod ordinary_diagnostic_functions { use thousands::Separable; use web3::types::Address; - pub fn thriving_competitor_found_diagnostics( + pub fn diagnostics_for_accounts_above_disqualification_limit( account_info: &UnconfirmedAdjustment, disqualification_limit: u128, ) { diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index d8dbb7740..eba7af30f 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -27,17 +27,17 @@ pub fn accounts_before_and_after_debug( ) -> String { let excluded_wallets_and_balances = preprocess_excluded_accounts(&original_account_balances_mapped, adjusted_accounts); - let excluded_accounts_summary = excluded_wallets_and_balances.is_empty().not().then(|| { + let excluded_accounts_summary_opt = excluded_wallets_and_balances.is_empty().not().then(|| { write_title_and_summary( &excluded_accounts_title(), &format_summary_for_excluded_accounts(&excluded_wallets_and_balances), ) }); - let included_accounts = write_title_and_summary( + let included_accounts_summary = write_title_and_summary( &included_accounts_title(), &format_summary_for_included_accounts(&original_account_balances_mapped, adjusted_accounts), ); - concatenate_summaries(included_accounts, excluded_accounts_summary) + concatenate_summaries(included_accounts_summary, excluded_accounts_summary_opt) } fn included_accounts_title() -> String { @@ -66,19 +66,18 @@ fn format_summary_for_included_accounts( // Sorting in descending order Ord::cmp(&account_b.balance_wei, &account_a.balance_wei) }) - .map(|account| { - let original_balance = original_account_balances_mapped - .get(&account.wallet.address()) - .expectv(""); - (account, *original_balance) - }) - .map(format_single_included_account) + .map(|account| format_single_included_account(account, original_account_balances_mapped)) .join("\n") } fn format_single_included_account( - (processed_account, original_balance): (&PayableAccount, u128), + processed_account: &PayableAccount, + original_account_balances_mapped: &HashMap, ) -> String { + let original_balance = original_account_balances_mapped + .get(&processed_account.wallet.address()) + .expect("The hashmap should contain every wallet"); + format!( "{} {}\n{:^length$} {}", processed_account.wallet, @@ -174,20 +173,19 @@ pub fn log_adjustment_by_service_fee_is_required( pub fn log_insufficient_transaction_fee_balance( logger: &Logger, - cw_required_transactions_count: u16, + requested_tx_count: u16, + feasible_tx_count: u16, txn_fee_required_per_txn_minor: u128, transaction_fee_minor: U256, - limiting_count: u16, ) { warning!( logger, "Transaction fee balance of {} wei cannot cover the anticipated {} wei for {} \ transactions. Maximal count is set to {}. Adjustment must be performed.", transaction_fee_minor.separate_with_commas(), - (cw_required_transactions_count as u128 * txn_fee_required_per_txn_minor) - .separate_with_commas(), - cw_required_transactions_count, - limiting_count + (requested_tx_count as u128 * txn_fee_required_per_txn_minor).separate_with_commas(), + requested_tx_count, + feasible_tx_count ); info!(logger, "{}", REFILL_RECOMMENDATION) } diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index c372d5108..cd7448fb2 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -199,7 +199,7 @@ mod tests { use std::time::SystemTime; #[test] - fn no_affordable_accounts_found_found_returns_true_for_non_finalized_accounts() { + fn no_affordable_accounts_found_returns_true_for_non_finalized_accounts() { let result = no_affordable_accounts_found(&Either::Left(vec![])); assert_eq!(result, true) diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index 6640d01ea..ced4dabc8 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -25,6 +25,8 @@ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::percentage::PurePercentage; +type TxCount = u16; + pub struct PreparatoryAnalyzer {} impl PreparatoryAnalyzer { @@ -104,9 +106,12 @@ impl PreparatoryAnalyzer { fn handle_errors_if_present( number_of_accounts: usize, - transaction_fee_check_result: Result, TransactionFeeImmoderateInsufficiency>, + transaction_fee_check_result: Result< + Option, + TransactionFeeImmoderateInsufficiency, + >, service_fee_check_result: Result<(), ServiceFeeImmoderateInsufficiency>, - ) -> Result, PaymentAdjusterError> { + ) -> Result, PaymentAdjusterError> { let construct_error = |tx_fee_check_err_opt: Option, service_fee_check_err_opt: Option| { @@ -168,7 +173,7 @@ impl PreparatoryAnalyzer { per_transaction_requirement_minor: u128, number_of_qualified_accounts: usize, logger: &Logger, - ) -> Result, TransactionFeeImmoderateInsufficiency> { + ) -> Result, TransactionFeeImmoderateInsufficiency> { let per_txn_requirement_minor_with_margin = gas_price_margin.increase_by_percent_for(per_transaction_requirement_minor); @@ -178,8 +183,8 @@ impl PreparatoryAnalyzer { number_of_qualified_accounts, ); - let max_tx_count_we_can_afford: u16 = verified_tx_counts.affordable; - let required_tx_count: u16 = verified_tx_counts.required; + let max_tx_count_we_can_afford = verified_tx_counts.affordable; + let required_tx_count = verified_tx_counts.required; if max_tx_count_we_can_afford == 0 { Err(TransactionFeeImmoderateInsufficiency { @@ -192,9 +197,9 @@ impl PreparatoryAnalyzer { log_insufficient_transaction_fee_balance( logger, required_tx_count, + max_tx_count_we_can_afford, per_txn_requirement_minor_with_margin, cw_transaction_fee_balance_minor, - max_tx_count_we_can_afford, ); Ok(Some(max_tx_count_we_can_afford)) @@ -610,10 +615,10 @@ mod tests { } #[test] - fn recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighted_payables() { + fn recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighed_payables() { init_test_logging(); let test_name = - "recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighted_payables"; + "recheck_if_service_fee_adjustment_is_needed_works_nicely_for_weighed_payables"; let balance_1 = multiply_by_billion(2_000_000); let mut weighed_account_1 = make_meaningless_weighed_account(123); weighed_account_1 @@ -630,33 +635,33 @@ mod tests { .balance_wei = balance_2; let accounts = vec![weighed_account_1, weighed_account_2]; let service_fee_totally_required_minor = balance_1 + balance_2; - // We start at a value being one bigger than required, and in the act, we subtract from it - // so that we also get the exact edge and finally also not enough by one. - let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1; let error_factory = LateServiceFeeSingleTxErrorFactory::new(&accounts); let logger = Logger::new(test_name); let subject = PreparatoryAnalyzer::new(); - [(0, false), (1, false), (2, true)].iter().for_each( - |(subtrahend_from_cw_balance, adjustment_is_needed_expected)| { - let service_fee_balance = cw_service_fee_balance_minor - subtrahend_from_cw_balance; - let adjustment_is_needed_actual = subject - .recheck_if_service_fee_adjustment_is_needed( - &accounts, - service_fee_balance, - error_factory.clone(), - &logger, - ) - .unwrap(); - assert_eq!(adjustment_is_needed_actual, *adjustment_is_needed_expected); - }, - ); + [ + (service_fee_totally_required_minor - 1, false), + (service_fee_totally_required_minor, false), + (service_fee_totally_required_minor + 1, true), + ] + .iter() + .for_each(|(service_fee_balance, adjustment_is_needed_expected)| { + let adjustment_is_needed_actual = subject + .recheck_if_service_fee_adjustment_is_needed( + &accounts, + *service_fee_balance, + error_factory.clone(), + &logger, + ) + .unwrap(); + assert_eq!(adjustment_is_needed_actual, *adjustment_is_needed_expected); + }); TestLogHandler::new().exists_log_containing(&format!( "WARN: {test_name}: Mature payables amount to {} MASQ wei while the consuming wallet \ holds only {}", service_fee_totally_required_minor.separate_with_commas(), - (cw_service_fee_balance_minor - 2).separate_with_commas() + (service_fee_totally_required_minor - 1).separate_with_commas() )); } diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index f9e86a7b5..dd513423d 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -12,7 +12,7 @@ use itertools::Either; use masq_lib::logger::Logger; use masq_lib::utils::convert_collection; use std::vec; -use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::thriving_competitor_found_diagnostics; +use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::diagnostics_for_accounts_above_disqualification_limit; pub trait ServiceFeeAdjuster { fn perform_adjustment_by_service_fee( @@ -41,12 +41,14 @@ impl ServiceFeeAdjuster for ServiceFeeAdjusterReal { let checked_accounts = Self::try_confirm_some_accounts(unconfirmed_adjustments); match checked_accounts { - Either::Left(no_accounts_above_disq_limit) => Self::disqualify_single_account( + Either::Left(only_accounts_below_disq_limit) => Self::disqualify_single_account( disqualification_arbiter, - no_accounts_above_disq_limit, + only_accounts_below_disq_limit, logger, ), - Either::Right(some_accounts_above_disq_limit) => some_accounts_above_disq_limit, + Either::Right(some_accounts_above_or_even_to_disq_limit) => { + some_accounts_above_or_even_to_disq_limit + } } } } @@ -74,16 +76,16 @@ impl ServiceFeeAdjusterReal { fn try_confirm_some_accounts( unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { - let (accounts_above_disq_limit, accounts_below_disq_limit) = + let (accounts_above_or_even_to_disq_limit, accounts_below_disq_limit) = Self::filter_and_process_confirmable_accounts(unconfirmed_adjustments); - if accounts_above_disq_limit.is_empty() { + if accounts_above_or_even_to_disq_limit.is_empty() { Either::Left(accounts_below_disq_limit) } else { let remaining_undecided_accounts: Vec = convert_collection(accounts_below_disq_limit); let pre_processed_decided_accounts: Vec = - convert_collection(accounts_above_disq_limit); + convert_collection(accounts_above_or_even_to_disq_limit); Either::Right(AdjustmentIterationResult { decided_accounts: pre_processed_decided_accounts, remaining_undecided_accounts, @@ -119,27 +121,31 @@ impl ServiceFeeAdjusterReal { Vec, ) { let init: (Vec, Vec) = (vec![], vec![]); - let fold_guts = |(mut above_disq_limit, mut below_disq_limit): (Vec<_>, Vec<_>), - current: UnconfirmedAdjustment| { - let disqualification_limit = current.disqualification_limit_minor(); - if current.proposed_adjusted_balance_minor >= disqualification_limit { - thriving_competitor_found_diagnostics(¤t, disqualification_limit); - let mut adjusted = current; - adjusted.proposed_adjusted_balance_minor = disqualification_limit; - above_disq_limit.push(adjusted) - } else { - below_disq_limit.push(current) - } - (above_disq_limit, below_disq_limit) - }; + let fold_guts = + |(mut above_or_even_to_disq_limit, mut below_disq_limit): (Vec<_>, Vec<_>), + current: UnconfirmedAdjustment| { + let disqualification_limit = current.disqualification_limit_minor(); + if current.proposed_adjusted_balance_minor >= disqualification_limit { + diagnostics_for_accounts_above_disqualification_limit( + ¤t, + disqualification_limit, + ); + let mut adjusted = current; + adjusted.proposed_adjusted_balance_minor = disqualification_limit; + above_or_even_to_disq_limit.push(adjusted) + } else { + below_disq_limit.push(current) + } + (above_or_even_to_disq_limit, below_disq_limit) + }; - let (accounts_above_disq_limit, accounts_below_disq_limit) = + let (accounts_above_or_even_to_disq_limit, accounts_below_disq_limit) = unconfirmed_adjustments.into_iter().fold(init, fold_guts); - let decided_accounts = if accounts_above_disq_limit.is_empty() { + let decided_accounts = if accounts_above_or_even_to_disq_limit.is_empty() { vec![] } else { - convert_collection(accounts_above_disq_limit) + convert_collection(accounts_above_or_even_to_disq_limit) }; (decided_accounts, accounts_below_disq_limit) From e43f6555457e194a52a16db8b2041d47ccac7b24 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 24 Sep 2025 18:08:49 +0200 Subject: [PATCH 246/250] GH-711: interim commit --- node/src/accountant/mod.rs | 20 +++--- .../disqualification_arbiter.rs | 6 +- node/src/accountant/payment_adjuster/mod.rs | 42 ++++++++++-- .../payment_adjuster/non_unit_tests/mod.rs | 65 ++++++++++--------- .../payment_adjuster/service_fee_adjuster.rs | 36 +++++----- .../accountant/payment_adjuster/test_utils.rs | 6 +- node/src/accountant/scanners/mod.rs | 57 ++++++++-------- .../src/accountant/scanners/scanners_utils.rs | 3 + node/src/accountant/test_utils.rs | 8 +-- 9 files changed, 139 insertions(+), 104 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 7a15b4807..2a0fd71a7 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1753,11 +1753,11 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( - "WARN: {test_name}: Add more funds into your consuming wallet in order to become able \ - to repay already expired liabilities as the creditors would respond by delinquency bans \ - otherwise. Details: Current transaction fee balance is not enough to pay a single payment. \ - Number of canceled payments: 1. Transaction fee per payment: 3,300,000,000,000,000 wei, \ - while the wallet contains: 123,000,000,000 wei." + "WARN: {test_name}: Add more funds into your consuming wallet to become able to repay \ + already matured debts as the creditors would respond by a delinquency ban otherwise. \ + Details: Current transaction fee balance is not enough to pay a single payment. Number \ + of canceled payments: 1. Transaction fee per payment: 3,300,000,000,000,000 wei, while \ + the wallet contains: 123,000,000,000 wei." )); log_handler .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); @@ -1832,8 +1832,8 @@ mod tests { // No NodeUiMessage was sent because there is no `response_skeleton`. It is evident by // the fact that the test didn't blow up even though UIGateway is unbound TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Payable scanner is unable to generate payment \ - instructions. Resolution of the issue appears to be the user's responsibility." + "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ + It seems only the user can solve this problem." )); } @@ -3626,9 +3626,9 @@ mod tests { let payable_scanner = PayableScannerBuilder::new() .payable_dao(payable_dao_for_payable_scanner) .pending_payable_dao(pending_payable_dao_for_payable_scanner) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .payment_adjuster(payment_adjuster) .build(); subject.scanners.payable = Box::new(payable_scanner); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index 62240472a..c33d17dc6 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -283,7 +283,7 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::payment_adjuster::test_utils::local_utils::{ - make_meaningless_weighed_account, make_non_guaranteed_unconfirmed_adjustment, + make_meaningless_weighed_account, make_meaningless_unconfirmed_adjustment, }; use itertools::Itertools; use masq_lib::logger::Logger; @@ -445,7 +445,7 @@ mod tests { #[test] fn list_accounts_nominated_for_disqualification_ignores_adjustment_even_to_the_dsq_limit() { - let mut account = make_non_guaranteed_unconfirmed_adjustment(444); + let mut account = make_meaningless_unconfirmed_adjustment(444); account.proposed_adjusted_balance_minor = 1_000_000_000; account .weighed_account @@ -535,7 +535,7 @@ mod tests { .into_iter() .enumerate() .map(|(idx, weight)| { - let mut account = make_non_guaranteed_unconfirmed_adjustment(idx as u64); + let mut account = make_meaningless_unconfirmed_adjustment(idx as u64); account.weighed_account.weight = weight; account }) diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 8bcbdbfa3..690807adc 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -54,15 +54,43 @@ use masq_lib::utils::convert_collection; use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstraction::DisqualificationLimitProvidingAccount; // PaymentAdjuster is a recursive and scalable algorithm that inspects payments under conditions -// of an acute insolvency. You can easily expand the range of evaluated parameters to determine -// an optimized allocation of scarce assets by writing your own CriterionCalculator. The calculator -// is supposed to be dedicated to a single parameter that can be tracked for each payable account. +// of acute insolvency. Each parameter that participates in the determination of the optimized +// asset allocation should have its own calculator. You can easily maintain the range of evaluated +// parameters by removing or adding your own calculator. These calculators, placed in a vector, +// make the heart of the algorithm. // -// For parameters that can't be derived from each account, or even one at all, there is a way to -// provide such data up into the calculator. This can be achieved via the PaymentAdjusterInner. +// For parameters that can't be derived from an account, there is still a way to provide such values +// up into the calculator. This can be achieved via the PaymentAdjusterInner. + +// Algorithm description: +// +// It begins with accounts getting weights from the criteria calculators. The weighting is inverse +// to the debt size, accounts with smaller debts are prioritized so that we can satisfy as many +// accounts as possible and avoid the same number of bans. // -// Once the new calculator exists, its place belongs in the vector of calculators which is the heart -// of this module. +// If it is necessary to adjust the set by the transaction fee, the accounts are sorted +// by the weights and only accounts that fit together under the limit are kept in. The adjustment +// by service fee follows up (or it may take the first place if the need for the previous step was +// missing). + +// Here comes the recursive part. The algorithm iterates through the weighted accounts and assigns +// a proportional portion of the available means to them. Since the initial stage of the Payment- +// Adjuster, where accounts were tested on causing insolvency, they've remained equipped with +// a computed parameter of the so-called disqualification limit. This limit determines +// if the weight-derived assignment of the money is too low or enough. If it is below the limit, +// the account is removed from consideration. However, only a single account with the smallest +// weight is eliminated per each recursion. + +// The pool of money remains the same, but the set of accounts is reduced. The recursion repeats. +// If none of the accounts disqualifies, it means all accounts were proposed with enough money. +// Although the proposals may exceed the disqualification limits, only the value of the limit is +// dedicated to the selected accounts. + +// The remaining assets are later allocated to the accounts based on the order by their weights, +// exhausting them fully one account after another up their 100% allocation until there is any +// money that can be distributed. + +// In the end, the accounts are shaped back as a PayableAccount and returned. pub type AdjustmentAnalysisResult = Result, PaymentAdjusterError>; diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 4f8cbd20a..a5fd79c82 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -18,7 +18,7 @@ use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ PayableInspector, PayableThresholdsGaugeReal, }; use crate::accountant::test_utils::{ - make_single_qualified_payable_opt, try_to_make_guaranteed_qualified_payables, + make_single_qualified_payable_opt, try_to_make_qualified_payables, }; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; use crate::blockchain::blockchain_interface::blockchain_interface_web3::TX_FEE_MARGIN_IN_PERCENT; @@ -42,10 +42,10 @@ use web3::types::{Address, U256}; #[test] // TODO If an option for "occasional tests" is added, this is a good adept -#[ignore] +//#[ignore] fn loading_test_with_randomized_params() { - // This is a fuzz test. It generates possibly an overwhelming amount of scenarios that - // the PaymentAdjuster could be given sort them out, as realistic as it can get, while its + // This is a fuzz test. It generates possibly an overwhelming number of scenarios that + // the PaymentAdjuster could be given to sort them out, as realistic as it can get, while its // nature of randomness offers chances to have a dense range of combinations that a human fails // to even try imagining. The hypothesis is that some of those might be corner cases whose // trickiness wasn't recognized when the functionality was still at design. This test is to @@ -64,8 +64,8 @@ fn loading_test_with_randomized_params() { // we allow this always implied waste than trying to invent an algorithm whose randomness would // be exercised within strictly controlled boundaries. - // Some other are lost quite early as legitimate errors that the PaymentAdjuster can detect, - // which would prevent finishing the search for given scenario. + // Some others are lost quite early as legitimate errors that the PaymentAdjuster can detect, + // which would prevent finishing the search for a given scenario. // When the test reaches its end, it produces important output in a text file, located: // node/generated/test/payment_adjuster/tests/home/loading_test_output.txt @@ -81,15 +81,15 @@ fn loading_test_with_randomized_params() { // payables. See those percentages. They may not excel at explaining themselves when it comes to // their inconsistent proportionality towards the balances. These percents represent a payment // coverage of the initial debts. But why don't they correspond with ascending balances? There's - // a principle to equip accounts low balances with the biggest weights. True. However, it doesn't + // a principle to equip account low balances with the biggest weights. True. However, it doesn't // need to be reflected so clearly, though. The adjustment depends heavily on a so-called // "disqualification limit". Besides other purposes, this value affects that the payment won't // require the entire amount but only its portion. That inherently will do for the payer to stay // unbanned. In bulky accounts, this until-some-time forgiven portion stands only as a fraction // of a whole. Small accounts, however, if it can be applied (as opposed to the account having // to be excluded) might get shrunk a lot, and therefore many percents are to be reported as - // missing. This is what the numbers like 99% and 90% illustrates. That said, the letter account - // comes across as it should take precedence for its expectedly larger weight, and gain at the + // missing. This is what the numbers like 99% and 90% illustrate. That said, the letter account + // comes across as it should take precedence for its expectedly larger weight and gain at the // expanse of the other, but the percents speak otherwise. Yet, it's correct. The interpretation // is the key. (Caution: this test displays its output with those accounts sorted). @@ -100,9 +100,9 @@ fn loading_test_with_randomized_params() { // 2000000|1000|1000|1000000|500000|1000000 // _____________________________________________________________________________________________ // 1,988,742,049,305,843 wei | 236,766 s | 100 % - // 21,971,010,542,100,729 wei | 472,884 s | 99 % # # # # # # # # - // 4,726,030,753,976,563 wei | 395,377 s | 95 % # # # # # # # # - // 3,995,577,830,314,875 wei | 313,396 s | 90 % # # # # # # # # + // 21,971,010,542,100,729 wei | 472,884 s | 99 % << << << << + // 4,726,030,753,976,563 wei | 395,377 s | 95 % << << << << + // 3,995,577,830,314,875 wei | 313,396 s | 90 % << << << << // 129,594,971,536,673,815 wei | 343,511 s | X // In the code, we select and pale up accounts so that the picked balance isn't the full range, @@ -256,20 +256,21 @@ fn generate_debt_age(gn: &mut ThreadRng, thresholds: &PaymentThresholds) -> u64 gn, thresholds.maturity_threshold_sec, thresholds.maturity_threshold_sec + thresholds.threshold_interval_sec, - ) / 2 + ) } fn generate_highly_randomized_payable_account_balance( gn: &mut ThreadRng, thresholds: &PaymentThresholds, ) -> u128 { - // This seems overcomplicated, damn. As a result of simple intentions though. I wanted to ensure - // occurrence of accounts with balances having different magnitudes in the frame of a single - // scenario. This was crucial to me so much that I was ready to write even this piece of code - // a bit crazy by look. - // This setup worked well to stress the randomness I needed, a lot more significant compared to - // what the naked number generator can put for you. Using some nesting, it broke the rigid - // pattern and gave an existence to accounts with diverse balances. + // This seems overcomplicated, damn. Yet it's a result of good simple intentions. I wanted + // to ensure the occurrence of accounts with balances of different magnitudes to be generated + // for a single scenario. This was crucial to me so much that I didn't stop myself from writing + // this fishy-looking piece of code. + // This setup worked well for the randomness I needed, a lot significantly more compared to + // what the default number generator from this library seemed to be able to provide only. + // Using some nesting, it scattered the distribution better and allowed me to have accounts + // with diverse balances. let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; let parameter_a = generate_u128(); @@ -311,14 +312,14 @@ fn try_make_qualified_payables_by_applied_thresholds( ) -> Vec { let payment_inspector = PayableInspector::new(Box::new(PayableThresholdsGaugeReal::default())); match applied_thresholds { - AppliedThresholds::Defaulted => try_to_make_guaranteed_qualified_payables( + AppliedThresholds::Defaulted => try_to_make_qualified_payables( payable_accounts, &PRESERVED_TEST_PAYMENT_THRESHOLDS, now, false, ), AppliedThresholds::CommonButRandomized { common_thresholds } => { - try_to_make_guaranteed_qualified_payables( + try_to_make_qualified_payables( payable_accounts, common_thresholds, now, @@ -378,14 +379,15 @@ fn pick_appropriate_cw_service_fee_balance( qualified_payables: &[QualifiedPayableAccount], accounts_count: usize, ) -> u128 { - // Value picked empirically - const COEFFICIENT: usize = 1000; + // Values picked empirically + const COEFFICIENT_A: usize = 1000; + const COEFFICIENT_B: usize = 2; let balance_average = sum_as(qualified_payables, |account| { account.initial_balance_minor() }) / accounts_count as u128; - let max_pieces = accounts_count * COEFFICIENT; - let number_of_pieces = generate_usize(gn, max_pieces - 2) as u128 + 2; - balance_average / COEFFICIENT as u128 * number_of_pieces + let max_pieces = accounts_count * COEFFICIENT_A; + let number_of_pieces = generate_usize(gn, max_pieces - COEFFICIENT_B) as u128 + COEFFICIENT_B as u128; + balance_average / COEFFICIENT_A as u128 * number_of_pieces } fn make_payables_according_to_thresholds_setup( @@ -715,10 +717,9 @@ fn introduction(file: &mut File) { let page_width = PAGE_WIDTH; file.write_fmt(format_args!( "{:^page_width$}\n", - "A short summary can be found at the tail" + "There is a short summary at the tail" )) .unwrap(); - write_thick_dividing_line(file); write_thick_dividing_line(file) } @@ -747,8 +748,8 @@ fn write_brief_test_summary_at_file_s_tail( With 'RecursionDrainedAllAccounts':.... {}\n\ With late insufficient balance errors:. {}\n\n\ Legend\n\ - Adjusted balances are highlighted by \ - these marks by the side:............. . {}", + Adjusted balances are highlighted\n\ + by these marks by the side:............ {}", scenarios_requested, scenarios_evaluated, output_collector.oks, @@ -976,7 +977,7 @@ fn single_account_output( .unwrap(); } -const NON_EXHAUSTED_ACCOUNT_MARKER: &str = "# # # # # # # #"; +const NON_EXHAUSTED_ACCOUNT_MARKER: &str = "<< << << <<"; fn resolve_account_fulfilment_status_graphically( bill_coverage_in_percentage_opt: Option, diff --git a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs index dd513423d..ab1c51d3d 100644 --- a/node/src/accountant/payment_adjuster/service_fee_adjuster.rs +++ b/node/src/accountant/payment_adjuster/service_fee_adjuster.rs @@ -68,11 +68,12 @@ impl ServiceFeeAdjusterReal { // wisely, better redistributed among the rest of accounts, as much as the wider group of them // can be satisfied, even though just partially. // - // However, if it begins to be clear that the remaining money doesn't allow to keep any + // However, if it begins to be clear that the remaining money doesn't allow keeping any // additional account in the selection, there is the next step to come, where the already // selected accounts are reviewed again in the order of their significance resolved from // remembering their weights from the earlier processing, and the unused money is poured into, // until all resources are used. + fn try_confirm_some_accounts( unconfirmed_adjustments: Vec, ) -> Either, AdjustmentIterationResult> { @@ -190,7 +191,7 @@ fn compute_proportional_cw_fragment( multiplication_coefficient: u128, ) -> u128 { cw_service_fee_balance_minor - // Considered safe for the nature of the calculus producing this coefficient + // Considered safe as to the nature of the calculus producing this coefficient .checked_mul(multiplication_coefficient) .unwrap_or_else(|| { panic!( @@ -207,13 +208,13 @@ mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; use crate::accountant::payment_adjuster::service_fee_adjuster::ServiceFeeAdjusterReal; use crate::accountant::payment_adjuster::test_utils::local_utils::{ - make_non_guaranteed_unconfirmed_adjustment, multiply_by_quintillion, + make_meaningless_unconfirmed_adjustment, multiply_by_quintillion, multiply_by_quintillion_concise, }; #[test] fn filter_and_process_confirmable_accounts_limits_them_by_their_disqualification_edges() { - let mut account_1 = make_non_guaranteed_unconfirmed_adjustment(111); + let mut account_1 = make_meaningless_unconfirmed_adjustment(111); let weight_1 = account_1.weighed_account.weight; account_1 .weighed_account @@ -226,7 +227,7 @@ mod tests { .analyzed_account .disqualification_limit_minor = multiply_by_quintillion_concise(1.8); account_1.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(3.0); - let mut account_2 = make_non_guaranteed_unconfirmed_adjustment(222); + let mut account_2 = make_meaningless_unconfirmed_adjustment(222); let weight_2 = account_2.weighed_account.weight; account_2 .weighed_account @@ -239,7 +240,7 @@ mod tests { .analyzed_account .disqualification_limit_minor = multiply_by_quintillion_concise(4.2) - 1; account_2.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(4.2); - let mut account_3 = make_non_guaranteed_unconfirmed_adjustment(333); + let mut account_3 = make_meaningless_unconfirmed_adjustment(333); account_3 .weighed_account .analyzed_account @@ -251,7 +252,7 @@ mod tests { .analyzed_account .disqualification_limit_minor = multiply_by_quintillion(2) + 1; account_3.proposed_adjusted_balance_minor = multiply_by_quintillion(2); - let mut account_4 = make_non_guaranteed_unconfirmed_adjustment(444); + let mut account_4 = make_meaningless_unconfirmed_adjustment(444); let weight_4 = account_4.weighed_account.weight; account_4 .weighed_account @@ -264,7 +265,7 @@ mod tests { .analyzed_account .disqualification_limit_minor = multiply_by_quintillion_concise(0.5); account_4.proposed_adjusted_balance_minor = multiply_by_quintillion_concise(0.5); - let mut account_5 = make_non_guaranteed_unconfirmed_adjustment(555); + let mut account_5 = make_meaningless_unconfirmed_adjustment(555); account_5 .weighed_account .analyzed_account @@ -341,18 +342,21 @@ pub mod illustrative_util { unconfirmed_adjustments[1].wallet(), wallet_of_expected_outweighed ); - // To prevent unjust reallocation we used to secure a rule an account could never demand - // more than 100% of its size. + // To prevent unjust reallocation, we secured a rule an account could never demand more + // than 100% of its size. + + // Later it was changed to a different policy, the so-called "outweighed" account is given + // automatically a balance equal to its disqualification limit. Still, it's quite likely + // some accounts will acquire slightly more by a distribution of the last bits of funds + // away out of the consuming wallet. - // Later it was changed to a different policy, the so called "outweighed" account is given - // automatically a balance equal to its disqualification limit. Still, later on, it's quite - // likely to acquire slightly more by a distribution of the last bits of funds away from - // within the consuming wallet. + // Here, though, the assertion illustrates what the latest policy intends to fight off, + // as the unprotected proposed adjusted balance rises over the original balance. let proposed_adjusted_balance = unconfirmed_adjustments[1].proposed_adjusted_balance_minor; assert!( proposed_adjusted_balance > (original_balance_of_outweighed_account * 11 / 10), - "we expected the proposed balance at least 1.1 times bigger than the original balance \ - which is {} but it was {}", + "we expected the proposed balance to be unsound, bigger than the original balance \ + (at least 1.1 times more) which would be {} but it was {}", original_balance_of_outweighed_account.separate_with_commas(), proposed_adjusted_balance.separate_with_commas() ); diff --git a/node/src/accountant/payment_adjuster/test_utils.rs b/node/src/accountant/payment_adjuster/test_utils.rs index 435cb8cd3..f85408b5c 100644 --- a/node/src/accountant/payment_adjuster/test_utils.rs +++ b/node/src/accountant/payment_adjuster/test_utils.rs @@ -113,8 +113,8 @@ pub(super) mod local_utils { .into_iter() .map(|months| (months, constant_balance)) .collect(), - Either::Right(specific_months_and_specific_balance) => { - specific_months_and_specific_balance + Either::Right(specific_months_and_specific_balances) => { + specific_months_and_specific_balances } }; accounts_seeds @@ -143,7 +143,7 @@ pub(super) mod local_utils { unban_below_gwei: 1_000_000, }; - pub fn make_non_guaranteed_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { + pub fn make_meaningless_unconfirmed_adjustment(n: u64) -> UnconfirmedAdjustment { let qualified_account = make_meaningless_qualified_payable(n); let account_balance = qualified_account.bare_account.balance_wei; let proposed_adjusted_balance_minor = (2 * account_balance) / 3; diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index e5f6cd15c..a0eb39c34 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -330,8 +330,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { fn cancel_scan(&mut self, logger: &Logger) { error!( logger, - "Payable scanner is unable to generate payment instructions. Resolution of the issue \ - appears to be the user's responsibility." + "Payable scanner is unable to generate payment instructions. It seems only the user can \ + solve this problem." ); self.mark_as_ended(logger) } @@ -582,9 +582,8 @@ impl PayableScanner { } const ADD_MORE_FUNDS_URGE: &'static str = - "Add more funds into your consuming wallet in order to \ - become able to repay already expired liabilities as the creditors would respond by delinquency \ - bans otherwise"; + "Add more funds into your consuming wallet to become able to repay already matured debts \ + as the creditors would respond by a delinquency ban otherwise"; } pub struct PendingPayableScanner { @@ -1328,9 +1327,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default()) + ) .build(); let result = subject.begin_scan(now, None, &Logger::new(test_name)); @@ -1364,9 +1363,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let _result = subject.begin_scan(now, None, &Logger::new("test")); @@ -1389,9 +1388,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let result = subject.begin_scan(now, None, &Logger::new("test")); @@ -2187,9 +2186,9 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified_and_the_threshold_value_is_returned"; @@ -2220,9 +2219,9 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified"; let logger = Logger::new(test_name); @@ -2263,9 +2262,9 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let logger = Logger::new(test_name); @@ -2288,9 +2287,9 @@ mod tests { unban_below_gwei: 0, }; let wallet = make_wallet("abc"); - // It is important to have a payable laying in the declining part of the thresholds, also - // it will be the more believable the steeper we have the slope because then a single second - // can make a certain difference for the intercept value which is the value this test + // It is important to have a payable lying in the declining part of the thresholds, also + // it will be more believable the steeper we have the slope because then a single second + // can make a certain difference for the intercept value, which is the value this test // compares for carrying out the conclusion let debt_age = payment_thresholds.maturity_threshold_sec + (payment_thresholds.threshold_interval_sec / 2); @@ -2302,9 +2301,9 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_inspector(PayableInspector::new(Box::new( - PayableThresholdsGaugeReal::default(), - ))) + .payable_threshold_gauge(Box::new( + PayableThresholdsGaugeReal::default() + )) .build(); let intercept_before = subject .payable_exceeded_threshold(&payable, SystemTime::now()) diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 16c445362..9313d9bc6 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -314,6 +314,9 @@ pub mod payable_scanner_utils { .payable_threshold_gauge .calculate_payout_threshold_in_gwei(payment_thresholds, debt_age); + eprintln!( + "Balance wei: {}\n\ + Threshold: {}", payable.balance_wei, threshold); if payable.balance_wei > threshold { Some(threshold) } else { diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 85f679c13..bb8b0b8a7 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1133,8 +1133,8 @@ impl PayableScannerBuilder { self } - pub fn payable_inspector(mut self, payable_inspector: PayableInspector) -> Self { - self.payable_inspector = payable_inspector; + pub fn payable_threshold_gauge(mut self, payable_threshold_gauge: Box) -> Self { + self.payable_inspector = PayableInspector::new(payable_threshold_gauge); self } @@ -1747,7 +1747,7 @@ pub fn make_qualified_payables( payment_thresholds: &PaymentThresholds, now: SystemTime, ) -> Vec { - try_to_make_guaranteed_qualified_payables(payables, payment_thresholds, now, true) + try_to_make_qualified_payables(payables, payment_thresholds, now, true) } pub fn make_analyzed_payables( @@ -1758,7 +1758,7 @@ pub fn make_analyzed_payables( convert_qualified_p_into_analyzed_p(make_qualified_payables(payables, payment_thresholds, now)) } -pub fn try_to_make_guaranteed_qualified_payables( +pub fn try_to_make_qualified_payables( payables: Vec, payment_thresholds: &PaymentThresholds, now: SystemTime, From 86b5a9a054582118f21464a33675371007fc56cd Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 24 Sep 2025 21:08:21 +0200 Subject: [PATCH 247/250] GH-711: fixes in the non-unit test --- node/src/accountant/mod.rs | 8 +- .../disqualification_arbiter.rs | 2 +- .../logging_and_diagnostics/log_functions.rs | 1 - .../payment_adjuster/non_unit_tests/mod.rs | 259 +++++++++++------- node/src/accountant/scanners/mod.rs | 28 +- .../src/accountant/scanners/scanners_utils.rs | 4 +- node/src/accountant/test_utils.rs | 5 +- 7 files changed, 177 insertions(+), 130 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 2a0fd71a7..292fda1c8 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1084,9 +1084,7 @@ mod tests { TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; - use crate::accountant::scanners::scanners_utils::payable_scanner_utils::{ - PayableInspector, PayableThresholdsGaugeReal, - }; + use crate::accountant::scanners::scanners_utils::payable_scanner_utils::PayableThresholdsGaugeReal; use crate::accountant::scanners::test_utils::protect_qualified_payables_in_test; use crate::accountant::scanners::BeginScanError; use crate::accountant::test_utils::DaoWithDestination::{ @@ -3626,9 +3624,7 @@ mod tests { let payable_scanner = PayableScannerBuilder::new() .payable_dao(payable_dao_for_payable_scanner) .pending_payable_dao(pending_payable_dao_for_payable_scanner) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .payment_adjuster(payment_adjuster) .build(); subject.scanners.payable = Box::new(payable_scanner); diff --git a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs index c33d17dc6..1992b9667 100644 --- a/node/src/accountant/payment_adjuster/disqualification_arbiter.rs +++ b/node/src/accountant/payment_adjuster/disqualification_arbiter.rs @@ -283,7 +283,7 @@ mod tests { }; use crate::accountant::payment_adjuster::miscellaneous::data_structures::UnconfirmedAdjustment; use crate::accountant::payment_adjuster::test_utils::local_utils::{ - make_meaningless_weighed_account, make_meaningless_unconfirmed_adjustment, + make_meaningless_unconfirmed_adjustment, make_meaningless_weighed_account, }; use itertools::Itertools; use masq_lib::logger::Logger; diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index eba7af30f..6f3cf030b 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -2,7 +2,6 @@ use crate::accountant::db_access_objects::payable_dao::PayableAccount; use crate::accountant::payment_adjuster::disqualification_arbiter::DisqualificationSuspectedAccount; -use crate::masq_lib::utils::ExpectValue; use itertools::Itertools; use masq_lib::constants::WALLET_ADDRESS_LENGTH; use masq_lib::logger::Logger; diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index a5fd79c82..4fcd199db 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -171,27 +171,35 @@ fn loading_test_with_randomized_params() { let second_stage_scenarios = first_stage_output.allowed_scenarios; let test_overall_output_collector = first_stage_output.output_collector; - let scenario_adjustment_results = second_stage_scenarios - .into_iter() - .map(|scenario| { - let prepared_adjustment = scenario.prepared_adjustment; - let account_infos = - preserve_account_infos(&prepared_adjustment.adjustment_analysis.accounts, now); - let required_adjustment = prepared_adjustment.adjustment_analysis.adjustment.clone(); - let cw_service_fee_balance_minor = - prepared_adjustment.agent.service_fee_balance_minor(); - - let payment_adjuster_result = subject.adjust_payments(prepared_adjustment); - - administrate_single_scenario_result( - payment_adjuster_result, - account_infos, - scenario.applied_thresholds, - required_adjustment, - cw_service_fee_balance_minor, - ) - }) - .collect(); + let (test_overall_output_collector, scenario_adjustment_results) = + second_stage_scenarios.into_iter().fold( + (test_overall_output_collector, vec![]), + |(test_overall_output_collector_in_fold, mut scenario_results), scenario| { + let prepared_adjustment = scenario.prepared_adjustment; + let account_infos = + preserve_account_infos(&prepared_adjustment.adjustment_analysis.accounts, now); + + let required_adjustment = + prepared_adjustment.adjustment_analysis.adjustment.clone(); + let cw_service_fee_balance_minor = + prepared_adjustment.agent.service_fee_balance_minor(); + + let payment_adjuster_result = subject.adjust_payments(prepared_adjustment); + + let (t_o_c_i_f, scenario_result) = administrate_single_scenario_result( + test_overall_output_collector_in_fold, + payment_adjuster_result, + account_infos, + scenario.applied_thresholds, + required_adjustment, + cw_service_fee_balance_minor, + ); + + scenario_results.push(scenario_result); + + (t_o_c_i_f, scenario_results) + }, + ); render_results_to_file_and_attempt_basic_assertions( scenario_adjustment_results, @@ -271,7 +279,13 @@ fn generate_highly_randomized_payable_account_balance( // what the default number generator from this library seemed to be able to provide only. // Using some nesting, it scattered the distribution better and allowed me to have accounts // with diverse balances. - let mut generate_u128 = || generate_non_zero_usize(gn, 100) as u128; + const COEFFICIENT_A: usize = 100; + const COEFFICIENT_B: usize = 2; + const COEFFICIENT_C: usize = 3; + const COEFFICIENT_D: usize = 4; + const COEFFICIENT_E: usize = 5; + + let mut generate_u128 = || generate_non_zero_usize(gn, COEFFICIENT_A) as u128; let parameter_a = generate_u128(); let parameter_b = generate_u128(); @@ -280,29 +294,17 @@ fn generate_highly_randomized_payable_account_balance( let parameter_e = generate_u128(); let parameter_f = generate_u128(); - let mut use_variable_exponent = + let mut apply_arbitrary_variable_exponent = |parameter: u128, up_to: usize| parameter.pow(generate_non_zero_usize(gn, up_to) as u32); - let a_b_c_d_e = parameter_a - * use_variable_exponent(parameter_b, 2) - * use_variable_exponent(parameter_c, 3) - * use_variable_exponent(parameter_d, 4) - * use_variable_exponent(parameter_e, 5); - let addition = (0..6).fold(a_b_c_d_e, |so_far, subtrahend| { - if so_far != a_b_c_d_e { - so_far - } else { - if let Some(num) = - a_b_c_d_e.checked_sub(use_variable_exponent(parameter_f, 6 - subtrahend)) - { - num - } else { - so_far - } - } - }); + let a_b_c_d_e_f = parameter_a + * apply_arbitrary_variable_exponent(parameter_b, COEFFICIENT_B) + * apply_arbitrary_variable_exponent(parameter_c, COEFFICIENT_C) + * apply_arbitrary_variable_exponent(parameter_d, COEFFICIENT_D) + * apply_arbitrary_variable_exponent(parameter_e, COEFFICIENT_E) + * parameter_f; - thresholds.permanent_debt_allowed_gwei as u128 + addition + thresholds.permanent_debt_allowed_gwei as u128 + a_b_c_d_e_f } fn try_make_qualified_payables_by_applied_thresholds( @@ -314,38 +316,45 @@ fn try_make_qualified_payables_by_applied_thresholds( match applied_thresholds { AppliedThresholds::Defaulted => try_to_make_qualified_payables( payable_accounts, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, + &PaymentThresholds::default(), now, false, ), AppliedThresholds::CommonButRandomized { common_thresholds } => { - try_to_make_qualified_payables( - payable_accounts, - common_thresholds, - now, - false, - ) + try_to_make_qualified_payables(payable_accounts, common_thresholds, now, false) } AppliedThresholds::RandomizedForEachAccount { individual_thresholds, - } => { - let vec_of_thresholds = individual_thresholds.values().collect_vec(); - let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); - zipped - .flat_map(|(qualified_payable, thresholds)| { - make_single_qualified_payable_opt( - qualified_payable, - &payment_inspector, - &thresholds, - false, - now, - ) - }) - .collect() - } + } => make_qualified_payables_for_individualized_thresholds( + payable_accounts, + now, + &payment_inspector, + individual_thresholds, + ), } } +fn make_qualified_payables_for_individualized_thresholds( + payable_accounts: Vec, + now: SystemTime, + payment_inspector: &PayableInspector, + individual_thresholds: &HashMap, +) -> Vec { + let vec_of_thresholds = individual_thresholds.values().collect_vec(); + let zipped = payable_accounts.into_iter().zip(vec_of_thresholds.iter()); + zipped + .flat_map(|(qualified_payable, thresholds)| { + make_single_qualified_payable_opt( + qualified_payable, + &payment_inspector, + &thresholds, + false, + now, + ) + }) + .collect() +} + fn try_generating_qualified_payables_and_cw_balance( gn: &mut ThreadRng, accounts_count: usize, @@ -382,11 +391,15 @@ fn pick_appropriate_cw_service_fee_balance( // Values picked empirically const COEFFICIENT_A: usize = 1000; const COEFFICIENT_B: usize = 2; + let balance_average = sum_as(qualified_payables, |account| { account.initial_balance_minor() }) / accounts_count as u128; + let max_pieces = accounts_count * COEFFICIENT_A; - let number_of_pieces = generate_usize(gn, max_pieces - COEFFICIENT_B) as u128 + COEFFICIENT_B as u128; + let number_of_pieces = + generate_usize(gn, max_pieces - COEFFICIENT_B) as u128 + COEFFICIENT_B as u128; + balance_average / COEFFICIENT_A as u128 * number_of_pieces } @@ -400,12 +413,9 @@ fn make_payables_according_to_thresholds_setup( let nominated_thresholds = choose_thresholds(gn, &wallets); let payables = match &nominated_thresholds { - AppliedThresholds::Defaulted => make_payables_with_common_thresholds( - gn, - wallets, - &PRESERVED_TEST_PAYMENT_THRESHOLDS, - now, - ), + AppliedThresholds::Defaulted => { + make_payables_with_common_thresholds(gn, wallets, &PaymentThresholds::default(), now) + } AppliedThresholds::CommonButRandomized { common_thresholds } => { make_payables_with_common_thresholds(gn, wallets, common_thresholds, now) } @@ -519,12 +529,13 @@ fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { } fn administrate_single_scenario_result( + mut test_overall_output_collector: TestOverallOutputCollector, payment_adjuster_result: Result, account_infos: Vec, used_thresholds: AppliedThresholds, required_adjustment: Adjustment, cw_service_fee_balance_minor: u128, -) -> ScenarioResult { +) -> (TestOverallOutputCollector, ScenarioResult) { let common = CommonScenarioInfo { cw_service_fee_balance_minor, required_adjustment, @@ -537,17 +548,23 @@ fn administrate_single_scenario_result( PercentPortionOfCWUsed::new(&adjusted_accounts, &common); let merged = merge_information_about_particular_account(account_infos, adjusted_accounts); - let interpretable_adjustments = merged - .into_iter() - .map(InterpretableAccountAdjustmentResult::new) - .collect_vec(); - let (partially_sorted_interpretable_adjustments, were_no_accounts_eliminated) = + + let (interpretable_adjustments, adjusted_accounts_within_this_scenario) = + prepare_interpretable_adjustment_results(merged); + + look_after_adjustment_statistics( + &mut test_overall_output_collector, + adjusted_accounts_within_this_scenario, + ); + + let (partially_sorted_interpretable_adjustments, no_accounts_eliminated) = sort_interpretable_adjustments(interpretable_adjustments); + Ok(SuccessfulAdjustment { common, portion_of_cw_cumulatively_used_percents, partially_sorted_interpretable_adjustments, - no_accounts_eliminated: were_no_accounts_eliminated, + no_accounts_eliminated, }) } Err(adjuster_error) => Err(FailedAdjustment { @@ -557,7 +574,10 @@ fn administrate_single_scenario_result( }), }; - ScenarioResult::new(reinterpreted_result) + ( + test_overall_output_collector, + ScenarioResult::new(reinterpreted_result), + ) } fn merge_information_about_particular_account( @@ -578,6 +598,35 @@ fn merge_information_about_particular_account( .collect() } +fn prepare_interpretable_adjustment_results( + merged: Vec<(AccountInfo, Option)>, +) -> (Vec, usize) { + merged.into_iter().fold( + (vec![], 0), + |(mut interpretable_accounts, adjusted_accounts_so_far), + (info, maybe_eliminated_account)| { + let (interpretable_account, was_this_account_eliminated) = + InterpretableAccountAdjustmentResult::new(info, maybe_eliminated_account); + interpretable_accounts.push(interpretable_account); + ( + interpretable_accounts, + adjusted_accounts_so_far + if was_this_account_eliminated { 1 } else { 0 }, + ) + }, + ) +} + +fn look_after_adjustment_statistics( + test_overall_output_collector: &mut TestOverallOutputCollector, + adjusted_accounts_within_this_scenario: usize, +) { + if adjusted_accounts_within_this_scenario > 1 { + test_overall_output_collector.scenarios_with_more_than_one_adjustment += 1 + } + test_overall_output_collector.adjusted_account_count_total += + adjusted_accounts_within_this_scenario; +} + enum PercentPortionOfCWUsed { Percents(u8), LessThanOnePercent, @@ -681,7 +730,7 @@ fn render_results_to_file_and_attempt_basic_assertions( total_scenarios_handled_including_invalid_ones, number_of_requested_scenarios ); // Only some of the generated scenarios are acceptable, don't be surprised by the waste. That's - // anticipated given the nature of the generator and the requirements on the payable accounts + // expected given the nature of the generator and the requirements on the payable accounts // so that they are picked up and let in the PaymentAdjuster. We'll be better off truly faithful // to the use case and the expected conditions. Therefore, we insist on making "guaranteed" // QualifiedPayableAccounts out of PayableAccount which is where we take the losses. @@ -743,6 +792,9 @@ fn write_brief_test_summary_at_file_s_tail( Plain service fee adjustments:......... {}\n\ Bills fulfillment distribution:\n\ {}\n\n\ + Individual accounts adjusted:.......... {}\n\ + Scenarios with more than one account\n\ + adjusted:.............................. {}\n\n\ Unsuccessful\n\ Caught by the entry check:............. {}\n\ With 'RecursionDrainedAllAccounts':.... {}\n\ @@ -766,6 +818,8 @@ fn write_brief_test_summary_at_file_s_tail( output_collector .fulfillment_distribution_for_service_fee_adjustments .render_in_two_lines(), + output_collector.adjusted_account_count_total, + output_collector.scenarios_with_more_than_one_adjustment, output_collector.scenarios_denied_before_adjustment_started, output_collector.all_accounts_eliminated, output_collector.late_immoderately_insufficient_service_fee_balance, @@ -1081,6 +1135,8 @@ struct TestOverallOutputCollector { // ____________________________________ oks: usize, with_no_accounts_eliminated: usize, + adjusted_account_count_total: usize, + scenarios_with_more_than_one_adjustment: usize, fulfillment_distribution_for_transaction_fee_adjustments: PercentageFulfillmentDistribution, fulfillment_distribution_for_service_fee_adjustments: PercentageFulfillmentDistribution, // Errors @@ -1095,6 +1151,8 @@ impl TestOverallOutputCollector { scenarios_denied_before_adjustment_started: 0, oks: 0, with_no_accounts_eliminated: 0, + adjusted_account_count_total: 0, + scenarios_with_more_than_one_adjustment: 0, fulfillment_distribution_for_transaction_fee_adjustments: Default::default(), fulfillment_distribution_for_service_fee_adjustments: Default::default(), all_accounts_eliminated: 0, @@ -1238,27 +1296,30 @@ impl AccountWithWallet for InterpretableAccountAdjustmentResult { } impl InterpretableAccountAdjustmentResult { - fn new((info, non_eliminated_payable): (AccountInfo, Option)) -> Self { - let bill_coverage_in_percentage_opt = match &non_eliminated_payable { - Some(payable) => { - let bill_coverage_in_percentage = { - let percentage = - (payable.balance_wei * 100) / info.initially_requested_service_fee_minor; - u8::try_from(percentage).unwrap() - }; - Some(bill_coverage_in_percentage) - } - None => None, - }; - InterpretableAccountAdjustmentResult { - info: AccountInfo { - wallet: info.wallet, - debt_age_s: info.debt_age_s, - initially_requested_service_fee_minor: info.initially_requested_service_fee_minor, + fn new(info: AccountInfo, maybe_eliminated_payable: Option) -> (Self, bool) { + let (bill_coverage_in_percentage_opt, was_this_account_adjusted) = + match &maybe_eliminated_payable { + Some(payable) => { + let percents_of_bill_coverage = Self::percents_of_bill_coverage(&info, payable); + ( + Some(percents_of_bill_coverage), + percents_of_bill_coverage != 100, + ) + } + None => (None, false), + }; + ( + InterpretableAccountAdjustmentResult { + info, + bill_coverage_in_percentage_opt, }, + was_this_account_adjusted, + ) + } - bill_coverage_in_percentage_opt, - } + fn percents_of_bill_coverage(info: &AccountInfo, payable: &PayableAccount) -> u8 { + let percentage = (payable.balance_wei * 100) / info.initially_requested_service_fee_minor; + u8::try_from(percentage).unwrap() } } diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index a0eb39c34..f3b7e6b25 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -1327,9 +1327,7 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default()) - ) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let result = subject.begin_scan(now, None, &Logger::new(test_name)); @@ -1363,9 +1361,7 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let _result = subject.begin_scan(now, None, &Logger::new("test")); @@ -1388,9 +1384,7 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); let mut subject = PayableScannerBuilder::new() .payable_dao(payable_dao) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let result = subject.begin_scan(now, None, &Logger::new("test")); @@ -2186,9 +2180,7 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified_and_the_threshold_value_is_returned"; @@ -2219,9 +2211,7 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let test_name = "payable_with_debt_above_the_slope_is_qualified"; let logger = Logger::new(test_name); @@ -2262,9 +2252,7 @@ mod tests { }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let logger = Logger::new(test_name); @@ -2301,9 +2289,7 @@ mod tests { }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) - .payable_threshold_gauge(Box::new( - PayableThresholdsGaugeReal::default() - )) + .payable_threshold_gauge(Box::new(PayableThresholdsGaugeReal::default())) .build(); let intercept_before = subject .payable_exceeded_threshold(&payable, SystemTime::now()) diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index 9313d9bc6..748879593 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -316,7 +316,9 @@ pub mod payable_scanner_utils { eprintln!( "Balance wei: {}\n\ - Threshold: {}", payable.balance_wei, threshold); + Threshold: {}", + payable.balance_wei, threshold + ); if payable.balance_wei > threshold { Some(threshold) } else { diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index bb8b0b8a7..02d5a192a 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1133,7 +1133,10 @@ impl PayableScannerBuilder { self } - pub fn payable_threshold_gauge(mut self, payable_threshold_gauge: Box) -> Self { + pub fn payable_threshold_gauge( + mut self, + payable_threshold_gauge: Box, + ) -> Self { self.payable_inspector = PayableInspector::new(payable_threshold_gauge); self } From baf2745b249fc51c70cac32add37f08f99d49317 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 Sep 2025 11:24:32 +0200 Subject: [PATCH 248/250] GH-711: interim commit --- node/src/accountant/mod.rs | 2 +- node/src/accountant/payment_adjuster/mod.rs | 56 +++++++++---------- .../payment_adjuster/non_unit_tests/mod.rs | 44 +++++++-------- .../preparatory_analyser/mod.rs | 38 ++++++------- 4 files changed, 67 insertions(+), 73 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 292fda1c8..687657941 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1775,7 +1775,7 @@ mod tests { Adjustment::ByServiceFee, vec![make_meaningless_analyzed_account(123)], )))) - .adjust_payments_result(Err(PaymentAdjusterError::RecursionDrainedAllAccounts)); + .adjust_payments_result(Err(PaymentAdjusterError::RecursionEliminatedAllAccounts)); test_payment_adjuster_error_during_different_stages(test_name, payment_adjuster); diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index 690807adc..d77eebd12 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -34,7 +34,7 @@ use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ exhaust_cw_balance_entirely, find_largest_exceeding_balance, sum_as, no_affordable_accounts_found, }; -use crate::accountant::payment_adjuster::preparatory_analyser::{LateServiceFeeSingleTxErrorFactory, PreparatoryAnalyzer}; +use crate::accountant::payment_adjuster::preparatory_analyser::{LaterServiceFeeErrorFactory, PreparatoryAnalyzer}; use crate::accountant::payment_adjuster::service_fee_adjuster::{ ServiceFeeAdjuster, ServiceFeeAdjusterReal, }; @@ -212,7 +212,7 @@ impl PaymentAdjusterReal { let processed_accounts = self.resolve_initial_adjustment_dispatch(weighed_accounts)?; if no_affordable_accounts_found(&processed_accounts) { - return Err(PaymentAdjusterError::RecursionDrainedAllAccounts); + return Err(PaymentAdjusterError::RecursionEliminatedAllAccounts); } match processed_accounts { @@ -258,7 +258,7 @@ impl PaymentAdjusterReal { &weighed_accounts ); - let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); + let error_factory = LaterServiceFeeErrorFactory::new(&weighed_accounts); let weighed_accounts_affordable_by_transaction_fee = eliminate_accounts_by_tx_fee_limit(weighed_accounts, transaction_count_limit); @@ -313,21 +313,20 @@ impl PaymentAdjusterReal { } if !decided_accounts.is_empty() { - self.adjust_remaining_remaining_cw_balance_down(&decided_accounts) + self.adjust_remaining_cw_balance_down(&decided_accounts) } - let merged = - if self.is_cw_balance_enough_to_remaining_accounts(&remaining_undecided_accounts) { - Self::merge_accounts( - decided_accounts, - convert_collection(remaining_undecided_accounts), - ) - } else { - Self::merge_accounts( - decided_accounts, - self.propose_possible_adjustment_recursively(remaining_undecided_accounts), - ) - }; + let merged = if self.is_cw_balance_enough(&remaining_undecided_accounts) { + Self::merge_accounts( + decided_accounts, + convert_collection(remaining_undecided_accounts), + ) + } else { + Self::merge_accounts( + decided_accounts, + self.propose_possible_adjustment_recursively(remaining_undecided_accounts), + ) + }; diagnostics!( "\nFINAL SET OF ADJUSTED ACCOUNTS IN CURRENT ITERATION:", @@ -337,10 +336,7 @@ impl PaymentAdjusterReal { merged } - fn is_cw_balance_enough_to_remaining_accounts( - &self, - remaining_undecided_accounts: &[WeighedPayable], - ) -> bool { + fn is_cw_balance_enough(&self, remaining_undecided_accounts: &[WeighedPayable]) -> bool { let remaining_cw_service_fee_balance = self.inner.remaining_cw_service_fee_balance_minor(); let minimum_sum_required: u128 = sum_as(remaining_undecided_accounts, |weighed_account| { weighed_account.disqualification_limit() @@ -392,7 +388,7 @@ impl PaymentAdjusterReal { .collect() } - fn adjust_remaining_remaining_cw_balance_down( + fn adjust_remaining_cw_balance_down( &self, decided_accounts: &[AdjustedAccountBeforeFinalization], ) { @@ -476,7 +472,7 @@ pub enum PaymentAdjusterError { original_total_service_fee_required_minor: u128, cw_service_fee_balance_minor: u128, }, - RecursionDrainedAllAccounts, + RecursionEliminatedAllAccounts, } #[derive(Debug, PartialEq, Eq)] @@ -498,7 +494,7 @@ impl PaymentAdjusterError { PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { .. } => true, - PaymentAdjusterError::RecursionDrainedAllAccounts => true, + PaymentAdjusterError::RecursionEliminatedAllAccounts => true, // We haven't needed to worry in this matter yet, this is rather a future alarm that // will draw attention after somebody adds a possibility for an error not necessarily // implying that an insolvency was detected before. At the moment, each error occurs @@ -566,7 +562,7 @@ impl Display for PaymentAdjusterError { original_total_service_fee_required_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() ), - PaymentAdjusterError::RecursionDrainedAllAccounts => write!( + PaymentAdjusterError::RecursionEliminatedAllAccounts => write!( f, "The payments adjusting process failed to find any combination of payables that \ can be paid immediately with the finances provided." @@ -973,7 +969,7 @@ mod tests { required amount of service fee: 1,234,567,891,011 wei, while the wallet contains \ 333,333 wei."), ( - PaymentAdjusterError::RecursionDrainedAllAccounts, + PaymentAdjusterError::RecursionEliminatedAllAccounts, "The payments adjusting process failed to find any combination of payables that \ can be paid immediately with the finances provided.", ), @@ -1002,7 +998,7 @@ mod tests { #[test] fn we_can_say_if_error_occurred_after_insolvency_was_detected() { let inputs = vec![ - PaymentAdjusterError::RecursionDrainedAllAccounts, + PaymentAdjusterError::RecursionEliminatedAllAccounts, PaymentAdjusterError::AbsolutelyInsufficientBalance { number_of_accounts: 0, transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { @@ -1215,7 +1211,7 @@ mod tests { ok.affordable_accounts ), }; - assert_eq!(err, PaymentAdjusterError::RecursionDrainedAllAccounts) + assert_eq!(err, PaymentAdjusterError::RecursionEliminatedAllAccounts) } #[test] @@ -1351,7 +1347,7 @@ mod tests { Ok(_) => panic!("we expected err but got ok"), Err(e) => e, }; - assert_eq!(err, PaymentAdjusterError::RecursionDrainedAllAccounts); + assert_eq!(err, PaymentAdjusterError::RecursionEliminatedAllAccounts); let expected_log = |wallet: &str| { format!( "INFO: {test_name}: Ready payment to {wallet} was eliminated to spare MASQ for \ @@ -1405,7 +1401,7 @@ mod tests { initial_disqualification_limit_for_each_account; let weighed_payables = vec![payable_1, payable_2]; - let result = subject.is_cw_balance_enough_to_remaining_accounts(&weighed_payables); + let result = subject.is_cw_balance_enough(&weighed_payables); assert_eq!(result, expected_result) } @@ -2492,7 +2488,7 @@ mod tests { // It allows to halt the code executions without a dive in the recursion assert_eq!( actual_result, - Err(PaymentAdjusterError::RecursionDrainedAllAccounts) + Err(PaymentAdjusterError::RecursionEliminatedAllAccounts) ); let mut perform_adjustment_by_service_fee_params = perform_adjustment_by_service_fee_params_arc.lock().unwrap(); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 4fcd199db..58e509083 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -436,22 +436,22 @@ fn prepare_account_wallets(accounts_count: usize) -> Vec { fn choose_thresholds(gn: &mut ThreadRng, prepared_wallets: &[Wallet]) -> AppliedThresholds { let be_defaulted = generate_boolean(gn); if be_defaulted { - AppliedThresholds::Defaulted - } else { - let be_same_for_all_accounts = generate_boolean(gn); - if be_same_for_all_accounts { - AppliedThresholds::CommonButRandomized { - common_thresholds: return_single_randomized_thresholds(gn), - } - } else { - let individual_thresholds = prepared_wallets - .iter() - .map(|wallet| (wallet.address(), return_single_randomized_thresholds(gn))) - .collect::>(); - AppliedThresholds::RandomizedForEachAccount { - individual_thresholds, - } - } + return AppliedThresholds::Defaulted; + } + + let be_same_for_all_accounts = generate_boolean(gn); + if be_same_for_all_accounts { + return AppliedThresholds::CommonButRandomized { + common_thresholds: return_single_randomized_thresholds(gn), + }; + } + + let individual_thresholds = prepared_wallets + .iter() + .map(|wallet| (wallet.address(), return_single_randomized_thresholds(gn))) + .collect::>(); + AppliedThresholds::RandomizedForEachAccount { + individual_thresholds, } } @@ -512,7 +512,7 @@ fn make_agent(cw_service_fee_balance: u128) -> BlockchainAgentMock { .service_fee_balance_minor_result(cw_service_fee_balance) // For PaymentAdjuster itself .service_fee_balance_minor_result(cw_service_fee_balance) - .gas_price_margin_result(TX_FEE_MARGIN_IN_PERCENT.clone()) + .gas_price_margin_result(*TX_FEE_MARGIN_IN_PERCENT) } fn make_adjustment(gn: &mut ThreadRng, accounts_count: usize) -> Adjustment { @@ -636,10 +636,10 @@ impl PercentPortionOfCWUsed { fn new(adjusted_accounts: &[PayableAccount], common: &CommonScenarioInfo) -> Self { let used_absolute: u128 = sum_as(adjusted_accounts, |account| account.balance_wei); let percents = ((100 * used_absolute) / common.cw_service_fee_balance_minor) as u8; - if percents >= 1 { - PercentPortionOfCWUsed::Percents(percents) - } else { + if percents < 1 { PercentPortionOfCWUsed::LessThanOnePercent + } else { + PercentPortionOfCWUsed::Percents(percents) } } @@ -797,7 +797,7 @@ fn write_brief_test_summary_at_file_s_tail( adjusted:.............................. {}\n\n\ Unsuccessful\n\ Caught by the entry check:............. {}\n\ - With 'RecursionDrainedAllAccounts':.... {}\n\ + With 'RecursionEliminatedAllAccounts':.... {}\n\ With late insufficient balance errors:. {}\n\n\ Legend\n\ Adjusted balances are highlighted\n\ @@ -876,7 +876,7 @@ fn do_final_processing_of_single_scenario( PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { .. } => { output_collector.late_immoderately_insufficient_service_fee_balance += 1 } - PaymentAdjusterError::RecursionDrainedAllAccounts => { + PaymentAdjusterError::RecursionEliminatedAllAccounts => { output_collector.all_accounts_eliminated += 1 } } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index ced4dabc8..fdf1685f9 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -74,7 +74,7 @@ impl PreparatoryAnalyzer { ); let service_fee_check_result = if is_service_fee_adjustment_needed { - let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); + let error_factory = EarlyServiceFeeErrorFactory::default(); Self::check_adjustment_possibility( &prepared_accounts, @@ -143,7 +143,7 @@ impl PreparatoryAnalyzer { &self, weighed_accounts: &[WeighedPayable], cw_service_fee_balance_minor: u128, - error_factory: LateServiceFeeSingleTxErrorFactory, + error_factory: LaterServiceFeeErrorFactory, logger: &Logger, ) -> Result { if Self::is_service_fee_adjustment_needed( @@ -224,7 +224,7 @@ impl PreparatoryAnalyzer { ) -> Result<(), Error> where AnalyzableAccounts: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, - ErrorFactory: ServiceFeeSingleTXErrorFactory, + ErrorFactory: ServiceFeeErrorFactory, { let lowest_disqualification_limit = Self::find_lowest_disqualification_limit(prepared_accounts); @@ -296,7 +296,7 @@ impl PreparatoryAnalyzer { } } -pub trait ServiceFeeSingleTXErrorFactory +pub trait ServiceFeeErrorFactory where AnalyzableAccount: BalanceProvidingAccount, { @@ -304,10 +304,10 @@ where } #[derive(Default)] -pub struct EarlyServiceFeeSingleTXErrorFactory {} +pub struct EarlyServiceFeeErrorFactory {} -impl ServiceFeeSingleTXErrorFactory - for EarlyServiceFeeSingleTXErrorFactory +impl ServiceFeeErrorFactory + for EarlyServiceFeeErrorFactory { fn make( &self, @@ -324,12 +324,12 @@ impl ServiceFeeSingleTXErrorFactory Self { let original_number_of_accounts = unadjusted_accounts.len(); let original_total_service_fee_required_minor = sum_as(unadjusted_accounts, |account| { @@ -342,9 +342,7 @@ impl LateServiceFeeSingleTxErrorFactory { } } -impl ServiceFeeSingleTXErrorFactory - for LateServiceFeeSingleTxErrorFactory -{ +impl ServiceFeeErrorFactory for LaterServiceFeeErrorFactory { fn make( &self, current_set_of_accounts: &[WeighedPayable], @@ -372,8 +370,8 @@ mod tests { BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; use crate::accountant::payment_adjuster::preparatory_analyser::{ - EarlyServiceFeeSingleTXErrorFactory, LateServiceFeeSingleTxErrorFactory, - PreparatoryAnalyzer, ServiceFeeSingleTXErrorFactory, + EarlyServiceFeeErrorFactory, LaterServiceFeeErrorFactory, PreparatoryAnalyzer, + ServiceFeeErrorFactory, }; use crate::accountant::payment_adjuster::test_utils::local_utils::{ make_meaningless_weighed_account, multiply_by_billion, multiply_by_billion_concise, @@ -513,7 +511,7 @@ mod tests { ) where EnsureAccountsRightType: FnOnce(Vec) -> Vec, PrepareExpectedError: FnOnce(usize, u128, u128) -> Error, - ErrorFactory: ServiceFeeSingleTXErrorFactory, + ErrorFactory: ServiceFeeErrorFactory, Error: Debug + PartialEq, AnalyzableAccount: DisqualificationLimitProvidingAccount + BalanceProvidingAccount, { @@ -562,7 +560,7 @@ mod tests { #[test] fn not_enough_for_even_the_smallest_account_error_right_after_alarmed_tx_fee_check() { - let error_factory = EarlyServiceFeeSingleTXErrorFactory::default(); + let error_factory = EarlyServiceFeeErrorFactory::default(); let ensure_accounts_right_type = |weighed_payables: Vec| { weighed_payables .into_iter() @@ -596,7 +594,7 @@ mod tests { let initial_sum = sum_as(&original_accounts, |account| { account.initial_balance_minor() }); - let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts); + let error_factory = LaterServiceFeeErrorFactory::new(&original_accounts); let ensure_accounts_right_type = |accounts| accounts; let prepare_expected_error = |number_of_accounts, _, cw_service_fee_balance_minor| { PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { @@ -635,7 +633,7 @@ mod tests { .balance_wei = balance_2; let accounts = vec![weighed_account_1, weighed_account_2]; let service_fee_totally_required_minor = balance_1 + balance_2; - let error_factory = LateServiceFeeSingleTxErrorFactory::new(&accounts); + let error_factory = LaterServiceFeeErrorFactory::new(&accounts); let logger = Logger::new(test_name); let subject = PreparatoryAnalyzer::new(); @@ -683,11 +681,11 @@ mod tests { .balance_wei = balance_2; let weighed_accounts = vec![account_1, account_2]; - let result = LateServiceFeeSingleTxErrorFactory::new(&weighed_accounts); + let result = LaterServiceFeeErrorFactory::new(&weighed_accounts); assert_eq!( result, - LateServiceFeeSingleTxErrorFactory { + LaterServiceFeeErrorFactory { original_number_of_accounts: 2, original_total_service_fee_required_minor: balance_1 + balance_2 } From 3499edd69fdfa6cb00f7ba5686c5d4257bb0ca9a Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 Sep 2025 12:13:02 +0200 Subject: [PATCH 249/250] GH-711: interim commit --- .../miscellaneous/data_structures.rs | 5 ++ .../miscellaneous/helper_functions.rs | 28 +++++---- node/src/accountant/payment_adjuster/mod.rs | 61 +++++++++---------- .../payment_adjuster/non_unit_tests/mod.rs | 4 +- .../preparatory_analyser/mod.rs | 8 +-- 5 files changed, 56 insertions(+), 50 deletions(-) diff --git a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs index 597aaaf61..23156e7ae 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/data_structures.rs @@ -101,6 +101,11 @@ impl AffordableAndRequiredTxCounts { } } +pub enum AccountsByFinalization { + Unexhausted(Vec), + Finalized(Vec), +} + #[cfg(test)] mod tests { use crate::accountant::payment_adjuster::miscellaneous::data_structures::AffordableAndRequiredTxCounts; diff --git a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs index cd7448fb2..0a276cf17 100644 --- a/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs +++ b/node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs @@ -6,16 +6,14 @@ use crate::accountant::payment_adjuster::diagnostics; use crate::accountant::payment_adjuster::logging_and_diagnostics::diagnostics::ordinary_diagnostic_functions::{ exhausting_cw_balance_diagnostics, not_exhausting_cw_balance_diagnostics, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeighedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AccountsByFinalization, AdjustedAccountBeforeFinalization, WeighedPayable}; use crate::accountant::{AnalyzedPayableAccount}; -use itertools::{Either, Itertools}; +use itertools::{Itertools}; -pub fn no_affordable_accounts_found( - accounts: &Either, Vec>, -) -> bool { +pub fn no_affordable_accounts_found(accounts: &AccountsByFinalization) -> bool { match accounts { - Either::Left(vector) => vector.is_empty(), - Either::Right(vector) => vector.is_empty(), + AccountsByFinalization::Finalized(vector) => vector.is_empty(), + AccountsByFinalization::Unexhausted(vector) => vector.is_empty(), } } @@ -185,7 +183,9 @@ impl ConsumingWalletExhaustingStatus { #[cfg(test)] mod tests { use crate::accountant::db_access_objects::payable_dao::PayableAccount; - use crate::accountant::payment_adjuster::miscellaneous::data_structures::AdjustedAccountBeforeFinalization; + use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ + AccountsByFinalization, AdjustedAccountBeforeFinalization, + }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ compute_mul_coefficient_preventing_fractional_numbers, eliminate_accounts_by_tx_fee_limit, exhaust_cw_balance_entirely, find_largest_exceeding_balance, no_affordable_accounts_found, @@ -195,19 +195,19 @@ mod tests { use crate::accountant::test_utils::{make_meaningless_analyzed_account, make_payable_account}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; - use itertools::{Either, Itertools}; + use itertools::Itertools; use std::time::SystemTime; #[test] fn no_affordable_accounts_found_returns_true_for_non_finalized_accounts() { - let result = no_affordable_accounts_found(&Either::Left(vec![])); + let result = no_affordable_accounts_found(&AccountsByFinalization::Unexhausted(vec![])); assert_eq!(result, true) } #[test] fn no_affordable_accounts_found_returns_false_for_non_finalized_accounts() { - let result = no_affordable_accounts_found(&Either::Left(vec![ + let result = no_affordable_accounts_found(&AccountsByFinalization::Unexhausted(vec![ AdjustedAccountBeforeFinalization::new(make_payable_account(456), 5678, 1234), ])); @@ -216,14 +216,16 @@ mod tests { #[test] fn no_affordable_accounts_found_returns_true_for_finalized_accounts() { - let result = no_affordable_accounts_found(&Either::Right(vec![])); + let result = no_affordable_accounts_found(&AccountsByFinalization::Finalized(vec![])); assert_eq!(result, true) } #[test] fn no_affordable_accounts_found_returns_false_for_finalized_accounts() { - let result = no_affordable_accounts_found(&Either::Right(vec![make_payable_account(123)])); + let result = no_affordable_accounts_found(&AccountsByFinalization::Finalized(vec![ + make_payable_account(123), + ])); assert_eq!(result, false) } diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index d77eebd12..e88badcea 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -28,7 +28,7 @@ use crate::accountant::payment_adjuster::inner::{ use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::{ accounts_before_and_after_debug, }; -use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AdjustedAccountBeforeFinalization, WeighedPayable}; +use crate::accountant::payment_adjuster::miscellaneous::data_structures::{AccountsByFinalization, AdjustedAccountBeforeFinalization, WeighedPayable}; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ eliminate_accounts_by_tx_fee_limit, exhaust_cw_balance_entirely, find_largest_exceeding_balance, @@ -216,31 +216,25 @@ impl PaymentAdjusterReal { } match processed_accounts { - Either::Left(non_exhausted_accounts) => { - let original_cw_service_fee_balance_minor = - self.inner.original_cw_service_fee_balance_minor(); - let exhaustive_affordable_accounts = exhaust_cw_balance_entirely( - non_exhausted_accounts, - original_cw_service_fee_balance_minor, - ); - Ok(exhaustive_affordable_accounts) + AccountsByFinalization::Unexhausted(unexhausted_accounts) => { + Ok(exhaust_cw_balance_entirely( + unexhausted_accounts, + self.inner.original_cw_service_fee_balance_minor(), + )) } - Either::Right(finalized_accounts) => Ok(finalized_accounts), + AccountsByFinalization::Finalized(accounts) => Ok(accounts), } } fn resolve_initial_adjustment_dispatch( &self, weighed_payables: Vec, - ) -> Result< - Either, Vec>, - PaymentAdjusterError, - > { + ) -> Result { if let Some(limit) = self.inner.transaction_count_limit_opt() { return self.begin_with_adjustment_by_transaction_fee(weighed_payables, limit); } - Ok(Either::Left( + Ok(AccountsByFinalization::Unexhausted( self.propose_possible_adjustment_recursively(weighed_payables), )) } @@ -249,10 +243,7 @@ impl PaymentAdjusterReal { &self, weighed_accounts: Vec, transaction_count_limit: u16, - ) -> Result< - Either, Vec>, - PaymentAdjusterError, - > { + ) -> Result { diagnostics!( "\nBEGINNING WITH ADJUSTMENT BY TRANSACTION FEE FOR ACCOUNTS:", &weighed_accounts @@ -276,12 +267,16 @@ impl PaymentAdjusterReal { weighed_accounts_affordable_by_transaction_fee, ); - Ok(Either::Left(final_set_before_exhausting_cw_balance)) + Ok(AccountsByFinalization::Unexhausted( + final_set_before_exhausting_cw_balance, + )) } else { let accounts_not_needing_adjustment = convert_collection(weighed_accounts_affordable_by_transaction_fee); - Ok(Either::Right(accounts_not_needing_adjustment)) + Ok(AccountsByFinalization::Finalized( + accounts_not_needing_adjustment, + )) } } @@ -577,7 +572,7 @@ mod tests { use crate::accountant::payment_adjuster::inner::PaymentAdjusterInner; use crate::accountant::payment_adjuster::logging_and_diagnostics::log_functions::LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY; use crate::accountant::payment_adjuster::miscellaneous::data_structures::{ - AdjustmentIterationResult, WeighedPayable, + AccountsByFinalization, AdjustmentIterationResult, WeighedPayable, }; use crate::accountant::payment_adjuster::miscellaneous::helper_functions::{ find_largest_exceeding_balance, sum_as, @@ -1071,12 +1066,13 @@ mod tests { WeighedPayable::new(account_2, weighed_account_2), ]; - let mut result = subject - .resolve_initial_adjustment_dispatch(weighed_payables.clone()) - .unwrap() - .left() - .unwrap(); + let result = subject.resolve_initial_adjustment_dispatch(weighed_payables.clone()); + let mut accounts = if let AccountsByFinalization::Unexhausted(accounts) = result.unwrap() { + accounts + } else { + panic!("We expected unexhausted accounts but got those already finalized") + }; // This shows how the weights can turn tricky for which it's important to have a hard upper // limit, chosen quite down, as the disqualification limit, for optimisation. In its // extremity, the naked algorithm of the reallocation of funds could have granted a value @@ -1095,19 +1091,19 @@ mod tests { .analyzed_account .qualified_as .bare_account; - let first_returned_account = result.remove(0); + let first_returned_account = accounts.remove(0); assert_eq!(&first_returned_account.original_account, payable_account_2); assert_eq!( first_returned_account.proposed_adjusted_balance_minor, disqualification_limit_2 ); - let second_returned_account = result.remove(0); + let second_returned_account = accounts.remove(0); assert_eq!(&second_returned_account.original_account, payable_account_1); assert_eq!( second_returned_account.proposed_adjusted_balance_minor, disqualification_limit_1 ); - assert!(result.is_empty()); + assert!(accounts.is_empty()); } #[test] @@ -2289,12 +2285,15 @@ mod tests { .into_iter() .map(|calculator| calculator.calculate(&qualified_payable, &context)) .fold(0, |previous_result, current_result| { + // Testing a bigger gap between the values of the different calculators (we don't + // want to use previous_result != current_result because that could also mean + // a difference by one or a similarly negligible value) let slightly_less_than_current = (current_result * 97) / 100; let slightly_more_than_current = (current_result * 103) / 100; assert_ne!(current_result, 0); assert!( previous_result <= slightly_less_than_current - || slightly_more_than_current <= previous_result + || previous_result >= slightly_more_than_current ); current_result }); diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index 58e509083..e622b1ce8 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -42,7 +42,7 @@ use web3::types::{Address, U256}; #[test] // TODO If an option for "occasional tests" is added, this is a good adept -//#[ignore] +#[ignore] fn loading_test_with_randomized_params() { // This is a fuzz test. It generates possibly an overwhelming number of scenarios that // the PaymentAdjuster could be given to sort them out, as realistic as it can get, while its @@ -90,7 +90,7 @@ fn loading_test_with_randomized_params() { // to be excluded) might get shrunk a lot, and therefore many percents are to be reported as // missing. This is what the numbers like 99% and 90% illustrate. That said, the letter account // comes across as it should take precedence for its expectedly larger weight and gain at the - // expanse of the other, but the percents speak otherwise. Yet, it's correct. The interpretation + // expanse of the other, but the percents speak otherwise. Yet it's correct. The interpretation // is the key. (Caution: this test displays its output with those accounts sorted). // CW service fee balance: 32,041,461,894,055,482 wei diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index fdf1685f9..ac8939cd6 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -229,10 +229,10 @@ impl PreparatoryAnalyzer { let lowest_disqualification_limit = Self::find_lowest_disqualification_limit(prepared_accounts); - // We cannot do much in this area but stepping in if the cw balance is zero or nearly + // We can do little in this area but stepping in if the cw balance is zero or nearly // zero with the assumption that the debt with the lowest disqualification limit in // the set fits in the available balance. If it doesn't, we're not going to bother - // the payment adjuster by that work, so it'll abort and no payments will come out. + // the payment adjuster by that work, so it'll abort, and no payments will come out. if lowest_disqualification_limit <= cw_service_fee_balance_minor { Ok(()) } else { @@ -638,9 +638,9 @@ mod tests { let subject = PreparatoryAnalyzer::new(); [ - (service_fee_totally_required_minor - 1, false), + (service_fee_totally_required_minor - 1, true), (service_fee_totally_required_minor, false), - (service_fee_totally_required_minor + 1, true), + (service_fee_totally_required_minor + 1, false), ] .iter() .for_each(|(service_fee_balance, adjustment_is_needed_expected)| { From 2e150b4e0f7ad891eacb151f7bbd606ab1693eda Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 25 Sep 2025 13:24:34 +0200 Subject: [PATCH 250/250] GH-711: review two finished --- node/src/accountant/mod.rs | 40 +-- .../logging_and_diagnostics/log_functions.rs | 8 +- node/src/accountant/payment_adjuster/mod.rs | 262 ++++++++++-------- .../payment_adjuster/non_unit_tests/mod.rs | 19 +- .../preparatory_analyser/mod.rs | 38 +-- node/src/accountant/scanners/mod.rs | 4 +- 6 files changed, 215 insertions(+), 156 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 687657941..a3a27257e 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1080,7 +1080,7 @@ mod tests { use crate::accountant::db_access_objects::utils::{from_time_t, to_time_t, CustomQuery}; use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, + Adjustment, AdjustmentAnalysisReport, DetectionPhase, PaymentAdjusterError, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -1737,13 +1737,15 @@ mod tests { let test_name = "payment_adjuster_throws_out_an_error_during_stage_one_the_insolvency_check"; let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 1, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), - cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), - }), - service_fee_opt: None, + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: gwei_to_wei(60_u64 * 55_000), + cw_transaction_fee_balance_minor: gwei_to_wei(123_u64), + }), + service_fee_opt: None, + }, }, )); @@ -1761,7 +1763,7 @@ mod tests { .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ - Resolution of the issue appears to be the user's responsibility." + It looks like only the user can resolve this issue." )); } @@ -1782,8 +1784,8 @@ mod tests { let log_handler = TestLogHandler::new(); log_handler.exists_log_containing(&format!( "WARN: {test_name}: Payment adjustment has not produced any executable payments. Add \ - more funds into your consuming wallet in order to become able to repay already expired \ - liabilities as the creditors would respond by delinquency bans otherwise. Details: The \ + more funds into your consuming wallet to become able to repay already matured debts as \ + the creditors would respond by a delinquency ban otherwise. Details: The \ payments adjusting process failed to find any combination of payables that can be paid \ immediately with the finances provided" )); @@ -1791,7 +1793,7 @@ mod tests { .exists_log_containing(&format!("INFO: {test_name}: The Payables scan ended in")); log_handler.exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ - Resolution of the issue appears to be the user's responsibility." + It looks like only the user can resolve this issue." )); } @@ -1802,13 +1804,15 @@ mod tests { "payment_adjuster_error_is_not_reported_to_ui_if_scan_not_manually_requested"; let mut subject = AccountantBuilder::default().build(); let payment_adjuster = PaymentAdjusterMock::default().consider_adjustment_result(Err( - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 20, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: 40_000_000_000, - cw_transaction_fee_balance_minor: U256::from(123), - }), - service_fee_opt: None, + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 40_000_000_000, + cw_transaction_fee_balance_minor: U256::from(123), + }), + service_fee_opt: None, + }, }, )); let payable_scanner = PayableScannerBuilder::new() @@ -1831,7 +1835,7 @@ mod tests { // the fact that the test didn't blow up even though UIGateway is unbound TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: Payable scanner is unable to generate payment instructions. \ - It seems only the user can solve this problem." + It looks like only the user can resolve this issue." )); } diff --git a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs index 6f3cf030b..ed3d5934e 100644 --- a/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs +++ b/node/src/accountant/payment_adjuster/logging_and_diagnostics/log_functions.rs @@ -11,8 +11,8 @@ use thousands::Separable; use web3::types::{Address, U256}; const REFILL_RECOMMENDATION: &str = "\ -Please be aware that abandoning your debts is going to result in delinquency bans. In order to \ -consume services without limitations, you will need to place more funds into your consuming wallet."; +Please be aware that abandoning your debts is going to result in delinquency bans. To consume \ +services without limitations, you will need to place more funds into your consuming wallet."; pub const LATER_DETECTED_SERVICE_FEE_SEVERE_SCARCITY: &str = "\ Passed successfully adjustment by transaction fee, then rechecked the service fee balance to be \ applied on the adjusted set, but discovered a shortage of MASQ not to suffice even for a single \ @@ -203,8 +203,8 @@ mod tests { fn constants_are_correct() { assert_eq!( REFILL_RECOMMENDATION, - "Please be aware that abandoning your debts is going to result in delinquency bans. In \ - order to consume services without limitations, you will need to place more funds into \ + "Please be aware that abandoning your debts is going to result in delinquency bans. \ + To consume services without limitations, you will need to place more funds into \ your consuming wallet." ); assert_eq!( diff --git a/node/src/accountant/payment_adjuster/mod.rs b/node/src/accountant/payment_adjuster/mod.rs index e88badcea..1a9a7c6bc 100644 --- a/node/src/accountant/payment_adjuster/mod.rs +++ b/node/src/accountant/payment_adjuster/mod.rs @@ -456,18 +456,35 @@ impl AdjustmentAnalysisReport { #[derive(Debug, PartialEq, Eq, VariantCount)] pub enum PaymentAdjusterError { - AbsolutelyInsufficientBalance { + AbsoluteFeeInsufficiency { number_of_accounts: usize, + detection_phase: DetectionPhase, + }, + // AbsolutelyInsufficientBalance { + // number_of_accounts: usize, + // transaction_fee_opt: Option, + // service_fee_opt: Option, + // }, + // AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { + // original_number_of_accounts: usize, + // number_of_accounts: usize, + // original_total_service_fee_required_minor: u128, + // cw_service_fee_balance_minor: u128, + // }, + RecursionEliminatedAllAccounts, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum DetectionPhase { + InitialCheck { transaction_fee_opt: Option, service_fee_opt: Option, }, - AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { + PostTxFeeAdjustment { original_number_of_accounts: usize, - number_of_accounts: usize, original_total_service_fee_required_minor: u128, cw_service_fee_balance_minor: u128, }, - RecursionEliminatedAllAccounts, } #[derive(Debug, PartialEq, Eq)] @@ -485,12 +502,14 @@ pub struct ServiceFeeImmoderateInsufficiency { impl PaymentAdjusterError { pub fn insolvency_detected(&self) -> bool { match self { - PaymentAdjusterError::AbsolutelyInsufficientBalance { .. } => true, - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - .. - } => true, + PaymentAdjusterError::AbsoluteFeeInsufficiency { + detection_phase, .. + } => match detection_phase { + DetectionPhase::InitialCheck { .. } => true, + DetectionPhase::PostTxFeeAdjustment { .. } => true, + }, PaymentAdjusterError::RecursionEliminatedAllAccounts => true, - // We haven't needed to worry in this matter yet, this is rather a future alarm that + // We haven't needed to worry about this matter, yet this is rather a future alarm that // will draw attention after somebody adds a possibility for an error not necessarily // implying that an insolvency was detected before. At the moment, each error occurs // only alongside an actual insolvency. (Hint: There might be consequences for @@ -503,12 +522,16 @@ impl PaymentAdjusterError { impl Display for PaymentAdjusterError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - transaction_fee_opt, - service_fee_opt, + detection_phase } => { - match (transaction_fee_opt, service_fee_opt) { + match detection_phase { + DetectionPhase::InitialCheck { + transaction_fee_opt, + service_fee_opt + } => + match (transaction_fee_opt, service_fee_opt) { (Some(transaction_fee_check_summary), None) => write!( f, @@ -541,14 +564,9 @@ impl Display for PaymentAdjusterError { service_fee_check_summary.cw_service_fee_balance_minor.separate_with_commas() ), (None, None) => unreachable!("This error contains no specifications") - } - }, - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts, - number_of_accounts, - original_total_service_fee_required_minor, - cw_service_fee_balance_minor, - } => write!(f, "The original set with {} accounts was adjusted down to {} due to \ + }, + DetectionPhase::PostTxFeeAdjustment { original_number_of_accounts, original_total_service_fee_required_minor, cw_service_fee_balance_minor } + => write!(f, "The original set with {} accounts was adjusted down to {} due to \ transaction fee. The new set was tested on service fee later again and did not \ pass. Original required amount of service fee: {} wei, while the wallet \ contains {} wei.", @@ -556,7 +574,7 @@ impl Display for PaymentAdjusterError { number_of_accounts, original_total_service_fee_required_minor.separate_with_commas(), cw_service_fee_balance_minor.separate_with_commas() - ), + )}}, PaymentAdjusterError::RecursionEliminatedAllAccounts => write!( f, "The payments adjusting process failed to find any combination of payables that \ @@ -586,8 +604,8 @@ mod tests { MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR, PRESERVED_TEST_PAYMENT_THRESHOLDS, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, - PaymentAdjusterReal, ServiceFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, DetectionPhase, PaymentAdjuster, + PaymentAdjusterError, PaymentAdjusterReal, ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; @@ -734,7 +752,7 @@ mod tests { )); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ - delinquency bans. In order to consume services without limitations, you will need to \ + delinquency bans. To consume services without limitations, you will need to \ place more funds into your consuming wallet." )); } @@ -775,7 +793,7 @@ mod tests { )); log_handler.exists_log_containing(&format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to result in \ - delinquency bans. In order to consume services without limitations, you will need to \ + delinquency bans. To consume services without limitations, you will need to \ place more funds into your consuming wallet." )); } @@ -810,13 +828,15 @@ mod tests { }; assert_eq!( result, - Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + Err(PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor: cw_transaction_fee_balance_minor.into(), - }), - service_fee_opt: None + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor: cw_transaction_fee_balance_minor.into(), + }), + service_fee_opt: None + } }) ); } @@ -855,13 +875,15 @@ mod tests { assert_eq!( result, - Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + Err(PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 3, - transaction_fee_opt: None, - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: multiply_by_billion(920), - cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance - }) + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: multiply_by_billion(920), + cw_service_fee_balance_minor: actual_insufficient_cw_service_fee_balance + }) + } }) ); } @@ -892,16 +914,18 @@ mod tests { TX_FEE_MARGIN_IN_PERCENT.increase_by_percent_for(55_000 * multiply_by_billion(123)); assert_eq!( result, - Err(PaymentAdjusterError::AbsolutelyInsufficientBalance { + Err(PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor, - cw_transaction_fee_balance_minor: U256::zero(), - }), - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: multiply_by_billion(500), - cw_service_fee_balance_minor: 0 - }) + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor, + cw_transaction_fee_balance_minor: U256::zero(), + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: multiply_by_billion(500), + cw_service_fee_balance_minor: 0 + }) + } }) ); } @@ -910,42 +934,48 @@ mod tests { fn payment_adjuster_error_implements_display() { let inputs = vec![ ( - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 4, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ - per_transaction_requirement_minor: multiply_by_billion(70_000), - cw_transaction_fee_balance_minor: U256::from(90_000), - }), - service_fee_opt: None + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: multiply_by_billion(70_000), + cw_transaction_fee_balance_minor: U256::from(90_000), + }), + service_fee_opt: None + } }, "Current transaction fee balance is not enough to pay a single payment. Number of \ canceled payments: 4. Transaction fee per payment: 70,000,000,000,000 wei, while \ the wallet contains: 90,000 wei", ), ( - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 5, - transaction_fee_opt: None, - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency{ - total_service_fee_required_minor: 6_000_000_000, - cw_service_fee_balance_minor: 333_000_000, - }) + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 6_000_000_000, + cw_service_fee_balance_minor: 333_000_000, + }) + } }, "Current service fee balance is not enough to pay a single payment. Number of \ canceled payments: 5. Total amount required: 6,000,000,000 wei, while the wallet \ contains: 333,000,000 wei", ), ( - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 5, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency{ - per_transaction_requirement_minor: 5_000_000_000, - cw_transaction_fee_balance_minor: U256::from(3_000_000_000_u64) - }), - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency{ - total_service_fee_required_minor: 7_000_000_000, - cw_service_fee_balance_minor: 100_000_000 - }) + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 5_000_000_000, + cw_transaction_fee_balance_minor: U256::from(3_000_000_000_u64) + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 7_000_000_000, + cw_service_fee_balance_minor: 100_000_000 + }) + } }, "Neither transaction fee nor service fee balance is enough to pay a single payment. \ Number of payments considered: 5. Transaction fee per payment: 5,000,000,000 wei, \ @@ -953,11 +983,13 @@ mod tests { while in wallet: 100,000,000 wei", ), ( - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts: 6, + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 3, - original_total_service_fee_required_minor: 1234567891011, - cw_service_fee_balance_minor: 333333, + detection_phase: DetectionPhase::PostTxFeeAdjustment { + original_number_of_accounts: 6, + original_total_service_fee_required_minor: 1234567891011, + cw_service_fee_balance_minor: 333333, + } }, "The original set with 6 accounts was adjusted down to 3 due to transaction fee. \ The new set was tested on service fee later again and did not pass. Original \ @@ -973,7 +1005,7 @@ mod tests { inputs .into_iter() .for_each(|(error, expected_msg)| assert_eq!(error.to_string(), expected_msg)); - assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 2) + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 3) } #[test] @@ -982,10 +1014,12 @@ mod tests { specifications" )] fn error_message_for_input_referring_to_no_issues_cannot_be_made() { - let _ = PaymentAdjusterError::AbsolutelyInsufficientBalance { + let _ = PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 0, - transaction_fee_opt: None, - service_fee_opt: None, + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: None, + service_fee_opt: None, + }, } .to_string(); } @@ -994,38 +1028,46 @@ mod tests { fn we_can_say_if_error_occurred_after_insolvency_was_detected() { let inputs = vec![ PaymentAdjusterError::RecursionEliminatedAllAccounts, - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 0, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: 0, - cw_transaction_fee_balance_minor: Default::default(), - }), - service_fee_opt: None, + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 0, + cw_transaction_fee_balance_minor: Default::default(), + }), + service_fee_opt: None, + }, }, - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 0, - transaction_fee_opt: None, - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: 0, - cw_service_fee_balance_minor: 0, - }), + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: None, + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + }), + }, }, - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 0, - transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { - per_transaction_requirement_minor: 0, - cw_transaction_fee_balance_minor: Default::default(), - }), - service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { - total_service_fee_required_minor: 0, - cw_service_fee_balance_minor: 0, - }), + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: Some(TransactionFeeImmoderateInsufficiency { + per_transaction_requirement_minor: 0, + cw_transaction_fee_balance_minor: Default::default(), + }), + service_fee_opt: Some(ServiceFeeImmoderateInsufficiency { + total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + }), + }, }, - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts: 0, + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 0, - original_total_service_fee_required_minor: 0, - cw_service_fee_balance_minor: 0, + detection_phase: DetectionPhase::PostTxFeeAdjustment { + original_number_of_accounts: 0, + original_total_service_fee_required_minor: 0, + cw_service_fee_balance_minor: 0, + }, }, ]; let inputs_count = inputs.len(); @@ -1034,7 +1076,7 @@ mod tests { .map(|err| err.insolvency_detected()) .collect::>(); assert_eq!(results, vec![true, true, true, true, true]); - assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 2) + assert_eq!(inputs_count, PaymentAdjusterError::VARIANT_COUNT + 3) } #[test] @@ -2089,13 +2131,15 @@ mod tests { }; assert_eq!( err, - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts: 3, + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts: 2, - original_total_service_fee_required_minor: balance_account_1 - + balance_account_2 - + balance_account_3, - cw_service_fee_balance_minor + detection_phase: DetectionPhase::PostTxFeeAdjustment { + original_number_of_accounts: 3, + original_total_service_fee_required_minor: balance_account_1 + + balance_account_2 + + balance_account_3, + cw_service_fee_balance_minor + } } ); TestLogHandler::new().assert_logs_contain_in_order(vec![ @@ -2106,7 +2150,7 @@ mod tests { ), &format!( "INFO: {test_name}: Please be aware that abandoning your debts is going to \ - result in delinquency bans. In order to consume services without limitations, you \ + result in delinquency bans. To consume services without limitations, you \ will need to place more funds into your consuming wallet.", ), &format!( diff --git a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs index e622b1ce8..49bcddcce 100644 --- a/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs +++ b/node/src/accountant/payment_adjuster/non_unit_tests/mod.rs @@ -9,7 +9,7 @@ use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstract use crate::accountant::payment_adjuster::test_utils::exposed_utils::convert_qualified_p_into_analyzed_p; use crate::accountant::payment_adjuster::test_utils::local_utils::PRESERVED_TEST_PAYMENT_THRESHOLDS; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjuster, PaymentAdjusterError, + Adjustment, AdjustmentAnalysisReport, DetectionPhase, PaymentAdjuster, PaymentAdjusterError, PaymentAdjusterReal, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -870,12 +870,17 @@ fn do_final_processing_of_single_scenario( } Err(negative) => { match negative.adjuster_error { - PaymentAdjusterError::AbsolutelyInsufficientBalance { .. } => { - panic!("Such errors should be already filtered out") - } - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { .. } => { - output_collector.late_immoderately_insufficient_service_fee_balance += 1 - } + PaymentAdjusterError::AbsoluteFeeInsufficiency { + ref detection_phase, + .. + } => match detection_phase { + DetectionPhase::InitialCheck { .. } => { + panic!("Such errors should be already filtered out") + } + DetectionPhase::PostTxFeeAdjustment { .. } => { + output_collector.late_immoderately_insufficient_service_fee_balance += 1 + } + }, PaymentAdjusterError::RecursionEliminatedAllAccounts => { output_collector.all_accounts_eliminated += 1 } diff --git a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs index ac8939cd6..d16d8c3b5 100644 --- a/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs +++ b/node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs @@ -15,8 +15,8 @@ use crate::accountant::payment_adjuster::preparatory_analyser::accounts_abstract BalanceProvidingAccount, DisqualificationLimitProvidingAccount, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, - TransactionFeeImmoderateInsufficiency, + Adjustment, AdjustmentAnalysisReport, DetectionPhase, PaymentAdjusterError, + ServiceFeeImmoderateInsufficiency, TransactionFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::blockchain_agent::BlockchainAgent; use crate::accountant::{AnalyzedPayableAccount, QualifiedPayableAccount}; @@ -115,10 +115,12 @@ impl PreparatoryAnalyzer { let construct_error = |tx_fee_check_err_opt: Option, service_fee_check_err_opt: Option| { - PaymentAdjusterError::AbsolutelyInsufficientBalance { + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - transaction_fee_opt: tx_fee_check_err_opt, - service_fee_opt: service_fee_check_err_opt, + detection_phase: DetectionPhase::InitialCheck { + transaction_fee_opt: tx_fee_check_err_opt, + service_fee_opt: service_fee_check_err_opt, + }, } }; @@ -349,12 +351,14 @@ impl ServiceFeeErrorFactory for LaterServi cw_service_fee_balance_minor: u128, ) -> PaymentAdjusterError { let number_of_accounts = current_set_of_accounts.len(); - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts: self.original_number_of_accounts, + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - original_total_service_fee_required_minor: self - .original_total_service_fee_required_minor, - cw_service_fee_balance_minor, + detection_phase: DetectionPhase::PostTxFeeAdjustment { + cw_service_fee_balance_minor, + original_number_of_accounts: self.original_number_of_accounts, + original_total_service_fee_required_minor: self + .original_total_service_fee_required_minor, + }, } } } @@ -378,7 +382,7 @@ mod tests { DisqualificationGaugeMock, }; use crate::accountant::payment_adjuster::{ - Adjustment, AdjustmentAnalysisReport, PaymentAdjusterError, + Adjustment, AdjustmentAnalysisReport, DetectionPhase, PaymentAdjusterError, ServiceFeeImmoderateInsufficiency, }; use crate::accountant::scanners::mid_scan_msg_handling::payable_scanner::test_utils::BlockchainAgentMock; @@ -591,17 +595,19 @@ mod tests { make_meaningless_weighed_account(1011), ]; let original_number_of_accounts = original_accounts.len(); - let initial_sum = sum_as(&original_accounts, |account| { + let original_total_service_fee_required_minor = sum_as(&original_accounts, |account| { account.initial_balance_minor() }); let error_factory = LaterServiceFeeErrorFactory::new(&original_accounts); let ensure_accounts_right_type = |accounts| accounts; let prepare_expected_error = |number_of_accounts, _, cw_service_fee_balance_minor| { - PaymentAdjusterError::AbsolutelyInsufficientServiceFeeBalancePostTxFeeAdjustment { - original_number_of_accounts, + PaymentAdjusterError::AbsoluteFeeInsufficiency { number_of_accounts, - original_total_service_fee_required_minor: initial_sum, - cw_service_fee_balance_minor, + detection_phase: DetectionPhase::PostTxFeeAdjustment { + original_number_of_accounts, + cw_service_fee_balance_minor, + original_total_service_fee_required_minor, + }, } }; diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index f3b7e6b25..3b50910af 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -330,8 +330,8 @@ impl SolvencySensitivePaymentInstructor for PayableScanner { fn cancel_scan(&mut self, logger: &Logger) { error!( logger, - "Payable scanner is unable to generate payment instructions. It seems only the user can \ - solve this problem." + "Payable scanner is unable to generate payment instructions. It looks like only \ + the user can resolve this issue." ); self.mark_as_ended(logger) }